@qwen-code/qwen-code 0.16.0-preview.0 → 0.16.1

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 (89) hide show
  1. package/bundled/qc-helper/docs/configuration/settings.md +39 -37
  2. package/bundled/qc-helper/docs/features/_meta.ts +2 -0
  3. package/bundled/qc-helper/docs/features/approval-mode.md +119 -2
  4. package/bundled/qc-helper/docs/features/auto-mode.md +263 -0
  5. package/bundled/qc-helper/docs/features/commands.md +11 -10
  6. package/bundled/qc-helper/docs/features/skills.md +3 -0
  7. package/bundled/qc-helper/docs/features/structured-output.md +309 -0
  8. package/bundled/qc-helper/docs/features/sub-agents.md +47 -5
  9. package/bundled/qc-helper/docs/qwen-serve.md +119 -14
  10. package/bundled/review/SKILL.md +12 -3
  11. package/chunks/{agent-ZNQPH67I.js → agent-2JCG7FDJ.js} +9 -11
  12. package/chunks/{anthropicContentGenerator-ICBDZ6R2.js → anthropicContentGenerator-RQJNXJIY.js} +6 -3
  13. package/chunks/{askUserQuestion-WQILGUSQ.js → askUserQuestion-PQPMPNM3.js} +1 -1
  14. package/chunks/{ca-S3XJMT6P.js → ca-UZ7BANMN.js} +3 -3
  15. package/chunks/{chunk-XVHR7ATJ.js → chunk-4AOCVI6J.js} +1 -0
  16. package/chunks/chunk-7LMPOVYW.js +956 -0
  17. package/chunks/{chunk-MNPZ2WO6.js → chunk-BAZDG3QU.js} +9104 -3955
  18. package/chunks/{chunk-AEJ2DKLP.js → chunk-C6WMLUNB.js} +1 -1
  19. package/chunks/{chunk-PPHYLJSS.js → chunk-CAVZVZX6.js} +2 -2
  20. package/chunks/{chunk-VTPOO6GV.js → chunk-CSWBPY3P.js} +1 -1
  21. package/chunks/chunk-D6LBYOCX.js +19126 -0
  22. package/chunks/{chunk-NAID3ZWF.js → chunk-DMIMF3CG.js} +1 -1
  23. package/chunks/{chunk-2B7UBDY5.js → chunk-GGNTZ2NH.js} +90 -19
  24. package/chunks/chunk-HW5S7L73.js +379 -0
  25. package/chunks/{chunk-C3LHPHN2.js → chunk-JCR2WRXZ.js} +1227 -657
  26. package/chunks/{chunk-EDYSNFEM.js → chunk-L5E26RN6.js} +2 -2
  27. package/chunks/{chunk-CW44BRRA.js → chunk-MAY32HXD.js} +375 -0
  28. package/chunks/{chunk-7QXHXMC6.js → chunk-N6GSJHZ4.js} +96 -20
  29. package/chunks/{chunk-FZIUV27X.js → chunk-PVVL5Q3W.js} +31 -0
  30. package/chunks/{chunk-YHEAJFCI.js → chunk-USE2VQ5P.js} +3 -0
  31. package/chunks/{chunk-JYQUJ5DS.js → chunk-YJLGXDQJ.js} +1 -1
  32. package/chunks/{contextCommand-IGBCEXI4.js → contextCommand-XVRGKS3Q.js} +11 -13
  33. package/chunks/{cron-create-AVI3Q267.js → cron-create-IGYXQVG4.js} +27 -1
  34. package/chunks/{cron-delete-ZCEGDXXV.js → cron-delete-ETKIZCWT.js} +1 -1
  35. package/chunks/{cron-list-VN653OK5.js → cron-list-BVCUSWRU.js} +1 -1
  36. package/chunks/{de-MNR4SMAI.js → de-V4IE2OOZ.js} +3 -3
  37. package/chunks/{dist-RRYNPBOE.js → dist-4L54HRX2.js} +2 -2
  38. package/chunks/{dist-WP4AH3VK.js → dist-BXDUQ2QY.js} +1 -1
  39. package/chunks/{dist-M6GFCZ7S.js → dist-MN2PDDPR.js} +1 -1
  40. package/chunks/{edit-74Q4AFHQ.js → edit-3MLXHQPW.js} +22 -12
  41. package/chunks/{en-FIUWJSZR.js → en-HGJ2SPLM.js} +4 -3
  42. package/chunks/{enter-worktree-H72HXC7D.js → enter-worktree-OCA4SG6D.js} +35 -11
  43. package/chunks/{exit-worktree-FGIQO3S3.js → exit-worktree-6EDLXVEV.js} +35 -11
  44. package/chunks/{exitPlanMode-NBR2PK2D.js → exitPlanMode-H75KHRX4.js} +9 -11
  45. package/chunks/{fr-OFJFHLCR.js → fr-CJULI7ZX.js} +3 -3
  46. package/chunks/{geminiContentGenerator-33RP4WKD.js → geminiContentGenerator-E7Y6TCPU.js} +1 -1
  47. package/chunks/{glob-WEE3CJL6.js → glob-JFFSKARO.js} +9 -11
  48. package/chunks/{grep-DZKSBFZK.js → grep-7TAFR7MX.js} +9 -11
  49. package/chunks/{ja-V6OQ6VL7.js → ja-L7CHRQEW.js} +3 -3
  50. package/chunks/{ls-6F3VSP6S.js → ls-7HD6XG3V.js} +1 -1
  51. package/chunks/{lsp-67Y7DJN5.js → lsp-ZZSFCIWD.js} +1 -1
  52. package/chunks/{monitor-EDZWEZVS.js → monitor-YX2ABLXH.js} +21 -11
  53. package/chunks/notebook-edit-EEJEGFZR.js +756 -0
  54. package/chunks/{openaiContentGenerator-5NQG3W64.js → openaiContentGenerator-BSAWHGQJ.js} +9 -8
  55. package/chunks/{pt-ZLE6SA4A.js → pt-M6JULLEQ.js} +3 -3
  56. package/chunks/{qwenContentGenerator-4DPUUS6R.js → qwenContentGenerator-47XRHQXM.js} +11 -13
  57. package/chunks/{qwenOAuth2-JE7H47TE.js → qwenOAuth2-EEJGROP7.js} +7 -1
  58. package/chunks/{read-file-CQOF7BQ2.js → read-file-O53WD46Y.js} +4 -5
  59. package/chunks/{ripGrep-KR5LKGTI.js → ripGrep-OXNZ5Z3T.js} +9 -11
  60. package/chunks/{ru-A4OHIUNN.js → ru-QILM4HBC.js} +3 -3
  61. package/chunks/{send-message-GB4AQZNC.js → send-message-ULK4MQXJ.js} +22 -1
  62. package/chunks/{serve-GAD2PEST.js → serve-H2REZAYD.js} +13728 -3026
  63. package/chunks/{shell-E2HMCBGR.js → shell-DET66JID.js} +9 -11
  64. package/chunks/{skill-KDZH6UZ6.js → skill-ZIXPX3L3.js} +19 -6
  65. package/chunks/{src-LY4RU5AI.js → src-PN3XGQYP.js} +205 -13
  66. package/chunks/{syntheticOutput-HFL3DE7R.js → syntheticOutput-IS2X5OZ2.js} +2 -2
  67. package/chunks/{task-stop-ZQF26RXS.js → task-stop-7QSJGSSP.js} +1 -1
  68. package/chunks/{todoWrite-U4SC643O.js → todoWrite-7CVACFUX.js} +2 -2
  69. package/chunks/{tool-search-U4XQVLFU.js → tool-search-GTYLSGZ3.js} +4 -5
  70. package/chunks/{web-fetch-BRWZ4WSE.js → web-fetch-ENQ2I5JA.js} +5 -2
  71. package/chunks/{write-file-NBLRMNGB.js → write-file-NILNEZCR.js} +19 -12
  72. package/chunks/{zh-V32QONGV.js → zh-PWL2NKY3.js} +4 -3
  73. package/chunks/{zh-TW-552S24LR.js → zh-TW-S3YGWICZ.js} +4 -3
  74. package/cli.js +14832 -49049
  75. package/locales/ca.js +4 -5
  76. package/locales/de.js +4 -5
  77. package/locales/en.js +6 -5
  78. package/locales/fr.js +4 -5
  79. package/locales/ja.js +4 -5
  80. package/locales/pt.js +4 -5
  81. package/locales/ru.js +4 -5
  82. package/locales/zh-TW.js +5 -4
  83. package/locales/zh.js +5 -4
  84. package/package.json +2 -2
  85. package/chunks/chunk-3MBY4GKN.js +0 -350
  86. package/chunks/chunk-5P5XGNYH.js +0 -93
  87. package/chunks/chunk-JHMX4QTD.js +0 -2306
  88. package/chunks/chunk-SYCJMSIJ.js +0 -82
  89. package/chunks/chunk-Y6Z2O3WR.js +0 -33
@@ -0,0 +1,756 @@
1
+ // Force strict mode and setup for ESM
2
+ "use strict";
3
+ import {
4
+ StructuredToolError
5
+ } from "./chunk-L5E26RN6.js";
6
+ import {
7
+ CommitAttributionService,
8
+ DEFAULT_DIFF_OPTIONS,
9
+ createPatch,
10
+ detectLineEnding,
11
+ getDiffStat
12
+ } from "./chunk-BAZDG3QU.js";
13
+ import "./chunk-K5PGHDBN.js";
14
+ import "./chunk-O4PICXES.js";
15
+ import "./chunk-TW522KN6.js";
16
+ import "./chunk-MLZQVCF3.js";
17
+ import "./chunk-CAVZVZX6.js";
18
+ import "./chunk-G7YTSRES.js";
19
+ import "./chunk-4AOCVI6J.js";
20
+ import "./chunk-77WXWU44.js";
21
+ import "./chunk-F23NCRJ2.js";
22
+ import "./chunk-CSWBPY3P.js";
23
+ import {
24
+ FileOperationEvent,
25
+ findCellIndex,
26
+ getCellDisplayId,
27
+ getNotebookLanguage,
28
+ getSpecificMimeType,
29
+ hasStableCellIds,
30
+ inferInsertedCellSourceArrayStyle,
31
+ inferNotebookJsonFormat,
32
+ isAmbiguousCellId,
33
+ logFileOperation,
34
+ makeCellId,
35
+ normalizeEditedCell,
36
+ normalizeSource,
37
+ parseNotebook,
38
+ serializeNotebook,
39
+ toNotebookSource
40
+ } from "./chunk-JCR2WRXZ.js";
41
+ import "./chunk-UWCTAVOD.js";
42
+ import "./chunk-OFEVLU4C.js";
43
+ import "./chunk-PR4T27R7.js";
44
+ import "./chunk-MAY32HXD.js";
45
+ import "./chunk-D5NTAHYL.js";
46
+ import "./chunk-T4VD6OJ4.js";
47
+ import "./chunk-RDYWTWEM.js";
48
+ import "./chunk-YJLGXDQJ.js";
49
+ import {
50
+ BaseDeclarativeTool,
51
+ BaseToolInvocation,
52
+ ToolDisplayNames,
53
+ ToolNames
54
+ } from "./chunk-PVVL5Q3W.js";
55
+ import "./chunk-GGNTZ2NH.js";
56
+ import "./chunk-KXZ4TJB4.js";
57
+ import {
58
+ createDebugLogger,
59
+ makeRelative,
60
+ shortenPath,
61
+ unescapePath
62
+ } from "./chunk-XP27SJMH.js";
63
+ import "./chunk-E7E2MFYM.js";
64
+ import "./chunk-ZERZSAZL.js";
65
+ import "./chunk-QN5NZ3UQ.js";
66
+ import "./chunk-BR4QREVK.js";
67
+ import "./chunk-QWSRH265.js";
68
+ import {
69
+ init_esbuild_shims
70
+ } from "./chunk-A4BMJM77.js";
71
+ import {
72
+ __name
73
+ } from "./chunk-J2S4EL5Y.js";
74
+
75
+ // packages/core/src/tools/notebook-edit.ts
76
+ init_esbuild_shims();
77
+ import * as fs from "node:fs";
78
+ import * as path from "node:path";
79
+ var debugLogger = createDebugLogger("NOTEBOOK_EDIT");
80
+ function rejectNotebookPriorRead(notebookPath, reason, decision) {
81
+ debugLogger.debug("prior-read-rejected", {
82
+ path: notebookPath,
83
+ reason,
84
+ type: decision.type,
85
+ displayMessage: decision.displayMessage
86
+ });
87
+ return decision;
88
+ }
89
+ __name(rejectNotebookPriorRead, "rejectNotebookPriorRead");
90
+ var NotebookEditError = class extends Error {
91
+ constructor(message, type) {
92
+ super(message);
93
+ this.type = type;
94
+ this.name = "NotebookEditError";
95
+ }
96
+ static {
97
+ __name(this, "NotebookEditError");
98
+ }
99
+ };
100
+ function requireNotebookSource(source, mode) {
101
+ if (mode === "delete") {
102
+ return "";
103
+ }
104
+ if (typeof source !== "string") {
105
+ throw new NotebookEditError(
106
+ `new_source is required when edit_mode is "${mode}".`,
107
+ "invalid_tool_params" /* INVALID_TOOL_PARAMS */
108
+ );
109
+ }
110
+ return source;
111
+ }
112
+ __name(requireNotebookSource, "requireNotebookSource");
113
+ function displayCellId(cell, index) {
114
+ return cell ? getCellDisplayId(cell, index) : `cell-${index}`;
115
+ }
116
+ __name(displayCellId, "displayCellId");
117
+ function resolveTargetIndex(notebook, cellId, mode) {
118
+ if (!cellId) {
119
+ if (mode === "insert") {
120
+ return -1;
121
+ }
122
+ throw new NotebookEditError(
123
+ "cell_id is required for replace and delete operations.",
124
+ "invalid_tool_params" /* INVALID_TOOL_PARAMS */
125
+ );
126
+ }
127
+ if (isAmbiguousCellId(notebook, cellId)) {
128
+ throw new NotebookEditError(
129
+ `Cell ID "${cellId}" is ambiguous in the rendered notebook. Re-read the notebook and target a stable real cell ID before editing.`,
130
+ "invalid_tool_params" /* INVALID_TOOL_PARAMS */
131
+ );
132
+ }
133
+ const index = findCellIndex(notebook, cellId);
134
+ if (index === -1) {
135
+ throw new NotebookEditError(
136
+ `Cell with ID "${cellId}" not found in notebook.`,
137
+ "notebook_cell_not_found" /* NOTEBOOK_CELL_NOT_FOUND */
138
+ );
139
+ }
140
+ return index;
141
+ }
142
+ __name(resolveTargetIndex, "resolveTargetIndex");
143
+ function createNotebookCell(notebook, cellType, source, preferSourceArray) {
144
+ const cell = {
145
+ cell_type: cellType,
146
+ metadata: {},
147
+ source: toNotebookSource(source, preferSourceArray)
148
+ };
149
+ const id = makeCellId(notebook);
150
+ if (id) {
151
+ cell.id = id;
152
+ }
153
+ normalizeEditedCell(cell, cellType);
154
+ return cell;
155
+ }
156
+ __name(createNotebookCell, "createNotebookCell");
157
+ function applyNotebookEdit(rawContent, params) {
158
+ let notebook;
159
+ try {
160
+ notebook = parseNotebook(rawContent);
161
+ } catch (error) {
162
+ throw new NotebookEditError(
163
+ error instanceof Error ? error.message : String(error),
164
+ "notebook_invalid_json" /* NOTEBOOK_INVALID_JSON */
165
+ );
166
+ }
167
+ const mode = params.edit_mode ?? "replace";
168
+ const source = requireNotebookSource(params.new_source, mode);
169
+ const originalHasStableCellIds = hasStableCellIds(notebook);
170
+ const targetIndex = resolveTargetIndex(notebook, params.cell_id, mode);
171
+ const language = getNotebookLanguage(notebook);
172
+ const jsonFormat = inferNotebookJsonFormat(rawContent);
173
+ const buildResult = /* @__PURE__ */ __name((updatedNotebook, result) => {
174
+ const structuralEdit = mode === "insert" || mode === "delete";
175
+ return {
176
+ ...result,
177
+ updatedContent: serializeNotebook(updatedNotebook, jsonFormat),
178
+ requiresReadAfterWrite: structuralEdit && !(originalHasStableCellIds && hasStableCellIds(updatedNotebook))
179
+ };
180
+ }, "buildResult");
181
+ switch (mode) {
182
+ case "insert": {
183
+ const cellType = params.cell_type ?? "code";
184
+ const insertAt = targetIndex === -1 ? 0 : targetIndex + 1;
185
+ const newCell = createNotebookCell(
186
+ notebook,
187
+ cellType,
188
+ source,
189
+ inferInsertedCellSourceArrayStyle(notebook, insertAt)
190
+ );
191
+ notebook.cells.splice(insertAt, 0, newCell);
192
+ return buildResult(notebook, {
193
+ editedCellId: displayCellId(newCell, insertAt),
194
+ editedCellType: cellType,
195
+ language,
196
+ mode
197
+ });
198
+ }
199
+ case "delete": {
200
+ const [removed] = notebook.cells.splice(targetIndex, 1);
201
+ return buildResult(notebook, {
202
+ editedCellId: displayCellId(removed, targetIndex),
203
+ editedCellType: removed?.cell_type,
204
+ language,
205
+ mode
206
+ });
207
+ }
208
+ case "replace": {
209
+ const target = notebook.cells[targetIndex];
210
+ if (!target) {
211
+ throw new NotebookEditError(
212
+ `Cell index ${targetIndex} is out of range.`,
213
+ "notebook_cell_not_found" /* NOTEBOOK_CELL_NOT_FOUND */
214
+ );
215
+ }
216
+ const finalType = params.cell_type ?? target.cell_type;
217
+ target.source = toNotebookSource(source, Array.isArray(target.source));
218
+ normalizeEditedCell(target, finalType);
219
+ return buildResult(notebook, {
220
+ editedCellId: displayCellId(target, targetIndex),
221
+ editedCellType: finalType,
222
+ language,
223
+ mode
224
+ });
225
+ }
226
+ default:
227
+ throw new NotebookEditError(
228
+ `Unsupported notebook edit mode: ${mode}`,
229
+ "invalid_tool_params" /* INVALID_TOOL_PARAMS */
230
+ );
231
+ }
232
+ }
233
+ __name(applyNotebookEdit, "applyNotebookEdit");
234
+ function prepareModifiedNotebookContent(originalContent, modifiedContent, params) {
235
+ let notebook;
236
+ let originalNotebook;
237
+ try {
238
+ notebook = parseNotebook(modifiedContent);
239
+ originalNotebook = parseNotebook(originalContent);
240
+ } catch (error) {
241
+ throw new NotebookEditError(
242
+ error instanceof Error ? error.message : String(error),
243
+ "notebook_invalid_json" /* NOTEBOOK_INVALID_JSON */
244
+ );
245
+ }
246
+ return {
247
+ updatedContent: modifiedContent,
248
+ editedCellId: params.cell_id ?? "user-modified-notebook",
249
+ editedCellType: void 0,
250
+ language: getNotebookLanguage(notebook),
251
+ mode: params.edit_mode ?? "replace",
252
+ requiresReadAfterWrite: !hasStableCellIds(originalNotebook) || !hasStableCellIds(notebook)
253
+ };
254
+ }
255
+ __name(prepareModifiedNotebookContent, "prepareModifiedNotebookContent");
256
+ async function checkPriorNotebookRead(config, notebookPath, options = {}) {
257
+ if (config.getFileReadCacheDisabled()) {
258
+ return { ok: true };
259
+ }
260
+ let stats;
261
+ try {
262
+ stats = await fs.promises.stat(notebookPath);
263
+ } catch (error) {
264
+ const code = error?.code;
265
+ if (code === "ENOENT") {
266
+ if (options.expectExisting) {
267
+ return rejectNotebookPriorRead(notebookPath, "missing-after-read", {
268
+ ok: false,
269
+ type: "file_changed_since_read" /* FILE_CHANGED_SINCE_READ */,
270
+ rawMessage: `Notebook ${notebookPath} disappeared after it was read. Re-read it with the ${ToolNames.READ_FILE} tool before editing it.`,
271
+ displayMessage: `notebook disappeared after last read; re-run ${ToolNames.READ_FILE} first.`
272
+ });
273
+ }
274
+ return { ok: true };
275
+ }
276
+ return rejectNotebookPriorRead(notebookPath, "stat-failed", {
277
+ ok: false,
278
+ type: "prior_read_verification_failed" /* PRIOR_READ_VERIFICATION_FAILED */,
279
+ rawMessage: `Could not stat ${notebookPath} to verify prior notebook read (${code ?? "unknown error"}). Re-read it with the ${ToolNames.READ_FILE} tool before editing it.`,
280
+ displayMessage: `cannot verify prior read of ${notebookPath}; re-run ${ToolNames.READ_FILE} before editing this notebook.`
281
+ });
282
+ }
283
+ if (stats.isDirectory()) {
284
+ return rejectNotebookPriorRead(notebookPath, "target-is-directory", {
285
+ ok: false,
286
+ type: "target_is_directory" /* TARGET_IS_DIRECTORY */,
287
+ rawMessage: `${notebookPath} is a directory. The NotebookEdit tool only operates on .ipynb files.`,
288
+ displayMessage: "path is a directory; cannot edit as a notebook."
289
+ });
290
+ }
291
+ if (!stats.isFile()) {
292
+ return rejectNotebookPriorRead(notebookPath, "target-not-regular-file", {
293
+ ok: false,
294
+ type: "target_not_regular_file" /* TARGET_NOT_REGULAR_FILE */,
295
+ rawMessage: `${notebookPath} is not a regular file. The NotebookEdit tool only operates on .ipynb files.`,
296
+ displayMessage: "special file; cannot edit as a notebook."
297
+ });
298
+ }
299
+ const status = config.getFileReadCache().check(stats);
300
+ if (status.state === "fresh" && status.entry.lastReadAt !== void 0 && status.entry.lastReadWasFull) {
301
+ return { ok: true };
302
+ }
303
+ if (status.state === "fresh" && status.entry.lastReadAt !== void 0 && !status.entry.lastReadWasFull) {
304
+ return rejectNotebookPriorRead(notebookPath, "truncated-cache-entry", {
305
+ ok: false,
306
+ type: "edit_requires_prior_read" /* EDIT_REQUIRES_PRIOR_READ */,
307
+ rawMessage: `Notebook ${notebookPath} is too large for cell-level editing because its rendered output was truncated when read. Reduce the notebook output size or split the notebook before editing cells.`,
308
+ displayMessage: "notebook too large for cell-level editing."
309
+ });
310
+ }
311
+ if (status.state === "stale") {
312
+ return rejectNotebookPriorRead(notebookPath, "stale-cache-entry", {
313
+ ok: false,
314
+ type: "file_changed_since_read" /* FILE_CHANGED_SINCE_READ */,
315
+ rawMessage: `Notebook ${notebookPath} has been modified since you last read it. Re-read it with the ${ToolNames.READ_FILE} tool before editing it.`,
316
+ displayMessage: `notebook changed since last read; re-run ${ToolNames.READ_FILE} first.`
317
+ });
318
+ }
319
+ return rejectNotebookPriorRead(notebookPath, `cache-${status.state}`, {
320
+ ok: false,
321
+ type: "edit_requires_prior_read" /* EDIT_REQUIRES_PRIOR_READ */,
322
+ rawMessage: `Notebook ${notebookPath} has not been fully read in this session. Use the ${ToolNames.READ_FILE} tool first, without offset or limit, before editing cells.`,
323
+ displayMessage: `${ToolNames.READ_FILE} required before editing this notebook.`
324
+ });
325
+ }
326
+ __name(checkPriorNotebookRead, "checkPriorNotebookRead");
327
+ var NotebookEditInvocation = class extends BaseToolInvocation {
328
+ constructor(config, params, modifyMetadata) {
329
+ super(params);
330
+ this.config = config;
331
+ this.modifyMetadata = modifyMetadata;
332
+ }
333
+ static {
334
+ __name(this, "NotebookEditInvocation");
335
+ }
336
+ toolLocations() {
337
+ return [{ path: this.params.notebook_path }];
338
+ }
339
+ getDescription() {
340
+ const relativePath = makeRelative(
341
+ this.params.notebook_path,
342
+ this.config.getTargetDir()
343
+ );
344
+ const mode = this.params.edit_mode ?? "replace";
345
+ const cell = this.params.cell_id ?? "beginning";
346
+ return `${mode} notebook cell ${cell} in ${shortenPath(relativePath)}`;
347
+ }
348
+ async getDefaultPermission() {
349
+ return "ask";
350
+ }
351
+ async getConfirmationDetails(abortSignal) {
352
+ const prepared = await this.prepareEdit(abortSignal);
353
+ const fileName = path.basename(this.params.notebook_path);
354
+ const fileDiff = createPatch(
355
+ fileName,
356
+ prepared.originalContent,
357
+ prepared.updatedContent,
358
+ "Current",
359
+ "Proposed",
360
+ DEFAULT_DIFF_OPTIONS
361
+ );
362
+ const confirmationDetails = {
363
+ type: "edit",
364
+ title: `Confirm Notebook Edit: ${shortenPath(makeRelative(this.params.notebook_path, this.config.getTargetDir()))}`,
365
+ fileName,
366
+ filePath: this.params.notebook_path,
367
+ fileDiff,
368
+ originalContent: prepared.originalContent,
369
+ newContent: prepared.updatedContent,
370
+ onConfirm: /* @__PURE__ */ __name(async (outcome) => {
371
+ if (outcome === "proceed_always" /* ProceedAlways */) {
372
+ this.config.setApprovalMode("auto-edit" /* AUTO_EDIT */);
373
+ }
374
+ }, "onConfirm")
375
+ };
376
+ return confirmationDetails;
377
+ }
378
+ async prepareEdit(abortSignal) {
379
+ const preDecision = await checkPriorNotebookRead(
380
+ this.config,
381
+ this.params.notebook_path
382
+ );
383
+ if (!preDecision.ok) {
384
+ throw new StructuredToolError(preDecision.rawMessage, preDecision.type);
385
+ }
386
+ let originalContent;
387
+ let bom = false;
388
+ let encoding;
389
+ let lineEnding = "lf";
390
+ try {
391
+ const fileInfo = await this.config.getFileSystemService().readTextFile({
392
+ path: this.params.notebook_path
393
+ });
394
+ originalContent = fileInfo.content;
395
+ bom = fileInfo._meta?.bom ?? false;
396
+ encoding = fileInfo._meta?.encoding;
397
+ lineEnding = fileInfo._meta?.lineEnding ?? detectLineEnding(fileInfo.content);
398
+ } catch (error) {
399
+ if (abortSignal.aborted) {
400
+ throw error;
401
+ }
402
+ const code = error?.code;
403
+ if (code === "ENOENT") {
404
+ throw new StructuredToolError(
405
+ `Notebook file not found: ${this.params.notebook_path}`,
406
+ "file_not_found" /* FILE_NOT_FOUND */
407
+ );
408
+ }
409
+ throw new StructuredToolError(
410
+ `Error reading notebook: ${error instanceof Error ? error.message : String(error)}`,
411
+ "read_content_failure" /* READ_CONTENT_FAILURE */
412
+ );
413
+ }
414
+ const postDecision = await checkPriorNotebookRead(
415
+ this.config,
416
+ this.params.notebook_path,
417
+ { expectExisting: true }
418
+ );
419
+ if (!postDecision.ok) {
420
+ throw new StructuredToolError(postDecision.rawMessage, postDecision.type);
421
+ }
422
+ try {
423
+ if (this.modifyMetadata?.modifiedNotebookContent !== void 0) {
424
+ return {
425
+ ...prepareModifiedNotebookContent(
426
+ originalContent,
427
+ this.modifyMetadata.modifiedNotebookContent,
428
+ this.params
429
+ ),
430
+ originalContent,
431
+ bom,
432
+ encoding,
433
+ lineEnding
434
+ };
435
+ }
436
+ return {
437
+ ...applyNotebookEdit(originalContent, this.params),
438
+ originalContent,
439
+ bom,
440
+ encoding,
441
+ lineEnding
442
+ };
443
+ } catch (error) {
444
+ if (error instanceof NotebookEditError) {
445
+ throw new StructuredToolError(error.message, error.type);
446
+ }
447
+ throw error;
448
+ }
449
+ }
450
+ async execute(signal) {
451
+ let prepared;
452
+ try {
453
+ prepared = await this.prepareEdit(signal);
454
+ } catch (error) {
455
+ if (signal.aborted) {
456
+ throw error;
457
+ }
458
+ const errorType = error instanceof StructuredToolError ? error.errorType : "notebook_edit_failure" /* NOTEBOOK_EDIT_FAILURE */;
459
+ const message = error instanceof Error ? error.message : String(error);
460
+ return {
461
+ llmContent: message,
462
+ returnDisplay: `Error: ${message}`,
463
+ error: {
464
+ message,
465
+ type: errorType
466
+ }
467
+ };
468
+ }
469
+ try {
470
+ try {
471
+ await this.config.getFileHistoryService().trackEdit(this.params.notebook_path);
472
+ } catch {
473
+ }
474
+ const writeDecision = await checkPriorNotebookRead(
475
+ this.config,
476
+ this.params.notebook_path,
477
+ { expectExisting: true }
478
+ );
479
+ if (!writeDecision.ok) {
480
+ return {
481
+ llmContent: writeDecision.rawMessage,
482
+ returnDisplay: `Error: ${writeDecision.displayMessage}`,
483
+ error: {
484
+ message: writeDecision.rawMessage,
485
+ type: writeDecision.type
486
+ }
487
+ };
488
+ }
489
+ await this.config.getFileSystemService().writeTextFile({
490
+ path: this.params.notebook_path,
491
+ content: prepared.updatedContent,
492
+ _meta: {
493
+ bom: prepared.bom,
494
+ encoding: prepared.encoding,
495
+ lineEnding: prepared.lineEnding
496
+ }
497
+ });
498
+ if (!this.modifyMetadata?.modifiedByUser) {
499
+ CommitAttributionService.getInstance().recordEdit(
500
+ this.params.notebook_path,
501
+ prepared.originalContent,
502
+ prepared.updatedContent
503
+ );
504
+ }
505
+ try {
506
+ const postWriteStats = fs.statSync(this.params.notebook_path);
507
+ const cache = this.config.getFileReadCache();
508
+ if (prepared.requiresReadAfterWrite) {
509
+ cache.invalidate(postWriteStats);
510
+ } else {
511
+ cache.recordWrite(this.params.notebook_path, postWriteStats, {
512
+ cacheable: false
513
+ });
514
+ }
515
+ } catch (error) {
516
+ debugLogger.warn(
517
+ `[NotebookEdit] post-write cache update failed for ${this.params.notebook_path}: ${error instanceof Error ? error.message : String(error)}`
518
+ );
519
+ }
520
+ const fileName = path.basename(this.params.notebook_path);
521
+ const fileDiff = createPatch(
522
+ fileName,
523
+ prepared.originalContent,
524
+ prepared.updatedContent,
525
+ "Current",
526
+ "Proposed",
527
+ DEFAULT_DIFF_OPTIONS
528
+ );
529
+ const diffStat = getDiffStat(
530
+ fileName,
531
+ prepared.originalContent,
532
+ this.modifyMetadata?.aiProposedContent ?? prepared.updatedContent,
533
+ prepared.updatedContent
534
+ );
535
+ logFileOperation(
536
+ this.config,
537
+ new FileOperationEvent(
538
+ NotebookEditTool.Name,
539
+ "update" /* UPDATE */,
540
+ prepared.updatedContent.split("\n").length,
541
+ getSpecificMimeType(this.params.notebook_path),
542
+ ".ipynb",
543
+ prepared.language
544
+ )
545
+ );
546
+ const displayResult = {
547
+ fileDiff,
548
+ fileName,
549
+ originalContent: prepared.originalContent,
550
+ newContent: prepared.updatedContent,
551
+ diffStat
552
+ };
553
+ const llmContent = this.modifyMetadata?.modifiedNotebookContent !== void 0 ? `Notebook ${this.params.notebook_path} has been updated. Notebook content was modified by the user before approval; the final saved notebook may differ from the original ${prepared.mode} cell ${prepared.editedCellId} proposal.` : `Notebook ${this.params.notebook_path} has been updated. ${prepared.mode} cell ${prepared.editedCellId}.${prepared.mode === "delete" ? "" : `
554
+
555
+ Updated source:
556
+
557
+ ---
558
+
559
+ ${normalizeSource(this.params.new_source ?? "")}`}`;
560
+ return {
561
+ llmContent,
562
+ returnDisplay: displayResult,
563
+ resultFilePaths: [this.params.notebook_path]
564
+ };
565
+ } catch (error) {
566
+ const message = error instanceof Error ? error.message : String(error);
567
+ return {
568
+ llmContent: `Error writing notebook: ${message}`,
569
+ returnDisplay: `Error writing notebook: ${message}`,
570
+ error: {
571
+ message,
572
+ type: "file_write_failure" /* FILE_WRITE_FAILURE */
573
+ }
574
+ };
575
+ }
576
+ }
577
+ };
578
+ var NotebookEditTool = class _NotebookEditTool extends BaseDeclarativeTool {
579
+ constructor(config) {
580
+ super(
581
+ _NotebookEditTool.Name,
582
+ ToolDisplayNames.NOTEBOOK_EDIT,
583
+ `Edits a Jupyter notebook (.ipynb) safely at the cell level. Use this instead of ${ToolNames.EDIT} or ${ToolNames.WRITE_FILE} for notebook cells. Supports replacing, inserting, and deleting cells. Always read the notebook first with ${ToolNames.READ_FILE}; then use the cell IDs shown in that output.`,
584
+ "edit" /* Edit */,
585
+ {
586
+ properties: {
587
+ notebook_path: {
588
+ description: "Absolute path to the Jupyter notebook file to edit. Must end with .ipynb.",
589
+ type: "string"
590
+ },
591
+ cell_id: {
592
+ description: "Target cell ID from read_file output, or cell-N 0-based fallback. Required for replace and delete. For insert, the new cell is inserted after this cell; if omitted, inserted at the beginning.",
593
+ type: "string"
594
+ },
595
+ new_source: {
596
+ description: "New source content for replace and insert operations. Not required for delete.",
597
+ type: "string"
598
+ },
599
+ cell_type: {
600
+ description: "Cell type for inserted cells or type conversion on replace.",
601
+ type: "string",
602
+ enum: ["code", "markdown"]
603
+ },
604
+ edit_mode: {
605
+ description: "Notebook edit operation. Defaults to replace.",
606
+ type: "string",
607
+ enum: ["replace", "insert", "delete"]
608
+ }
609
+ },
610
+ required: ["notebook_path"],
611
+ additionalProperties: false,
612
+ type: "object"
613
+ }
614
+ );
615
+ this.config = config;
616
+ }
617
+ static {
618
+ __name(this, "NotebookEditTool");
619
+ }
620
+ static Name = ToolNames.NOTEBOOK_EDIT;
621
+ modifyMetadataByParams = /* @__PURE__ */ new WeakMap();
622
+ modifyMetadataByKey = /* @__PURE__ */ new Map();
623
+ validateToolParamValues(params) {
624
+ params.notebook_path = unescapePath(params.notebook_path.trim());
625
+ if (!params.notebook_path) {
626
+ return "The 'notebook_path' parameter must be non-empty.";
627
+ }
628
+ if (!path.isAbsolute(params.notebook_path)) {
629
+ return `Notebook path must be absolute: ${params.notebook_path}`;
630
+ }
631
+ if (path.extname(params.notebook_path).toLowerCase() !== ".ipynb") {
632
+ return "File must be a Jupyter notebook (.ipynb). Use the edit tool for other file types.";
633
+ }
634
+ const mode = params.edit_mode ?? "replace";
635
+ if (!["replace", "insert", "delete"].includes(mode)) {
636
+ return "edit_mode must be 'replace', 'insert', or 'delete'.";
637
+ }
638
+ if (params.cell_type && !["code", "markdown"].includes(params.cell_type)) {
639
+ return "cell_type must be 'code' or 'markdown'.";
640
+ }
641
+ if (mode !== "insert" && !params.cell_id) {
642
+ return "cell_id is required for replace and delete operations.";
643
+ }
644
+ if (mode !== "delete" && typeof params.new_source !== "string") {
645
+ return `new_source is required when edit_mode is "${mode}".`;
646
+ }
647
+ const fileService = this.config.getFileService();
648
+ if (fileService.shouldQwenIgnoreFile(params.notebook_path)) {
649
+ return `File path '${params.notebook_path}' is ignored by .qwenignore pattern(s).`;
650
+ }
651
+ return null;
652
+ }
653
+ createInvocation(params) {
654
+ return new NotebookEditInvocation(
655
+ this.config,
656
+ params,
657
+ this.consumeModifyMetadata(params)
658
+ );
659
+ }
660
+ getModifyMetadataKey(params) {
661
+ return JSON.stringify({
662
+ notebook_path: unescapePath(params.notebook_path.trim()),
663
+ cell_id: params.cell_id,
664
+ new_source: params.new_source,
665
+ cell_type: params.cell_type,
666
+ edit_mode: params.edit_mode
667
+ });
668
+ }
669
+ rememberModifyMetadata(params, metadata) {
670
+ this.modifyMetadataByParams.set(params, metadata);
671
+ const key = this.getModifyMetadataKey(params);
672
+ const queue = this.modifyMetadataByKey.get(key) ?? [];
673
+ queue.push(metadata);
674
+ this.modifyMetadataByKey.set(key, queue);
675
+ }
676
+ consumeModifyMetadata(params) {
677
+ const metadata = this.modifyMetadataByParams.get(params);
678
+ if (metadata) {
679
+ this.modifyMetadataByParams.delete(params);
680
+ this.removeQueuedModifyMetadata(params, metadata);
681
+ return metadata;
682
+ }
683
+ const key = this.getModifyMetadataKey(params);
684
+ const queue = this.modifyMetadataByKey.get(key);
685
+ const queuedMetadata = queue?.shift();
686
+ if (queue && queue.length === 0) {
687
+ this.modifyMetadataByKey.delete(key);
688
+ }
689
+ return queuedMetadata;
690
+ }
691
+ removeQueuedModifyMetadata(params, metadata) {
692
+ const key = this.getModifyMetadataKey(params);
693
+ const queue = this.modifyMetadataByKey.get(key);
694
+ if (!queue) {
695
+ return;
696
+ }
697
+ const index = queue.indexOf(metadata);
698
+ if (index !== -1) {
699
+ queue.splice(index, 1);
700
+ }
701
+ if (queue.length === 0) {
702
+ this.modifyMetadataByKey.delete(key);
703
+ }
704
+ }
705
+ getModifyContext(_abortSignal) {
706
+ const currentContentSnapshots = /* @__PURE__ */ new Map();
707
+ const readCurrentContentSnapshot = /* @__PURE__ */ __name(async (params) => {
708
+ const cached = currentContentSnapshots.get(params.notebook_path);
709
+ if (cached !== void 0) {
710
+ return cached;
711
+ }
712
+ const { content } = await this.config.getFileSystemService().readTextFile({ path: params.notebook_path });
713
+ currentContentSnapshots.set(params.notebook_path, content);
714
+ return content;
715
+ }, "readCurrentContentSnapshot");
716
+ return {
717
+ getFilePath: /* @__PURE__ */ __name((params) => params.notebook_path, "getFilePath"),
718
+ getCurrentContent: /* @__PURE__ */ __name(async (params) => readCurrentContentSnapshot(params), "getCurrentContent"),
719
+ getProposedContent: /* @__PURE__ */ __name(async (params) => {
720
+ const content = await readCurrentContentSnapshot(params);
721
+ return applyNotebookEdit(content, params).updatedContent;
722
+ }, "getProposedContent"),
723
+ createUpdatedParams: /* @__PURE__ */ __name((oldContent, modifiedProposedContent, originalParams) => {
724
+ let aiProposedContent = this.modifyMetadataByParams.get(originalParams)?.aiProposedContent;
725
+ if (aiProposedContent === void 0) {
726
+ try {
727
+ aiProposedContent = applyNotebookEdit(
728
+ oldContent,
729
+ originalParams
730
+ ).updatedContent;
731
+ } catch {
732
+ aiProposedContent = modifiedProposedContent;
733
+ }
734
+ }
735
+ const updatedParams = {
736
+ ...originalParams
737
+ };
738
+ this.rememberModifyMetadata(updatedParams, {
739
+ aiProposedContent,
740
+ modifiedByUser: true,
741
+ modifiedNotebookContent: modifiedProposedContent
742
+ });
743
+ return updatedParams;
744
+ }, "createUpdatedParams")
745
+ };
746
+ }
747
+ };
748
+ export {
749
+ NotebookEditTool,
750
+ applyNotebookEdit
751
+ };
752
+ /**
753
+ * @license
754
+ * Copyright 2026 Qwen Team
755
+ * SPDX-License-Identifier: Apache-2.0
756
+ */