@qwen-code/qwen-code 0.1.2-nightly.20251031.ced79cf4 → 0.1.3-nightly.20251101.ff8a8ac6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/cli.js +678 -581
  2. package/package.json +2 -2
package/cli.js CHANGED
@@ -182628,7 +182628,7 @@ function createContentGeneratorConfig(config, authType, generationConfig) {
182628
182628
  };
182629
182629
  }
182630
182630
  async function createContentGenerator(config, gcConfig, sessionId2) {
182631
- const version2 = "0.1.2-nightly.20251031.ced79cf4";
182631
+ const version2 = "0.1.3-nightly.20251101.ff8a8ac6";
182632
182632
  const userAgent2 = `QwenCode/${version2} (${process.platform}; ${process.arch})`;
182633
182633
  const baseHeaders = {
182634
182634
  "User-Agent": userAgent2
@@ -184427,7 +184427,7 @@ var init_tool_names = __esm({
184427
184427
  WRITE_FILE: "write_file",
184428
184428
  READ_FILE: "read_file",
184429
184429
  READ_MANY_FILES: "read_many_files",
184430
- GREP: "search_file_content",
184430
+ GREP: "grep_search",
184431
184431
  GLOB: "glob",
184432
184432
  SHELL: "run_shell_command",
184433
184433
  TODO_WRITE: "todo_write",
@@ -198829,7 +198829,8 @@ var init_turn = __esm({
198829
198829
  CompressionStatus2[CompressionStatus2["COMPRESSED"] = 1] = "COMPRESSED";
198830
198830
  CompressionStatus2[CompressionStatus2["COMPRESSION_FAILED_INFLATED_TOKEN_COUNT"] = 2] = "COMPRESSION_FAILED_INFLATED_TOKEN_COUNT";
198831
198831
  CompressionStatus2[CompressionStatus2["COMPRESSION_FAILED_TOKEN_COUNT_ERROR"] = 3] = "COMPRESSION_FAILED_TOKEN_COUNT_ERROR";
198832
- CompressionStatus2[CompressionStatus2["NOOP"] = 4] = "NOOP";
198832
+ CompressionStatus2[CompressionStatus2["COMPRESSION_FAILED_EMPTY_SUMMARY"] = 4] = "COMPRESSION_FAILED_EMPTY_SUMMARY";
198833
+ CompressionStatus2[CompressionStatus2["NOOP"] = 5] = "NOOP";
198833
198834
  })(CompressionStatus || (CompressionStatus = {}));
198834
198835
  Turn = class {
198835
198836
  static {
@@ -198961,6 +198962,474 @@ ${[...this.pendingCitations].sort().join("\n")}`
198961
198962
  }
198962
198963
  });
198963
198964
 
198965
+ // packages/core/dist/src/config/constants.js
198966
+ var DEFAULT_MEMORY_FILE_FILTERING_OPTIONS, DEFAULT_FILE_FILTERING_OPTIONS;
198967
+ var init_constants3 = __esm({
198968
+ "packages/core/dist/src/config/constants.js"() {
198969
+ "use strict";
198970
+ init_esbuild_shims();
198971
+ DEFAULT_MEMORY_FILE_FILTERING_OPTIONS = {
198972
+ respectGitIgnore: false,
198973
+ respectQwenIgnore: true
198974
+ };
198975
+ DEFAULT_FILE_FILTERING_OPTIONS = {
198976
+ respectGitIgnore: true,
198977
+ respectQwenIgnore: true
198978
+ };
198979
+ }
198980
+ });
198981
+
198982
+ // packages/core/dist/src/utils/getFolderStructure.js
198983
+ import * as fs24 from "node:fs/promises";
198984
+ import * as path21 from "node:path";
198985
+ async function readFullStructure(rootPath, options2) {
198986
+ const rootName = path21.basename(rootPath);
198987
+ const rootNode = {
198988
+ name: rootName,
198989
+ path: rootPath,
198990
+ files: [],
198991
+ subFolders: [],
198992
+ totalChildren: 0,
198993
+ totalFiles: 0
198994
+ };
198995
+ const queue = [
198996
+ { folderInfo: rootNode, currentPath: rootPath }
198997
+ ];
198998
+ let currentItemCount = 0;
198999
+ const processedPaths = /* @__PURE__ */ new Set();
199000
+ while (queue.length > 0) {
199001
+ const { folderInfo, currentPath } = queue.shift();
199002
+ if (processedPaths.has(currentPath)) {
199003
+ continue;
199004
+ }
199005
+ processedPaths.add(currentPath);
199006
+ if (currentItemCount >= options2.maxItems) {
199007
+ continue;
199008
+ }
199009
+ let entries;
199010
+ try {
199011
+ const rawEntries = await fs24.readdir(currentPath, { withFileTypes: true });
199012
+ entries = rawEntries.sort((a, b) => a.name.localeCompare(b.name));
199013
+ } catch (error) {
199014
+ if (isNodeError(error) && (error.code === "EACCES" || error.code === "ENOENT")) {
199015
+ console.warn(`Warning: Could not read directory ${currentPath}: ${error.message}`);
199016
+ if (currentPath === rootPath && error.code === "ENOENT") {
199017
+ return null;
199018
+ }
199019
+ continue;
199020
+ }
199021
+ throw error;
199022
+ }
199023
+ const filesInCurrentDir = [];
199024
+ const subFoldersInCurrentDir = [];
199025
+ for (const entry of entries) {
199026
+ if (entry.isFile()) {
199027
+ if (currentItemCount >= options2.maxItems) {
199028
+ folderInfo.hasMoreFiles = true;
199029
+ break;
199030
+ }
199031
+ const fileName = entry.name;
199032
+ const filePath = path21.join(currentPath, fileName);
199033
+ if (options2.fileService) {
199034
+ const shouldIgnore = options2.fileFilteringOptions.respectGitIgnore && options2.fileService.shouldGitIgnoreFile(filePath) || options2.fileFilteringOptions.respectQwenIgnore && options2.fileService.shouldQwenIgnoreFile(filePath);
199035
+ if (shouldIgnore) {
199036
+ continue;
199037
+ }
199038
+ }
199039
+ if (!options2.fileIncludePattern || options2.fileIncludePattern.test(fileName)) {
199040
+ filesInCurrentDir.push(fileName);
199041
+ currentItemCount++;
199042
+ folderInfo.totalFiles++;
199043
+ folderInfo.totalChildren++;
199044
+ }
199045
+ }
199046
+ }
199047
+ folderInfo.files = filesInCurrentDir;
199048
+ for (const entry of entries) {
199049
+ if (entry.isDirectory()) {
199050
+ if (currentItemCount >= options2.maxItems) {
199051
+ folderInfo.hasMoreSubfolders = true;
199052
+ break;
199053
+ }
199054
+ const subFolderName = entry.name;
199055
+ const subFolderPath = path21.join(currentPath, subFolderName);
199056
+ let isIgnored = false;
199057
+ if (options2.fileService) {
199058
+ isIgnored = options2.fileFilteringOptions.respectGitIgnore && options2.fileService.shouldGitIgnoreFile(subFolderPath) || options2.fileFilteringOptions.respectQwenIgnore && options2.fileService.shouldQwenIgnoreFile(subFolderPath);
199059
+ }
199060
+ if (options2.ignoredFolders.has(subFolderName) || isIgnored) {
199061
+ const ignoredSubFolder = {
199062
+ name: subFolderName,
199063
+ path: subFolderPath,
199064
+ files: [],
199065
+ subFolders: [],
199066
+ totalChildren: 0,
199067
+ totalFiles: 0,
199068
+ isIgnored: true
199069
+ };
199070
+ subFoldersInCurrentDir.push(ignoredSubFolder);
199071
+ currentItemCount++;
199072
+ folderInfo.totalChildren++;
199073
+ continue;
199074
+ }
199075
+ const subFolderNode = {
199076
+ name: subFolderName,
199077
+ path: subFolderPath,
199078
+ files: [],
199079
+ subFolders: [],
199080
+ totalChildren: 0,
199081
+ totalFiles: 0
199082
+ };
199083
+ subFoldersInCurrentDir.push(subFolderNode);
199084
+ currentItemCount++;
199085
+ folderInfo.totalChildren++;
199086
+ queue.push({ folderInfo: subFolderNode, currentPath: subFolderPath });
199087
+ }
199088
+ }
199089
+ folderInfo.subFolders = subFoldersInCurrentDir;
199090
+ }
199091
+ return rootNode;
199092
+ }
199093
+ function formatStructure(node, currentIndent, isLastChildOfParent, isProcessingRootNode, builder) {
199094
+ const connector = isLastChildOfParent ? "\u2514\u2500\u2500\u2500" : "\u251C\u2500\u2500\u2500";
199095
+ if (!isProcessingRootNode || node.isIgnored) {
199096
+ builder.push(`${currentIndent}${connector}${node.name}${path21.sep}${node.isIgnored ? TRUNCATION_INDICATOR : ""}`);
199097
+ }
199098
+ const indentForChildren = isProcessingRootNode ? "" : currentIndent + (isLastChildOfParent ? " " : "\u2502 ");
199099
+ const fileCount = node.files.length;
199100
+ for (let i = 0; i < fileCount; i++) {
199101
+ const isLastFileAmongSiblings = i === fileCount - 1 && node.subFolders.length === 0 && !node.hasMoreSubfolders;
199102
+ const fileConnector = isLastFileAmongSiblings ? "\u2514\u2500\u2500\u2500" : "\u251C\u2500\u2500\u2500";
199103
+ builder.push(`${indentForChildren}${fileConnector}${node.files[i]}`);
199104
+ }
199105
+ if (node.hasMoreFiles) {
199106
+ const isLastIndicatorAmongSiblings = node.subFolders.length === 0 && !node.hasMoreSubfolders;
199107
+ const fileConnector = isLastIndicatorAmongSiblings ? "\u2514\u2500\u2500\u2500" : "\u251C\u2500\u2500\u2500";
199108
+ builder.push(`${indentForChildren}${fileConnector}${TRUNCATION_INDICATOR}`);
199109
+ }
199110
+ const subFolderCount = node.subFolders.length;
199111
+ for (let i = 0; i < subFolderCount; i++) {
199112
+ const isLastSubfolderAmongSiblings = i === subFolderCount - 1 && !node.hasMoreSubfolders;
199113
+ formatStructure(node.subFolders[i], indentForChildren, isLastSubfolderAmongSiblings, false, builder);
199114
+ }
199115
+ if (node.hasMoreSubfolders) {
199116
+ builder.push(`${indentForChildren}\u2514\u2500\u2500\u2500${TRUNCATION_INDICATOR}`);
199117
+ }
199118
+ }
199119
+ async function getFolderStructure(directory, options2) {
199120
+ const resolvedPath = path21.resolve(directory);
199121
+ const mergedOptions = {
199122
+ maxItems: options2?.maxItems ?? MAX_ITEMS,
199123
+ ignoredFolders: options2?.ignoredFolders ?? DEFAULT_IGNORED_FOLDERS,
199124
+ fileIncludePattern: options2?.fileIncludePattern,
199125
+ fileService: options2?.fileService,
199126
+ fileFilteringOptions: options2?.fileFilteringOptions ?? DEFAULT_FILE_FILTERING_OPTIONS
199127
+ };
199128
+ try {
199129
+ let isTruncated2 = function(node) {
199130
+ if (node.hasMoreFiles || node.hasMoreSubfolders || node.isIgnored) {
199131
+ return true;
199132
+ }
199133
+ for (const sub of node.subFolders) {
199134
+ if (isTruncated2(sub)) {
199135
+ return true;
199136
+ }
199137
+ }
199138
+ return false;
199139
+ };
199140
+ var isTruncated = isTruncated2;
199141
+ __name(isTruncated2, "isTruncated");
199142
+ const structureRoot = await readFullStructure(resolvedPath, mergedOptions);
199143
+ if (!structureRoot) {
199144
+ return `Error: Could not read directory "${resolvedPath}". Check path and permissions.`;
199145
+ }
199146
+ const structureLines = [];
199147
+ formatStructure(structureRoot, "", true, true, structureLines);
199148
+ let summary = `Showing up to ${mergedOptions.maxItems} items (files + folders).`;
199149
+ if (isTruncated2(structureRoot)) {
199150
+ summary += ` Folders or files indicated with ${TRUNCATION_INDICATOR} contain more items not shown, were ignored, or the display limit (${mergedOptions.maxItems} items) was reached.`;
199151
+ }
199152
+ return `${summary}
199153
+
199154
+ ${resolvedPath}${path21.sep}
199155
+ ${structureLines.join("\n")}`;
199156
+ } catch (error) {
199157
+ console.error(`Error getting folder structure for ${resolvedPath}:`, error);
199158
+ return `Error processing directory "${resolvedPath}": ${getErrorMessage(error)}`;
199159
+ }
199160
+ }
199161
+ var MAX_ITEMS, TRUNCATION_INDICATOR, DEFAULT_IGNORED_FOLDERS;
199162
+ var init_getFolderStructure = __esm({
199163
+ "packages/core/dist/src/utils/getFolderStructure.js"() {
199164
+ "use strict";
199165
+ init_esbuild_shims();
199166
+ init_errors();
199167
+ init_constants3();
199168
+ MAX_ITEMS = 20;
199169
+ TRUNCATION_INDICATOR = "...";
199170
+ DEFAULT_IGNORED_FOLDERS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist"]);
199171
+ __name(readFullStructure, "readFullStructure");
199172
+ __name(formatStructure, "formatStructure");
199173
+ __name(getFolderStructure, "getFolderStructure");
199174
+ }
199175
+ });
199176
+
199177
+ // packages/core/dist/src/utils/environmentContext.js
199178
+ async function getDirectoryContextString(config) {
199179
+ const workspaceContext = config.getWorkspaceContext();
199180
+ const workspaceDirectories = workspaceContext.getDirectories();
199181
+ const folderStructures = await Promise.all(workspaceDirectories.map((dir) => getFolderStructure(dir, {
199182
+ fileService: config.getFileService()
199183
+ })));
199184
+ const folderStructure = folderStructures.join("\n");
199185
+ let workingDirPreamble;
199186
+ if (workspaceDirectories.length === 1) {
199187
+ workingDirPreamble = `I'm currently working in the directory: ${workspaceDirectories[0]}`;
199188
+ } else {
199189
+ const dirList = workspaceDirectories.map((dir) => ` - ${dir}`).join("\n");
199190
+ workingDirPreamble = `I'm currently working in the following directories:
199191
+ ${dirList}`;
199192
+ }
199193
+ return `${workingDirPreamble}
199194
+ Here is the folder structure of the current working directories:
199195
+
199196
+ ${folderStructure}`;
199197
+ }
199198
+ async function getEnvironmentContext(config) {
199199
+ const today = (/* @__PURE__ */ new Date()).toLocaleDateString(void 0, {
199200
+ weekday: "long",
199201
+ year: "numeric",
199202
+ month: "long",
199203
+ day: "numeric"
199204
+ });
199205
+ const platform14 = process.platform;
199206
+ const directoryContext = await getDirectoryContextString(config);
199207
+ const context2 = `
199208
+ This is the Qwen Code. We are setting up the context for our chat.
199209
+ Today's date is ${today} (formatted according to the user's locale).
199210
+ My operating system is: ${platform14}
199211
+ ${directoryContext}
199212
+ `.trim();
199213
+ const initialParts = [{ text: context2 }];
199214
+ const toolRegistry = config.getToolRegistry();
199215
+ if (config.getFullContext()) {
199216
+ try {
199217
+ const readManyFilesTool = toolRegistry.getTool("read_many_files");
199218
+ if (readManyFilesTool) {
199219
+ const invocation = readManyFilesTool.build({
199220
+ paths: ["**/*"],
199221
+ // Read everything recursively
199222
+ useDefaultExcludes: true
199223
+ // Use default excludes
199224
+ });
199225
+ const result = await invocation.execute(AbortSignal.timeout(3e4));
199226
+ if (result.llmContent) {
199227
+ initialParts.push({
199228
+ text: `
199229
+ --- Full File Context ---
199230
+ ${result.llmContent}`
199231
+ });
199232
+ } else {
199233
+ console.warn("Full context requested, but read_many_files returned no content.");
199234
+ }
199235
+ } else {
199236
+ console.warn("Full context requested, but read_many_files tool not found.");
199237
+ }
199238
+ } catch (error) {
199239
+ console.error("Error reading full file context:", error);
199240
+ initialParts.push({
199241
+ text: "\n--- Error reading full file context ---"
199242
+ });
199243
+ }
199244
+ }
199245
+ return initialParts;
199246
+ }
199247
+ async function getInitialChatHistory(config, extraHistory) {
199248
+ const envParts = await getEnvironmentContext(config);
199249
+ const envContextString = envParts.map((part) => part.text || "").join("\n\n");
199250
+ return [
199251
+ {
199252
+ role: "user",
199253
+ parts: [{ text: envContextString }]
199254
+ },
199255
+ {
199256
+ role: "model",
199257
+ parts: [{ text: "Got it. Thanks for the context!" }]
199258
+ },
199259
+ ...extraHistory ?? []
199260
+ ];
199261
+ }
199262
+ var init_environmentContext = __esm({
199263
+ "packages/core/dist/src/utils/environmentContext.js"() {
199264
+ "use strict";
199265
+ init_esbuild_shims();
199266
+ init_getFolderStructure();
199267
+ __name(getDirectoryContextString, "getDirectoryContextString");
199268
+ __name(getEnvironmentContext, "getEnvironmentContext");
199269
+ __name(getInitialChatHistory, "getInitialChatHistory");
199270
+ }
199271
+ });
199272
+
199273
+ // packages/core/dist/src/services/chatCompressionService.js
199274
+ function findCompressSplitPoint(contents, fraction) {
199275
+ if (fraction <= 0 || fraction >= 1) {
199276
+ throw new Error("Fraction must be between 0 and 1");
199277
+ }
199278
+ const charCounts = contents.map((content) => JSON.stringify(content).length);
199279
+ const totalCharCount = charCounts.reduce((a, b) => a + b, 0);
199280
+ const targetCharCount = totalCharCount * fraction;
199281
+ let lastSplitPoint = 0;
199282
+ let cumulativeCharCount = 0;
199283
+ for (let i = 0; i < contents.length; i++) {
199284
+ const content = contents[i];
199285
+ if (content.role === "user" && !content.parts?.some((part) => !!part.functionResponse)) {
199286
+ if (cumulativeCharCount >= targetCharCount) {
199287
+ return i;
199288
+ }
199289
+ lastSplitPoint = i;
199290
+ }
199291
+ cumulativeCharCount += charCounts[i];
199292
+ }
199293
+ const lastContent = contents[contents.length - 1];
199294
+ if (lastContent?.role === "model" && !lastContent?.parts?.some((part) => part.functionCall)) {
199295
+ return contents.length;
199296
+ }
199297
+ return lastSplitPoint;
199298
+ }
199299
+ var COMPRESSION_TOKEN_THRESHOLD, COMPRESSION_PRESERVE_THRESHOLD, ChatCompressionService;
199300
+ var init_chatCompressionService = __esm({
199301
+ "packages/core/dist/src/services/chatCompressionService.js"() {
199302
+ "use strict";
199303
+ init_esbuild_shims();
199304
+ init_turn();
199305
+ init_uiTelemetry();
199306
+ init_tokenLimits();
199307
+ init_prompts();
199308
+ init_partUtils();
199309
+ init_loggers();
199310
+ init_types3();
199311
+ init_environmentContext();
199312
+ COMPRESSION_TOKEN_THRESHOLD = 0.7;
199313
+ COMPRESSION_PRESERVE_THRESHOLD = 0.3;
199314
+ __name(findCompressSplitPoint, "findCompressSplitPoint");
199315
+ ChatCompressionService = class {
199316
+ static {
199317
+ __name(this, "ChatCompressionService");
199318
+ }
199319
+ async compress(chat, promptId, force, model, config, hasFailedCompressionAttempt) {
199320
+ const curatedHistory = chat.getHistory(true);
199321
+ if (curatedHistory.length === 0 || hasFailedCompressionAttempt && !force) {
199322
+ return {
199323
+ newHistory: null,
199324
+ info: {
199325
+ originalTokenCount: 0,
199326
+ newTokenCount: 0,
199327
+ compressionStatus: CompressionStatus.NOOP
199328
+ }
199329
+ };
199330
+ }
199331
+ const originalTokenCount = uiTelemetryService.getLastPromptTokenCount();
199332
+ const contextPercentageThreshold = config.getChatCompression()?.contextPercentageThreshold;
199333
+ if (!force) {
199334
+ const threshold = contextPercentageThreshold ?? COMPRESSION_TOKEN_THRESHOLD;
199335
+ if (originalTokenCount < threshold * tokenLimit(model)) {
199336
+ return {
199337
+ newHistory: null,
199338
+ info: {
199339
+ originalTokenCount,
199340
+ newTokenCount: originalTokenCount,
199341
+ compressionStatus: CompressionStatus.NOOP
199342
+ }
199343
+ };
199344
+ }
199345
+ }
199346
+ const splitPoint = findCompressSplitPoint(curatedHistory, 1 - COMPRESSION_PRESERVE_THRESHOLD);
199347
+ const historyToCompress = curatedHistory.slice(0, splitPoint);
199348
+ const historyToKeep = curatedHistory.slice(splitPoint);
199349
+ if (historyToCompress.length === 0) {
199350
+ return {
199351
+ newHistory: null,
199352
+ info: {
199353
+ originalTokenCount,
199354
+ newTokenCount: originalTokenCount,
199355
+ compressionStatus: CompressionStatus.NOOP
199356
+ }
199357
+ };
199358
+ }
199359
+ const summaryResponse = await config.getContentGenerator().generateContent({
199360
+ model,
199361
+ contents: [
199362
+ ...historyToCompress,
199363
+ {
199364
+ role: "user",
199365
+ parts: [
199366
+ {
199367
+ text: "First, reason in your scratchpad. Then, generate the <state_snapshot>."
199368
+ }
199369
+ ]
199370
+ }
199371
+ ],
199372
+ config: {
199373
+ systemInstruction: getCompressionPrompt()
199374
+ }
199375
+ }, promptId);
199376
+ const summary = getResponseText(summaryResponse) ?? "";
199377
+ const isSummaryEmpty = !summary || summary.trim().length === 0;
199378
+ let newTokenCount = originalTokenCount;
199379
+ let extraHistory = [];
199380
+ if (!isSummaryEmpty) {
199381
+ extraHistory = [
199382
+ {
199383
+ role: "user",
199384
+ parts: [{ text: summary }]
199385
+ },
199386
+ {
199387
+ role: "model",
199388
+ parts: [{ text: "Got it. Thanks for the additional context!" }]
199389
+ },
199390
+ ...historyToKeep
199391
+ ];
199392
+ const fullNewHistory = await getInitialChatHistory(config, extraHistory);
199393
+ newTokenCount = Math.floor(fullNewHistory.reduce((total, content) => total + JSON.stringify(content).length, 0) / 4);
199394
+ }
199395
+ logChatCompression(config, makeChatCompressionEvent({
199396
+ tokens_before: originalTokenCount,
199397
+ tokens_after: newTokenCount
199398
+ }));
199399
+ if (isSummaryEmpty) {
199400
+ return {
199401
+ newHistory: null,
199402
+ info: {
199403
+ originalTokenCount,
199404
+ newTokenCount: originalTokenCount,
199405
+ compressionStatus: CompressionStatus.COMPRESSION_FAILED_EMPTY_SUMMARY
199406
+ }
199407
+ };
199408
+ } else if (newTokenCount > originalTokenCount) {
199409
+ return {
199410
+ newHistory: null,
199411
+ info: {
199412
+ originalTokenCount,
199413
+ newTokenCount,
199414
+ compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT
199415
+ }
199416
+ };
199417
+ } else {
199418
+ uiTelemetryService.setLastPromptTokenCount(newTokenCount);
199419
+ return {
199420
+ newHistory: extraHistory,
199421
+ info: {
199422
+ originalTokenCount,
199423
+ newTokenCount,
199424
+ compressionStatus: CompressionStatus.COMPRESSED
199425
+ }
199426
+ };
199427
+ }
199428
+ }
199429
+ };
199430
+ }
199431
+ });
199432
+
198964
199433
  // packages/core/dist/src/services/loopDetectionService.js
198965
199434
  import { createHash as createHash3 } from "node:crypto";
198966
199435
  var TOOL_CALL_LOOP_THRESHOLD, CONTENT_LOOP_THRESHOLD, CONTENT_CHUNK_SIZE, MAX_HISTORY_LENGTH, LLM_LOOP_CHECK_HISTORY_COUNT, LLM_CHECK_AFTER_TURNS, DEFAULT_LLM_CHECK_INTERVAL, MIN_LLM_CHECK_INTERVAL, MAX_LLM_CHECK_INTERVAL, LOOP_DETECTION_SYSTEM_PROMPT, LoopDetectionService;
@@ -199322,298 +199791,6 @@ var init_types6 = __esm({
199322
199791
  }
199323
199792
  });
199324
199793
 
199325
- // packages/core/dist/src/config/constants.js
199326
- var DEFAULT_MEMORY_FILE_FILTERING_OPTIONS, DEFAULT_FILE_FILTERING_OPTIONS;
199327
- var init_constants3 = __esm({
199328
- "packages/core/dist/src/config/constants.js"() {
199329
- "use strict";
199330
- init_esbuild_shims();
199331
- DEFAULT_MEMORY_FILE_FILTERING_OPTIONS = {
199332
- respectGitIgnore: false,
199333
- respectQwenIgnore: true
199334
- };
199335
- DEFAULT_FILE_FILTERING_OPTIONS = {
199336
- respectGitIgnore: true,
199337
- respectQwenIgnore: true
199338
- };
199339
- }
199340
- });
199341
-
199342
- // packages/core/dist/src/utils/getFolderStructure.js
199343
- import * as fs24 from "node:fs/promises";
199344
- import * as path21 from "node:path";
199345
- async function readFullStructure(rootPath, options2) {
199346
- const rootName = path21.basename(rootPath);
199347
- const rootNode = {
199348
- name: rootName,
199349
- path: rootPath,
199350
- files: [],
199351
- subFolders: [],
199352
- totalChildren: 0,
199353
- totalFiles: 0
199354
- };
199355
- const queue = [
199356
- { folderInfo: rootNode, currentPath: rootPath }
199357
- ];
199358
- let currentItemCount = 0;
199359
- const processedPaths = /* @__PURE__ */ new Set();
199360
- while (queue.length > 0) {
199361
- const { folderInfo, currentPath } = queue.shift();
199362
- if (processedPaths.has(currentPath)) {
199363
- continue;
199364
- }
199365
- processedPaths.add(currentPath);
199366
- if (currentItemCount >= options2.maxItems) {
199367
- continue;
199368
- }
199369
- let entries;
199370
- try {
199371
- const rawEntries = await fs24.readdir(currentPath, { withFileTypes: true });
199372
- entries = rawEntries.sort((a, b) => a.name.localeCompare(b.name));
199373
- } catch (error) {
199374
- if (isNodeError(error) && (error.code === "EACCES" || error.code === "ENOENT")) {
199375
- console.warn(`Warning: Could not read directory ${currentPath}: ${error.message}`);
199376
- if (currentPath === rootPath && error.code === "ENOENT") {
199377
- return null;
199378
- }
199379
- continue;
199380
- }
199381
- throw error;
199382
- }
199383
- const filesInCurrentDir = [];
199384
- const subFoldersInCurrentDir = [];
199385
- for (const entry of entries) {
199386
- if (entry.isFile()) {
199387
- if (currentItemCount >= options2.maxItems) {
199388
- folderInfo.hasMoreFiles = true;
199389
- break;
199390
- }
199391
- const fileName = entry.name;
199392
- const filePath = path21.join(currentPath, fileName);
199393
- if (options2.fileService) {
199394
- const shouldIgnore = options2.fileFilteringOptions.respectGitIgnore && options2.fileService.shouldGitIgnoreFile(filePath) || options2.fileFilteringOptions.respectQwenIgnore && options2.fileService.shouldQwenIgnoreFile(filePath);
199395
- if (shouldIgnore) {
199396
- continue;
199397
- }
199398
- }
199399
- if (!options2.fileIncludePattern || options2.fileIncludePattern.test(fileName)) {
199400
- filesInCurrentDir.push(fileName);
199401
- currentItemCount++;
199402
- folderInfo.totalFiles++;
199403
- folderInfo.totalChildren++;
199404
- }
199405
- }
199406
- }
199407
- folderInfo.files = filesInCurrentDir;
199408
- for (const entry of entries) {
199409
- if (entry.isDirectory()) {
199410
- if (currentItemCount >= options2.maxItems) {
199411
- folderInfo.hasMoreSubfolders = true;
199412
- break;
199413
- }
199414
- const subFolderName = entry.name;
199415
- const subFolderPath = path21.join(currentPath, subFolderName);
199416
- let isIgnored = false;
199417
- if (options2.fileService) {
199418
- isIgnored = options2.fileFilteringOptions.respectGitIgnore && options2.fileService.shouldGitIgnoreFile(subFolderPath) || options2.fileFilteringOptions.respectQwenIgnore && options2.fileService.shouldQwenIgnoreFile(subFolderPath);
199419
- }
199420
- if (options2.ignoredFolders.has(subFolderName) || isIgnored) {
199421
- const ignoredSubFolder = {
199422
- name: subFolderName,
199423
- path: subFolderPath,
199424
- files: [],
199425
- subFolders: [],
199426
- totalChildren: 0,
199427
- totalFiles: 0,
199428
- isIgnored: true
199429
- };
199430
- subFoldersInCurrentDir.push(ignoredSubFolder);
199431
- currentItemCount++;
199432
- folderInfo.totalChildren++;
199433
- continue;
199434
- }
199435
- const subFolderNode = {
199436
- name: subFolderName,
199437
- path: subFolderPath,
199438
- files: [],
199439
- subFolders: [],
199440
- totalChildren: 0,
199441
- totalFiles: 0
199442
- };
199443
- subFoldersInCurrentDir.push(subFolderNode);
199444
- currentItemCount++;
199445
- folderInfo.totalChildren++;
199446
- queue.push({ folderInfo: subFolderNode, currentPath: subFolderPath });
199447
- }
199448
- }
199449
- folderInfo.subFolders = subFoldersInCurrentDir;
199450
- }
199451
- return rootNode;
199452
- }
199453
- function formatStructure(node, currentIndent, isLastChildOfParent, isProcessingRootNode, builder) {
199454
- const connector = isLastChildOfParent ? "\u2514\u2500\u2500\u2500" : "\u251C\u2500\u2500\u2500";
199455
- if (!isProcessingRootNode || node.isIgnored) {
199456
- builder.push(`${currentIndent}${connector}${node.name}${path21.sep}${node.isIgnored ? TRUNCATION_INDICATOR : ""}`);
199457
- }
199458
- const indentForChildren = isProcessingRootNode ? "" : currentIndent + (isLastChildOfParent ? " " : "\u2502 ");
199459
- const fileCount = node.files.length;
199460
- for (let i = 0; i < fileCount; i++) {
199461
- const isLastFileAmongSiblings = i === fileCount - 1 && node.subFolders.length === 0 && !node.hasMoreSubfolders;
199462
- const fileConnector = isLastFileAmongSiblings ? "\u2514\u2500\u2500\u2500" : "\u251C\u2500\u2500\u2500";
199463
- builder.push(`${indentForChildren}${fileConnector}${node.files[i]}`);
199464
- }
199465
- if (node.hasMoreFiles) {
199466
- const isLastIndicatorAmongSiblings = node.subFolders.length === 0 && !node.hasMoreSubfolders;
199467
- const fileConnector = isLastIndicatorAmongSiblings ? "\u2514\u2500\u2500\u2500" : "\u251C\u2500\u2500\u2500";
199468
- builder.push(`${indentForChildren}${fileConnector}${TRUNCATION_INDICATOR}`);
199469
- }
199470
- const subFolderCount = node.subFolders.length;
199471
- for (let i = 0; i < subFolderCount; i++) {
199472
- const isLastSubfolderAmongSiblings = i === subFolderCount - 1 && !node.hasMoreSubfolders;
199473
- formatStructure(node.subFolders[i], indentForChildren, isLastSubfolderAmongSiblings, false, builder);
199474
- }
199475
- if (node.hasMoreSubfolders) {
199476
- builder.push(`${indentForChildren}\u2514\u2500\u2500\u2500${TRUNCATION_INDICATOR}`);
199477
- }
199478
- }
199479
- async function getFolderStructure(directory, options2) {
199480
- const resolvedPath = path21.resolve(directory);
199481
- const mergedOptions = {
199482
- maxItems: options2?.maxItems ?? MAX_ITEMS,
199483
- ignoredFolders: options2?.ignoredFolders ?? DEFAULT_IGNORED_FOLDERS,
199484
- fileIncludePattern: options2?.fileIncludePattern,
199485
- fileService: options2?.fileService,
199486
- fileFilteringOptions: options2?.fileFilteringOptions ?? DEFAULT_FILE_FILTERING_OPTIONS
199487
- };
199488
- try {
199489
- let isTruncated2 = function(node) {
199490
- if (node.hasMoreFiles || node.hasMoreSubfolders || node.isIgnored) {
199491
- return true;
199492
- }
199493
- for (const sub of node.subFolders) {
199494
- if (isTruncated2(sub)) {
199495
- return true;
199496
- }
199497
- }
199498
- return false;
199499
- };
199500
- var isTruncated = isTruncated2;
199501
- __name(isTruncated2, "isTruncated");
199502
- const structureRoot = await readFullStructure(resolvedPath, mergedOptions);
199503
- if (!structureRoot) {
199504
- return `Error: Could not read directory "${resolvedPath}". Check path and permissions.`;
199505
- }
199506
- const structureLines = [];
199507
- formatStructure(structureRoot, "", true, true, structureLines);
199508
- let summary = `Showing up to ${mergedOptions.maxItems} items (files + folders).`;
199509
- if (isTruncated2(structureRoot)) {
199510
- summary += ` Folders or files indicated with ${TRUNCATION_INDICATOR} contain more items not shown, were ignored, or the display limit (${mergedOptions.maxItems} items) was reached.`;
199511
- }
199512
- return `${summary}
199513
-
199514
- ${resolvedPath}${path21.sep}
199515
- ${structureLines.join("\n")}`;
199516
- } catch (error) {
199517
- console.error(`Error getting folder structure for ${resolvedPath}:`, error);
199518
- return `Error processing directory "${resolvedPath}": ${getErrorMessage(error)}`;
199519
- }
199520
- }
199521
- var MAX_ITEMS, TRUNCATION_INDICATOR, DEFAULT_IGNORED_FOLDERS;
199522
- var init_getFolderStructure = __esm({
199523
- "packages/core/dist/src/utils/getFolderStructure.js"() {
199524
- "use strict";
199525
- init_esbuild_shims();
199526
- init_errors();
199527
- init_constants3();
199528
- MAX_ITEMS = 20;
199529
- TRUNCATION_INDICATOR = "...";
199530
- DEFAULT_IGNORED_FOLDERS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist"]);
199531
- __name(readFullStructure, "readFullStructure");
199532
- __name(formatStructure, "formatStructure");
199533
- __name(getFolderStructure, "getFolderStructure");
199534
- }
199535
- });
199536
-
199537
- // packages/core/dist/src/utils/environmentContext.js
199538
- async function getDirectoryContextString(config) {
199539
- const workspaceContext = config.getWorkspaceContext();
199540
- const workspaceDirectories = workspaceContext.getDirectories();
199541
- const folderStructures = await Promise.all(workspaceDirectories.map((dir) => getFolderStructure(dir, {
199542
- fileService: config.getFileService()
199543
- })));
199544
- const folderStructure = folderStructures.join("\n");
199545
- let workingDirPreamble;
199546
- if (workspaceDirectories.length === 1) {
199547
- workingDirPreamble = `I'm currently working in the directory: ${workspaceDirectories[0]}`;
199548
- } else {
199549
- const dirList = workspaceDirectories.map((dir) => ` - ${dir}`).join("\n");
199550
- workingDirPreamble = `I'm currently working in the following directories:
199551
- ${dirList}`;
199552
- }
199553
- return `${workingDirPreamble}
199554
- Here is the folder structure of the current working directories:
199555
-
199556
- ${folderStructure}`;
199557
- }
199558
- async function getEnvironmentContext(config) {
199559
- const today = (/* @__PURE__ */ new Date()).toLocaleDateString(void 0, {
199560
- weekday: "long",
199561
- year: "numeric",
199562
- month: "long",
199563
- day: "numeric"
199564
- });
199565
- const platform14 = process.platform;
199566
- const directoryContext = await getDirectoryContextString(config);
199567
- const context2 = `
199568
- This is the Qwen Code. We are setting up the context for our chat.
199569
- Today's date is ${today} (formatted according to the user's locale).
199570
- My operating system is: ${platform14}
199571
- ${directoryContext}
199572
- `.trim();
199573
- const initialParts = [{ text: context2 }];
199574
- const toolRegistry = config.getToolRegistry();
199575
- if (config.getFullContext()) {
199576
- try {
199577
- const readManyFilesTool = toolRegistry.getTool("read_many_files");
199578
- if (readManyFilesTool) {
199579
- const invocation = readManyFilesTool.build({
199580
- paths: ["**/*"],
199581
- // Read everything recursively
199582
- useDefaultExcludes: true
199583
- // Use default excludes
199584
- });
199585
- const result = await invocation.execute(AbortSignal.timeout(3e4));
199586
- if (result.llmContent) {
199587
- initialParts.push({
199588
- text: `
199589
- --- Full File Context ---
199590
- ${result.llmContent}`
199591
- });
199592
- } else {
199593
- console.warn("Full context requested, but read_many_files returned no content.");
199594
- }
199595
- } else {
199596
- console.warn("Full context requested, but read_many_files tool not found.");
199597
- }
199598
- } catch (error) {
199599
- console.error("Error reading full file context:", error);
199600
- initialParts.push({
199601
- text: "\n--- Error reading full file context ---"
199602
- });
199603
- }
199604
- }
199605
- return initialParts;
199606
- }
199607
- var init_environmentContext = __esm({
199608
- "packages/core/dist/src/utils/environmentContext.js"() {
199609
- "use strict";
199610
- init_esbuild_shims();
199611
- init_getFolderStructure();
199612
- __name(getDirectoryContextString, "getDirectoryContextString");
199613
- __name(getEnvironmentContext, "getEnvironmentContext");
199614
- }
199615
- });
199616
-
199617
199794
  // packages/core/dist/src/subagents/subagent-events.js
199618
199795
  import { EventEmitter as EventEmitter5 } from "events";
199619
199796
  var SubAgentEventType, SubAgentEventEmitter;
@@ -200175,11 +200352,7 @@ var init_subagent = __esm({
200175
200352
  if (this.promptConfig.systemPrompt && this.promptConfig.initialMessages) {
200176
200353
  throw new Error("PromptConfig cannot have both `systemPrompt` and `initialMessages` defined.");
200177
200354
  }
200178
- const envParts = await getEnvironmentContext(this.runtimeContext);
200179
- const envHistory = [
200180
- { role: "user", parts: envParts },
200181
- { role: "model", parts: [{ text: "Got it. Thanks for the context!" }] }
200182
- ];
200355
+ const envHistory = await getInitialChatHistory(this.runtimeContext);
200183
200356
  const start_history = [
200184
200357
  ...envHistory,
200185
200358
  ...this.promptConfig.initialMessages ?? []
@@ -205187,32 +205360,7 @@ var init_types8 = __esm({
205187
205360
  function isThinkingSupported(model) {
205188
205361
  return model.startsWith("gemini-2.5") || model === DEFAULT_GEMINI_MODEL_AUTO;
205189
205362
  }
205190
- function findCompressSplitPoint(contents, fraction) {
205191
- if (fraction <= 0 || fraction >= 1) {
205192
- throw new Error("Fraction must be between 0 and 1");
205193
- }
205194
- const charCounts = contents.map((content) => JSON.stringify(content).length);
205195
- const totalCharCount = charCounts.reduce((a, b) => a + b, 0);
205196
- const targetCharCount = totalCharCount * fraction;
205197
- let lastSplitPoint = 0;
205198
- let cumulativeCharCount = 0;
205199
- for (let i = 0; i < contents.length; i++) {
205200
- const content = contents[i];
205201
- if (content.role === "user" && !content.parts?.some((part) => !!part.functionResponse)) {
205202
- if (cumulativeCharCount >= targetCharCount) {
205203
- return i;
205204
- }
205205
- lastSplitPoint = i;
205206
- }
205207
- cumulativeCharCount += charCounts[i];
205208
- }
205209
- const lastContent = contents[contents.length - 1];
205210
- if (lastContent?.role === "model" && !lastContent?.parts?.some((part) => part.functionCall)) {
205211
- return contents.length;
205212
- }
205213
- return lastSplitPoint;
205214
- }
205215
- var MAX_TURNS, COMPRESSION_TOKEN_THRESHOLD, COMPRESSION_PRESERVE_THRESHOLD, GeminiClient;
205363
+ var MAX_TURNS, GeminiClient;
205216
205364
  var init_client2 = __esm({
205217
205365
  "packages/core/dist/src/core/client.js"() {
205218
205366
  "use strict";
@@ -205221,9 +205369,9 @@ var init_client2 = __esm({
205221
205369
  init_models();
205222
205370
  init_geminiChat();
205223
205371
  init_prompts();
205224
- init_tokenLimits();
205225
205372
  init_turn();
205226
205373
  init_chatRecordingService();
205374
+ init_chatCompressionService();
205227
205375
  init_loopDetectionService();
205228
205376
  init_task();
205229
205377
  init_telemetry();
@@ -205237,10 +205385,7 @@ var init_client2 = __esm({
205237
205385
  init_types8();
205238
205386
  init_handler();
205239
205387
  __name(isThinkingSupported, "isThinkingSupported");
205240
- __name(findCompressSplitPoint, "findCompressSplitPoint");
205241
205388
  MAX_TURNS = 100;
205242
- COMPRESSION_TOKEN_THRESHOLD = 0.7;
205243
- COMPRESSION_PRESERVE_THRESHOLD = 0.3;
205244
205389
  GeminiClient = class {
205245
205390
  static {
205246
205391
  __name(this, "GeminiClient");
@@ -205324,21 +205469,10 @@ var init_client2 = __esm({
205324
205469
  async startChat(extraHistory) {
205325
205470
  this.forceFullIdeContext = true;
205326
205471
  this.hasFailedCompressionAttempt = false;
205327
- const envParts = await getEnvironmentContext(this.config);
205328
205472
  const toolRegistry = this.config.getToolRegistry();
205329
205473
  const toolDeclarations = toolRegistry.getFunctionDeclarations();
205330
205474
  const tools = [{ functionDeclarations: toolDeclarations }];
205331
- const history = [
205332
- {
205333
- role: "user",
205334
- parts: envParts
205335
- },
205336
- {
205337
- role: "model",
205338
- parts: [{ text: "Got it. Thanks for the context!" }]
205339
- },
205340
- ...extraHistory ?? []
205341
- ];
205475
+ const history = await getInitialChatHistory(this.config, extraHistory);
205342
205476
  try {
205343
205477
  const userMemory = this.config.getUserMemory();
205344
205478
  const model = this.config.getModel();
@@ -205509,12 +205643,13 @@ var init_client2 = __esm({
205509
205643
  const currentHistory = this.getChat().getHistory(true);
205510
205644
  const userMemory = this.config.getUserMemory();
205511
205645
  const systemPrompt = getCoreSystemPrompt(userMemory, this.config.getModel());
205512
- const environment = await getEnvironmentContext(this.config);
205646
+ const initialHistory = await getInitialChatHistory(this.config);
205513
205647
  const mockRequestContent = [
205514
205648
  {
205515
205649
  role: "system",
205516
- parts: [{ text: systemPrompt }, ...environment]
205650
+ parts: [{ text: systemPrompt }]
205517
205651
  },
205652
+ ...initialHistory,
205518
205653
  ...currentHistory
205519
205654
  ];
205520
205655
  const { totalTokens: totalRequestTokens } = await this.config.getContentGenerator().countTokens({
@@ -205641,85 +205776,19 @@ var init_client2 = __esm({
205641
205776
  }
205642
205777
  }
205643
205778
  async tryCompressChat(prompt_id, force = false) {
205644
- const model = this.config.getModel();
205645
- const curatedHistory = this.getChat().getHistory(true);
205646
- if (curatedHistory.length === 0 || this.hasFailedCompressionAttempt && !force) {
205647
- return {
205648
- originalTokenCount: 0,
205649
- newTokenCount: 0,
205650
- compressionStatus: CompressionStatus.NOOP
205651
- };
205652
- }
205653
- const originalTokenCount = uiTelemetryService.getLastPromptTokenCount();
205654
- const contextPercentageThreshold = this.config.getChatCompression()?.contextPercentageThreshold;
205655
- if (!force) {
205656
- const threshold = contextPercentageThreshold ?? COMPRESSION_TOKEN_THRESHOLD;
205657
- if (originalTokenCount < threshold * tokenLimit(model)) {
205658
- return {
205659
- originalTokenCount,
205660
- newTokenCount: originalTokenCount,
205661
- compressionStatus: CompressionStatus.NOOP
205662
- };
205779
+ const compressionService = new ChatCompressionService();
205780
+ const { newHistory, info } = await compressionService.compress(this.getChat(), prompt_id, force, this.config.getModel(), this.config, this.hasFailedCompressionAttempt);
205781
+ if (info.compressionStatus === CompressionStatus.COMPRESSED) {
205782
+ if (newHistory) {
205783
+ this.chat = await this.startChat(newHistory);
205784
+ this.forceFullIdeContext = true;
205663
205785
  }
205664
- }
205665
- const splitPoint = findCompressSplitPoint(curatedHistory, 1 - COMPRESSION_PRESERVE_THRESHOLD);
205666
- const historyToCompress = curatedHistory.slice(0, splitPoint);
205667
- const historyToKeep = curatedHistory.slice(splitPoint);
205668
- const summaryResponse = await this.config.getContentGenerator().generateContent({
205669
- model,
205670
- contents: [
205671
- ...historyToCompress,
205672
- {
205673
- role: "user",
205674
- parts: [
205675
- {
205676
- text: "First, reason in your scratchpad. Then, generate the <state_snapshot>."
205677
- }
205678
- ]
205679
- }
205680
- ],
205681
- config: {
205682
- systemInstruction: { text: getCompressionPrompt() }
205786
+ } else if (info.compressionStatus === CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT || info.compressionStatus === CompressionStatus.COMPRESSION_FAILED_EMPTY_SUMMARY) {
205787
+ if (!force) {
205788
+ this.hasFailedCompressionAttempt = true;
205683
205789
  }
205684
- }, prompt_id);
205685
- const summary = getResponseText(summaryResponse) ?? "";
205686
- const chat = await this.startChat([
205687
- {
205688
- role: "user",
205689
- parts: [{ text: summary }]
205690
- },
205691
- {
205692
- role: "model",
205693
- parts: [{ text: "Got it. Thanks for the additional context!" }]
205694
- },
205695
- ...historyToKeep
205696
- ]);
205697
- this.forceFullIdeContext = true;
205698
- const newTokenCount = Math.floor(chat.getHistory().reduce((total, content) => total + JSON.stringify(content).length, 0) / 4);
205699
- logChatCompression(this.config, makeChatCompressionEvent({
205700
- tokens_before: originalTokenCount,
205701
- tokens_after: newTokenCount
205702
- }));
205703
- if (newTokenCount > originalTokenCount) {
205704
- this.hasFailedCompressionAttempt = !force && true;
205705
- return {
205706
- originalTokenCount,
205707
- newTokenCount,
205708
- compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT
205709
- };
205710
- } else {
205711
- this.chat = chat;
205712
- uiTelemetryService.setLastPromptTokenCount(newTokenCount);
205713
205790
  }
205714
- logChatCompression(this.config, makeChatCompressionEvent({
205715
- tokens_before: originalTokenCount,
205716
- tokens_after: newTokenCount
205717
- }));
205718
- return {
205719
- originalTokenCount,
205720
- newTokenCount,
205721
- compressionStatus: CompressionStatus.COMPRESSED
205722
- };
205791
+ return info;
205723
205792
  }
205724
205793
  };
205725
205794
  }
@@ -231807,10 +231876,20 @@ function getRipgrepPath() {
231807
231876
  const vendorPath = isBundled ? path39.join(__dirname3, "vendor", "ripgrep", `${arch3}-${platform14}`, binaryName) : path39.join(__dirname3, "..", "..", "..", "vendor", "ripgrep", `${arch3}-${platform14}`, binaryName);
231808
231877
  return vendorPath;
231809
231878
  }
231810
- async function canUseRipgrep() {
231879
+ async function canUseRipgrep(useBuiltin = true) {
231811
231880
  try {
231812
- const rgPath = getRipgrepPath();
231813
- return await fileExists(rgPath);
231881
+ if (useBuiltin) {
231882
+ const rgPath = getRipgrepPath();
231883
+ if (await fileExists(rgPath)) {
231884
+ return true;
231885
+ }
231886
+ }
231887
+ const { spawn: spawn13 } = await import("node:child_process");
231888
+ return await new Promise((resolve23) => {
231889
+ const proc2 = spawn13("rg", ["--version"]);
231890
+ proc2.on("error", () => resolve23(false));
231891
+ proc2.on("exit", (code2) => resolve23(code2 === 0));
231892
+ });
231814
231893
  } catch (_error) {
231815
231894
  return false;
231816
231895
  }
@@ -231843,17 +231922,19 @@ import fs36 from "node:fs";
231843
231922
  import path40 from "node:path";
231844
231923
  import { EOL as EOL3 } from "node:os";
231845
231924
  import { spawn as spawn6 } from "node:child_process";
231846
- var DEFAULT_TOTAL_MAX_MATCHES, GrepToolInvocation2, RipGrepTool;
231925
+ var MAX_LLM_CONTENT_LENGTH, GrepToolInvocation2, RipGrepTool;
231847
231926
  var init_ripGrep = __esm({
231848
231927
  "packages/core/dist/src/tools/ripGrep.js"() {
231849
231928
  "use strict";
231850
231929
  init_esbuild_shims();
231851
231930
  init_tools();
231852
- init_schemaValidator();
231931
+ init_tool_names();
231853
231932
  init_paths();
231854
231933
  init_errors();
231855
231934
  init_ripgrepUtils();
231856
- DEFAULT_TOTAL_MAX_MATCHES = 2e4;
231935
+ init_schemaValidator();
231936
+ init_constants3();
231937
+ MAX_LLM_CONTENT_LENGTH = 2e4;
231857
231938
  GrepToolInvocation2 = class extends BaseToolInvocation {
231858
231939
  static {
231859
231940
  __name(this, "GrepToolInvocation");
@@ -231866,19 +231947,20 @@ var init_ripGrep = __esm({
231866
231947
  /**
231867
231948
  * Checks if a path is within the root directory and resolves it.
231868
231949
  * @param relativePath Path relative to the root directory (or undefined for root).
231869
- * @returns The absolute path if valid and exists, or null if no path specified (to search all directories).
231950
+ * @returns The absolute path to search within.
231870
231951
  * @throws {Error} If path is outside root, doesn't exist, or isn't a directory.
231871
231952
  */
231872
231953
  resolveAndValidatePath(relativePath) {
231873
- if (!relativePath) {
231874
- return null;
231875
- }
231876
- const targetPath = path40.resolve(this.config.getTargetDir(), relativePath);
231954
+ const targetDir = this.config.getTargetDir();
231955
+ const targetPath = relativePath ? path40.resolve(targetDir, relativePath) : targetDir;
231877
231956
  const workspaceContext = this.config.getWorkspaceContext();
231878
231957
  if (!workspaceContext.isPathWithinWorkspace(targetPath)) {
231879
231958
  const directories = workspaceContext.getDirectories();
231880
231959
  throw new Error(`Path validation failed: Attempted path "${relativePath}" resolves outside the allowed workspace directories: ${directories.join(", ")}`);
231881
231960
  }
231961
+ return this.ensureDirectory(targetPath);
231962
+ }
231963
+ ensureDirectory(targetPath) {
231882
231964
  try {
231883
231965
  const stats = fs36.statSync(targetPath);
231884
231966
  if (!stats.isDirectory()) {
@@ -231894,82 +231976,50 @@ var init_ripGrep = __esm({
231894
231976
  }
231895
231977
  async execute(signal) {
231896
231978
  try {
231897
- const workspaceContext = this.config.getWorkspaceContext();
231898
231979
  const searchDirAbs = this.resolveAndValidatePath(this.params.path);
231899
231980
  const searchDirDisplay = this.params.path || ".";
231900
- let searchDirectories;
231901
- if (searchDirAbs === null) {
231902
- searchDirectories = workspaceContext.getDirectories();
231903
- } else {
231904
- searchDirectories = [searchDirAbs];
231905
- }
231906
- let allMatches = [];
231907
- const totalMaxMatches = DEFAULT_TOTAL_MAX_MATCHES;
231908
- if (this.config.getDebugMode()) {
231909
- console.log(`[GrepTool] Total result limit: ${totalMaxMatches}`);
231910
- }
231911
- for (const searchDir of searchDirectories) {
231912
- const searchResult = await this.performRipgrepSearch({
231913
- pattern: this.params.pattern,
231914
- path: searchDir,
231915
- include: this.params.include,
231916
- signal
231917
- });
231918
- if (searchDirectories.length > 1) {
231919
- const dirName = path40.basename(searchDir);
231920
- searchResult.forEach((match2) => {
231921
- match2.filePath = path40.join(dirName, match2.filePath);
231922
- });
231923
- }
231924
- allMatches = allMatches.concat(searchResult);
231925
- if (allMatches.length >= totalMaxMatches) {
231926
- allMatches = allMatches.slice(0, totalMaxMatches);
231927
- break;
231928
- }
231929
- }
231930
- let searchLocationDescription;
231931
- if (searchDirAbs === null) {
231932
- const numDirs = workspaceContext.getDirectories().length;
231933
- searchLocationDescription = numDirs > 1 ? `across ${numDirs} workspace directories` : `in the workspace directory`;
231934
- } else {
231935
- searchLocationDescription = `in path "${searchDirDisplay}"`;
231936
- }
231937
- if (allMatches.length === 0) {
231938
- const noMatchMsg = `No matches found for pattern "${this.params.pattern}" ${searchLocationDescription}${this.params.include ? ` (filter: "${this.params.include}")` : ""}.`;
231981
+ const rawOutput = await this.performRipgrepSearch({
231982
+ pattern: this.params.pattern,
231983
+ path: searchDirAbs,
231984
+ glob: this.params.glob,
231985
+ signal
231986
+ });
231987
+ const searchLocationDescription = this.params.path ? `in path "${searchDirDisplay}"` : `in the workspace directory`;
231988
+ const filterDescription = this.params.glob ? ` (filter: "${this.params.glob}")` : "";
231989
+ if (!rawOutput.trim()) {
231990
+ const noMatchMsg = `No matches found for pattern "${this.params.pattern}" ${searchLocationDescription}${filterDescription}.`;
231939
231991
  return { llmContent: noMatchMsg, returnDisplay: `No matches found` };
231940
231992
  }
231941
- const wasTruncated = allMatches.length >= totalMaxMatches;
231942
- const matchesByFile = allMatches.reduce((acc, match2) => {
231943
- const fileKey = match2.filePath;
231944
- if (!acc[fileKey]) {
231945
- acc[fileKey] = [];
231946
- }
231947
- acc[fileKey].push(match2);
231948
- acc[fileKey].sort((a, b) => a.lineNumber - b.lineNumber);
231949
- return acc;
231950
- }, {});
231951
- const matchCount = allMatches.length;
231952
- const matchTerm = matchCount === 1 ? "match" : "matches";
231953
- let llmContent = `Found ${matchCount} ${matchTerm} for pattern "${this.params.pattern}" ${searchLocationDescription}${this.params.include ? ` (filter: "${this.params.include}")` : ""}`;
231954
- if (wasTruncated) {
231955
- llmContent += ` (results limited to ${totalMaxMatches} matches for performance)`;
231956
- }
231957
- llmContent += `:
231993
+ const allLines = rawOutput.split(EOL3).filter((line) => line.trim());
231994
+ const totalMatches = allLines.length;
231995
+ const matchTerm = totalMatches === 1 ? "match" : "matches";
231996
+ const header = `Found ${totalMatches} ${matchTerm} for pattern "${this.params.pattern}" ${searchLocationDescription}${filterDescription}:
231958
231997
  ---
231959
231998
  `;
231960
- for (const filePath in matchesByFile) {
231961
- llmContent += `File: ${filePath}
231962
- `;
231963
- matchesByFile[filePath].forEach((match2) => {
231964
- const trimmedLine = match2.line.trim();
231965
- llmContent += `L${match2.lineNumber}: ${trimmedLine}
231966
- `;
231967
- });
231968
- llmContent += "---\n";
231969
- }
231970
- let displayMessage = `Found ${matchCount} ${matchTerm}`;
231971
- if (wasTruncated) {
231972
- displayMessage += ` (limited)`;
231999
+ const maxTruncationNoticeLength = 100;
232000
+ const maxGrepOutputLength = MAX_LLM_CONTENT_LENGTH - header.length - maxTruncationNoticeLength;
232001
+ let truncatedByLineLimit = false;
232002
+ let linesToInclude = allLines;
232003
+ if (this.params.limit !== void 0 && allLines.length > this.params.limit) {
232004
+ linesToInclude = allLines.slice(0, this.params.limit);
232005
+ truncatedByLineLimit = true;
232006
+ }
232007
+ let grepOutput = linesToInclude.join(EOL3);
232008
+ let truncatedByCharLimit = false;
232009
+ if (grepOutput.length > maxGrepOutputLength) {
232010
+ grepOutput = grepOutput.slice(0, maxGrepOutputLength) + "...";
232011
+ truncatedByCharLimit = true;
232012
+ }
232013
+ const finalLines = grepOutput.split(EOL3).filter((line) => line.trim());
232014
+ const includedLines = finalLines.length;
232015
+ let llmContent = header + grepOutput;
232016
+ if (truncatedByLineLimit || truncatedByCharLimit) {
232017
+ const omittedMatches = totalMatches - includedLines;
232018
+ llmContent += ` [${omittedMatches} ${omittedMatches === 1 ? "line" : "lines"} truncated] ...`;
232019
+ }
232020
+ let displayMessage = `Found ${totalMatches} ${matchTerm}`;
232021
+ if (truncatedByLineLimit || truncatedByCharLimit) {
232022
+ displayMessage += ` (truncated)`;
231973
232023
  }
231974
232024
  return {
231975
232025
  llmContent: llmContent.trim(),
@@ -231984,38 +232034,8 @@ var init_ripGrep = __esm({
231984
232034
  };
231985
232035
  }
231986
232036
  }
231987
- parseRipgrepOutput(output, basePath) {
231988
- const results = [];
231989
- if (!output)
231990
- return results;
231991
- const lines = output.split(EOL3);
231992
- for (const line of lines) {
231993
- if (!line.trim())
231994
- continue;
231995
- const firstColonIndex = line.indexOf(":");
231996
- if (firstColonIndex === -1)
231997
- continue;
231998
- const secondColonIndex = line.indexOf(":", firstColonIndex + 1);
231999
- if (secondColonIndex === -1)
232000
- continue;
232001
- const filePathRaw = line.substring(0, firstColonIndex);
232002
- const lineNumberStr = line.substring(firstColonIndex + 1, secondColonIndex);
232003
- const lineContent = line.substring(secondColonIndex + 1);
232004
- const lineNumber = parseInt(lineNumberStr, 10);
232005
- if (!isNaN(lineNumber)) {
232006
- const absoluteFilePath = path40.resolve(basePath, filePathRaw);
232007
- const relativeFilePath = path40.relative(basePath, absoluteFilePath);
232008
- results.push({
232009
- filePath: relativeFilePath || path40.basename(absoluteFilePath),
232010
- lineNumber,
232011
- line: lineContent
232012
- });
232013
- }
232014
- }
232015
- return results;
232016
- }
232017
232037
  async performRipgrepSearch(options2) {
232018
- const { pattern, path: absolutePath, include } = options2;
232038
+ const { pattern, path: absolutePath, glob: glob2 } = options2;
232019
232039
  const rgArgs = [
232020
232040
  "--line-number",
232021
232041
  "--no-heading",
@@ -232024,26 +232044,23 @@ var init_ripGrep = __esm({
232024
232044
  "--regexp",
232025
232045
  pattern
232026
232046
  ];
232027
- if (include) {
232028
- rgArgs.push("--glob", include);
232029
- }
232030
- const excludes = [
232031
- ".git",
232032
- "node_modules",
232033
- "bower_components",
232034
- "*.log",
232035
- "*.tmp",
232036
- "build",
232037
- "dist",
232038
- "coverage"
232039
- ];
232040
- excludes.forEach((exclude) => {
232041
- rgArgs.push("--glob", `!${exclude}`);
232042
- });
232047
+ const filteringOptions = this.getFileFilteringOptions();
232048
+ if (!filteringOptions.respectGitIgnore) {
232049
+ rgArgs.push("--no-ignore-vcs");
232050
+ }
232051
+ if (filteringOptions.respectQwenIgnore) {
232052
+ const qwenIgnorePath = path40.join(this.config.getTargetDir(), ".qwenignore");
232053
+ if (fs36.existsSync(qwenIgnorePath)) {
232054
+ rgArgs.push("--ignore-file", qwenIgnorePath);
232055
+ }
232056
+ }
232057
+ if (glob2) {
232058
+ rgArgs.push("--glob", glob2);
232059
+ }
232043
232060
  rgArgs.push("--threads", "4");
232044
232061
  rgArgs.push(absolutePath);
232045
232062
  try {
232046
- const rgPath = await ensureRipgrepPath();
232063
+ const rgPath = this.config.getUseBuiltinRipgrep() ? await ensureRipgrepPath() : "rg";
232047
232064
  const output = await new Promise((resolve23, reject) => {
232048
232065
  const child = spawn6(rgPath, rgArgs, {
232049
232066
  windowsHide: true
@@ -232075,21 +232092,27 @@ var init_ripGrep = __esm({
232075
232092
  }
232076
232093
  });
232077
232094
  });
232078
- return this.parseRipgrepOutput(output, absolutePath);
232095
+ return output;
232079
232096
  } catch (error) {
232080
232097
  console.error(`GrepLogic: ripgrep failed: ${getErrorMessage(error)}`);
232081
232098
  throw error;
232082
232099
  }
232083
232100
  }
232101
+ getFileFilteringOptions() {
232102
+ const options2 = this.config.getFileFilteringOptions?.();
232103
+ return {
232104
+ respectGitIgnore: options2?.respectGitIgnore ?? DEFAULT_FILE_FILTERING_OPTIONS.respectGitIgnore,
232105
+ respectQwenIgnore: options2?.respectQwenIgnore ?? DEFAULT_FILE_FILTERING_OPTIONS.respectQwenIgnore
232106
+ };
232107
+ }
232084
232108
  /**
232085
232109
  * Gets a description of the grep operation
232086
- * @param params Parameters for the grep operation
232087
232110
  * @returns A string describing the grep
232088
232111
  */
232089
232112
  getDescription() {
232090
232113
  let description = `'${this.params.pattern}'`;
232091
- if (this.params.include) {
232092
- description += ` in ${this.params.include}`;
232114
+ if (this.params.glob) {
232115
+ description += ` in ${this.params.glob}`;
232093
232116
  }
232094
232117
  if (this.params.path) {
232095
232118
  const resolvedPath = path40.resolve(this.config.getTargetDir(), this.params.path);
@@ -232114,21 +232137,25 @@ var init_ripGrep = __esm({
232114
232137
  __name(this, "RipGrepTool");
232115
232138
  }
232116
232139
  config;
232117
- static Name = "search_file_content";
232140
+ static Name = ToolNames.GREP;
232118
232141
  constructor(config) {
232119
- super(_RipGrepTool.Name, "SearchText", "Searches for a regular expression pattern within the content of files in a specified directory (or current working directory). Can filter files by a glob pattern. Returns the lines containing matches, along with their file paths and line numbers. Total results limited to 20,000 matches like VSCode.", Kind.Search, {
232142
+ super(_RipGrepTool.Name, "Grep", 'A powerful search tool built on ripgrep\n\n Usage:\n - ALWAYS use Grep for search tasks. NEVER invoke `grep` or `rg` as a Bash command. The Grep tool has been optimized for correct permissions and access.\n - Supports full regex syntax (e.g., "log.*Error", "function\\s+\\w+")\n - Filter files with glob parameter (e.g., "*.js", "**/*.tsx")\n - Use Task tool for open-ended searches requiring multiple rounds\n - Pattern syntax: Uses ripgrep (not grep) - special regex characters need escaping (use `interface\\{\\}` to find `interface{}` in Go code)\n', Kind.Search, {
232120
232143
  properties: {
232121
232144
  pattern: {
232122
- description: "The regular expression (regex) pattern to search for within file contents (e.g., 'function\\s+myFunction', 'import\\s+\\{.*\\}\\s+from\\s+.*').",
232123
- type: "string"
232145
+ type: "string",
232146
+ description: "The regular expression pattern to search for in file contents"
232147
+ },
232148
+ glob: {
232149
+ type: "string",
232150
+ description: 'Glob pattern to filter files (e.g. "*.js", "*.{ts,tsx}") - maps to rg --glob'
232124
232151
  },
232125
232152
  path: {
232126
- description: "Optional: The absolute path to the directory to search within. If omitted, searches the current working directory.",
232127
- type: "string"
232153
+ type: "string",
232154
+ description: "File or directory to search in (rg PATH). Defaults to current working directory."
232128
232155
  },
232129
- include: {
232130
- description: "Optional: A glob pattern to filter which files are searched (e.g., '*.js', '*.{ts,tsx}', 'src/**'). If omitted, searches all files (respecting potential global ignores).",
232131
- type: "string"
232156
+ limit: {
232157
+ type: "number",
232158
+ description: "Limit output to first N lines/entries. Optional - shows all matches if not specified."
232132
232159
  }
232133
232160
  },
232134
232161
  required: ["pattern"],
@@ -232139,12 +232166,12 @@ var init_ripGrep = __esm({
232139
232166
  /**
232140
232167
  * Checks if a path is within the root directory and resolves it.
232141
232168
  * @param relativePath Path relative to the root directory (or undefined for root).
232142
- * @returns The absolute path if valid and exists, or null if no path specified (to search all directories).
232169
+ * @returns The absolute path to search within.
232143
232170
  * @throws {Error} If path is outside root, doesn't exist, or isn't a directory.
232144
232171
  */
232145
232172
  resolveAndValidatePath(relativePath) {
232146
232173
  if (!relativePath) {
232147
- return null;
232174
+ return this.config.getTargetDir();
232148
232175
  }
232149
232176
  const targetPath = path40.resolve(this.config.getTargetDir(), relativePath);
232150
232177
  const workspaceContext = this.config.getWorkspaceContext();
@@ -232170,11 +232197,16 @@ var init_ripGrep = __esm({
232170
232197
  * @param params Parameters to validate
232171
232198
  * @returns An error message string if invalid, null otherwise
232172
232199
  */
232173
- validateToolParams(params) {
232200
+ validateToolParamValues(params) {
232174
232201
  const errors = SchemaValidator.validate(this.schema.parametersJsonSchema, params);
232175
232202
  if (errors) {
232176
232203
  return errors;
232177
232204
  }
232205
+ try {
232206
+ new RegExp(params.pattern);
232207
+ } catch (error) {
232208
+ return `Invalid regular expression pattern: ${params.pattern}. Error: ${getErrorMessage(error)}`;
232209
+ }
232178
232210
  if (params.path) {
232179
232211
  try {
232180
232212
  this.resolveAndValidatePath(params.path);
@@ -243855,6 +243887,7 @@ var init_config3 = __esm({
243855
243887
  interactive;
243856
243888
  trustedFolder;
243857
243889
  useRipgrep;
243890
+ useBuiltinRipgrep;
243858
243891
  shouldUseNodePtyShell;
243859
243892
  skipNextSpeakerCheck;
243860
243893
  shellExecutionConfig;
@@ -243942,11 +243975,10 @@ var init_config3 = __esm({
243942
243975
  this.chatCompression = params.chatCompression;
243943
243976
  this.interactive = params.interactive ?? false;
243944
243977
  this.trustedFolder = params.trustedFolder;
243945
- this.shouldUseNodePtyShell = params.shouldUseNodePtyShell ?? false;
243946
- this.skipNextSpeakerCheck = params.skipNextSpeakerCheck ?? false;
243947
243978
  this.skipLoopDetection = params.skipLoopDetection ?? false;
243948
243979
  this.tavilyApiKey = params.tavilyApiKey;
243949
243980
  this.useRipgrep = params.useRipgrep ?? true;
243981
+ this.useBuiltinRipgrep = params.useBuiltinRipgrep ?? true;
243950
243982
  this.shouldUseNodePtyShell = params.shouldUseNodePtyShell ?? false;
243951
243983
  this.skipNextSpeakerCheck = params.skipNextSpeakerCheck ?? true;
243952
243984
  this.shellExecutionConfig = {
@@ -244314,6 +244346,9 @@ var init_config3 = __esm({
244314
244346
  getUseRipgrep() {
244315
244347
  return this.useRipgrep;
244316
244348
  }
244349
+ getUseBuiltinRipgrep() {
244350
+ return this.useBuiltinRipgrep;
244351
+ }
244317
244352
  getShouldUseNodePtyShell() {
244318
244353
  return this.shouldUseNodePtyShell;
244319
244354
  }
@@ -244402,13 +244437,14 @@ var init_config3 = __esm({
244402
244437
  let useRipgrep = false;
244403
244438
  let errorString = void 0;
244404
244439
  try {
244405
- useRipgrep = await canUseRipgrep();
244440
+ useRipgrep = await canUseRipgrep(this.getUseBuiltinRipgrep());
244406
244441
  } catch (error) {
244407
244442
  errorString = String(error);
244408
244443
  }
244409
244444
  if (useRipgrep) {
244410
244445
  registerCoreTool(RipGrepTool, this);
244411
244446
  } else {
244447
+ errorString = errorString || "Ripgrep is not available. Please install ripgrep globally.";
244412
244448
  logRipgrepFallback(this, new RipgrepFallbackEvent(errorString));
244413
244449
  registerCoreTool(GrepTool, this);
244414
244450
  }
@@ -251803,6 +251839,7 @@ var init_src = __esm({
251803
251839
  init_textUtils();
251804
251840
  init_formatters();
251805
251841
  init_generateContentResponseUtilities();
251842
+ init_ripgrepUtils();
251806
251843
  init_fileSearch();
251807
251844
  init_errorParsing();
251808
251845
  init_workspaceContext();
@@ -316229,7 +316266,7 @@ init_esbuild_shims();
316229
316266
 
316230
316267
  // packages/cli/src/generated/git-commit.ts
316231
316268
  init_esbuild_shims();
316232
- var GIT_COMMIT_INFO2 = "2d7b6ccf";
316269
+ var GIT_COMMIT_INFO2 = "388dee5a";
316233
316270
 
316234
316271
  // packages/cli/src/ui/components/AboutBox.tsx
316235
316272
  var import_jsx_runtime43 = __toESM(require_jsx_runtime(), 1);
@@ -317345,7 +317382,7 @@ var ToolsList = /* @__PURE__ */ __name(({
317345
317382
  showDescriptions,
317346
317383
  terminalWidth
317347
317384
  }) => /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(Box_default, { flexDirection: "column", marginBottom: 1, children: [
317348
- /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(Text, { bold: true, color: theme.text.primary, children: "Available Gemini CLI tools:" }),
317385
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(Text, { bold: true, color: theme.text.primary, children: "Available Qwen Code CLI tools:" }),
317349
317386
  /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(Box_default, { height: 1 }),
317350
317387
  tools.length > 0 ? tools.map((tool) => /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(Box_default, { flexDirection: "row", children: [
317351
317388
  /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(Text, { color: theme.text.primary, children: [
@@ -319044,6 +319081,15 @@ var SETTINGS_SCHEMA = {
319044
319081
  description: "Use ripgrep for file content search instead of the fallback implementation. Provides faster search performance.",
319045
319082
  showInDialog: true
319046
319083
  },
319084
+ useBuiltinRipgrep: {
319085
+ type: "boolean",
319086
+ label: "Use Builtin Ripgrep",
319087
+ category: "Tools",
319088
+ requiresRestart: false,
319089
+ default: true,
319090
+ description: 'Use the bundled ripgrep binary. When set to false, the system-level "rg" command will be used instead. This setting is only effective when useRipgrep is true.',
319091
+ showInDialog: true
319092
+ },
319047
319093
  enableToolOutputTruncation: {
319048
319094
  type: "boolean",
319049
319095
  label: "Enable Tool Output Truncation",
@@ -325786,6 +325832,8 @@ var USER_SETTINGS_PATH = Storage.getGlobalSettingsPath();
325786
325832
  var USER_SETTINGS_DIR2 = path71.dirname(USER_SETTINGS_PATH);
325787
325833
  var DEFAULT_EXCLUDED_ENV_VARS = ["DEBUG", "DEBUG_MODE"];
325788
325834
  var MIGRATE_V2_OVERWRITE = true;
325835
+ var SETTINGS_VERSION = 2;
325836
+ var SETTINGS_VERSION_KEY = "$version";
325789
325837
  var MIGRATION_MAP = {
325790
325838
  accessibility: "ui.accessibility",
325791
325839
  allowedTools: "tools.allowed",
@@ -325911,6 +325959,12 @@ function setNestedProperty(obj, path107, value) {
325911
325959
  }
325912
325960
  __name(setNestedProperty, "setNestedProperty");
325913
325961
  function needsMigration(settings) {
325962
+ if (SETTINGS_VERSION_KEY in settings) {
325963
+ const version2 = settings[SETTINGS_VERSION_KEY];
325964
+ if (typeof version2 === "number" && version2 >= SETTINGS_VERSION) {
325965
+ return false;
325966
+ }
325967
+ }
325914
325968
  const hasV1Keys = Object.entries(MIGRATION_MAP).some(([v1Key, v2Path]) => {
325915
325969
  if (v1Key === v2Path || !(v1Key in settings)) {
325916
325970
  return false;
@@ -325931,6 +325985,11 @@ function migrateSettingsToV2(flatSettings) {
325931
325985
  const flatKeys = new Set(Object.keys(flatSettings));
325932
325986
  for (const [oldKey, newPath] of Object.entries(MIGRATION_MAP)) {
325933
325987
  if (flatKeys.has(oldKey)) {
325988
+ if (KNOWN_V2_CONTAINERS.has(oldKey) && typeof flatSettings[oldKey] === "object" && flatSettings[oldKey] !== null && !Array.isArray(flatSettings[oldKey])) {
325989
+ v2Settings[oldKey] = flatSettings[oldKey];
325990
+ flatKeys.delete(oldKey);
325991
+ continue;
325992
+ }
325934
325993
  setNestedProperty(v2Settings, newPath, flatSettings[oldKey]);
325935
325994
  flatKeys.delete(oldKey);
325936
325995
  }
@@ -325954,6 +326013,7 @@ function migrateSettingsToV2(flatSettings) {
325954
326013
  v2Settings[remainingKey] = newValue;
325955
326014
  }
325956
326015
  }
326016
+ v2Settings[SETTINGS_VERSION_KEY] = SETTINGS_VERSION;
325957
326017
  return v2Settings;
325958
326018
  }
325959
326019
  __name(migrateSettingsToV2, "migrateSettingsToV2");
@@ -325990,6 +326050,9 @@ function migrateSettingsToV1(v2Settings) {
325990
326050
  v2Keys.delete("mcpServers");
325991
326051
  }
325992
326052
  for (const remainingKey of v2Keys) {
326053
+ if (remainingKey === SETTINGS_VERSION_KEY) {
326054
+ continue;
326055
+ }
325993
326056
  const value = v2Settings[remainingKey];
325994
326057
  if (value === void 0) {
325995
326058
  continue;
@@ -326195,6 +326258,21 @@ function loadSettings(workspaceDir = process27.cwd()) {
326195
326258
  }
326196
326259
  settingsObject = migratedSettings;
326197
326260
  }
326261
+ } else if (!(SETTINGS_VERSION_KEY in settingsObject)) {
326262
+ settingsObject[SETTINGS_VERSION_KEY] = SETTINGS_VERSION;
326263
+ if (MIGRATE_V2_OVERWRITE) {
326264
+ try {
326265
+ fs69.writeFileSync(
326266
+ filePath,
326267
+ JSON.stringify(settingsObject, null, 2),
326268
+ "utf-8"
326269
+ );
326270
+ } catch (e2) {
326271
+ console.error(
326272
+ `Error adding version to settings file: ${getErrorMessage(e2)}`
326273
+ );
326274
+ }
326275
+ }
326198
326276
  }
326199
326277
  return { settings: settingsObject, rawJson: content };
326200
326278
  }
@@ -338265,7 +338343,7 @@ __name(getPackageJson, "getPackageJson");
338265
338343
  // packages/cli/src/utils/version.ts
338266
338344
  async function getCliVersion() {
338267
338345
  const pkgJson = await getPackageJson();
338268
- return "0.1.2-nightly.20251031.ced79cf4";
338346
+ return "0.1.3-nightly.20251101.ff8a8ac6";
338269
338347
  }
338270
338348
  __name(getCliVersion, "getCliVersion");
338271
338349
 
@@ -338763,7 +338841,7 @@ async function parseArguments(settings) {
338763
338841
  'Use the "proxy" setting in settings.json instead. This flag will be removed in a future version.'
338764
338842
  ).command(
338765
338843
  "$0 [query..]",
338766
- "Launch Gemini CLI",
338844
+ "Launch Qwen Code CLI",
338767
338845
  (yargsInstance2) => yargsInstance2.positional("query", {
338768
338846
  description: "Positional prompt. Defaults to one-shot; use -i/--prompt-interactive for interactive."
338769
338847
  }).option("model", {
@@ -339146,6 +339224,7 @@ async function loadCliConfig(settings, extensions, extensionEnablementManager, s
339146
339224
  interactive,
339147
339225
  trustedFolder,
339148
339226
  useRipgrep: settings.tools?.useRipgrep,
339227
+ useBuiltinRipgrep: settings.tools?.useBuiltinRipgrep,
339149
339228
  shouldUseNodePtyShell: settings.tools?.shell?.enableInteractiveShell,
339150
339229
  skipNextSpeakerCheck: settings.model?.skipNextSpeakerCheck,
339151
339230
  enablePromptCompletion: settings.general?.enablePromptCompletion ?? false,
@@ -340228,7 +340307,7 @@ var bugCommand = {
340228
340307
  let info = `
340229
340308
  * **CLI Version:** ${cliVersion}
340230
340309
  * **Git Commit:** ${GIT_COMMIT_INFO2}
340231
- * **Session ID:** ${sessionId}
340310
+ * **Session ID:** ${config?.getSessionId() || "unknown"}
340232
340311
  * **Operating System:** ${osVersion}
340233
340312
  * **Sandbox Environment:** ${sandboxEnv}
340234
340313
  * **Auth Type:** ${selectedAuthType}`;
@@ -352954,10 +353033,10 @@ import * as os38 from "node:os";
352954
353033
  import path105 from "node:path";
352955
353034
  var homeDirectoryCheck = {
352956
353035
  id: "home-directory",
352957
- check: /* @__PURE__ */ __name(async (workspaceRoot) => {
353036
+ check: /* @__PURE__ */ __name(async (options2) => {
352958
353037
  try {
352959
353038
  const [workspaceRealPath, homeRealPath] = await Promise.all([
352960
- fs92.realpath(workspaceRoot),
353039
+ fs92.realpath(options2.workspaceRoot),
352961
353040
  fs92.realpath(os38.homedir())
352962
353041
  ]);
352963
353042
  if (workspaceRealPath === homeRealPath) {
@@ -352971,9 +353050,9 @@ var homeDirectoryCheck = {
352971
353050
  };
352972
353051
  var rootDirectoryCheck = {
352973
353052
  id: "root-directory",
352974
- check: /* @__PURE__ */ __name(async (workspaceRoot) => {
353053
+ check: /* @__PURE__ */ __name(async (options2) => {
352975
353054
  try {
352976
- const workspaceRealPath = await fs92.realpath(workspaceRoot);
353055
+ const workspaceRealPath = await fs92.realpath(options2.workspaceRoot);
352977
353056
  const errorMessage = "Warning: You are running Qwen Code in the root directory. Your entire folder structure will be used for context. It is strongly recommended to run in a project-specific directory.";
352978
353057
  if (path105.dirname(workspaceRealPath) === workspaceRealPath) {
352979
353058
  return errorMessage;
@@ -352984,13 +353063,27 @@ var rootDirectoryCheck = {
352984
353063
  }
352985
353064
  }, "check")
352986
353065
  };
353066
+ var ripgrepAvailabilityCheck = {
353067
+ id: "ripgrep-availability",
353068
+ check: /* @__PURE__ */ __name(async (options2) => {
353069
+ if (!options2.useRipgrep) {
353070
+ return null;
353071
+ }
353072
+ const isAvailable = await canUseRipgrep(options2.useBuiltinRipgrep);
353073
+ if (!isAvailable) {
353074
+ return "Ripgrep not available: Please install ripgrep globally to enable faster file content search. Falling back to built-in grep.";
353075
+ }
353076
+ return null;
353077
+ }, "check")
353078
+ };
352987
353079
  var WARNING_CHECKS = [
352988
353080
  homeDirectoryCheck,
352989
- rootDirectoryCheck
353081
+ rootDirectoryCheck,
353082
+ ripgrepAvailabilityCheck
352990
353083
  ];
352991
- async function getUserStartupWarnings(workspaceRoot = process.cwd()) {
353084
+ async function getUserStartupWarnings(options2) {
352992
353085
  const results = await Promise.all(
352993
- WARNING_CHECKS.map((check) => check.check(workspaceRoot))
353086
+ WARNING_CHECKS.map((check) => check.check(options2))
352994
353087
  );
352995
353088
  return results.filter((msg) => msg !== null);
352996
353089
  }
@@ -354917,7 +355010,11 @@ ${finalArgs[promptIndex + 1]}`;
354917
355010
  let input = config.getQuestion();
354918
355011
  const startupWarnings = [
354919
355012
  ...await getStartupWarnings(),
354920
- ...await getUserStartupWarnings()
355013
+ ...await getUserStartupWarnings({
355014
+ workspaceRoot: process.cwd(),
355015
+ useRipgrep: settings.merged.tools?.useRipgrep ?? true,
355016
+ useBuiltinRipgrep: settings.merged.tools?.useBuiltinRipgrep ?? true
355017
+ })
354921
355018
  ];
354922
355019
  if (config.isInteractive()) {
354923
355020
  await kittyProtocolDetectionComplete;