@wix/ditto-codegen-public 1.0.327 → 1.0.329

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.
@@ -0,0 +1,62 @@
1
+ import { tool } from "@opencode-ai/plugin";
2
+ import { readFile, writeFile } from "fs/promises";
3
+ import { join, isAbsolute } from "path";
4
+
5
+ export default tool({
6
+ description:
7
+ "Apply multiple find-and-replace edits across one or more files in a single call. " +
8
+ "Use this instead of calling edit multiple times. Each edit replaces oldString with newString " +
9
+ "in the named file. If oldString appears more than once and replaceAll is false, the edit fails — " +
10
+ "include more context in oldString to make it unique, or set replaceAll: true.",
11
+ args: {
12
+ edits: tool.schema
13
+ .array(
14
+ tool.schema.object({
15
+ path: tool.schema
16
+ .string()
17
+ .describe("File path relative to project root"),
18
+ oldString: tool.schema
19
+ .string()
20
+ .min(1)
21
+ .describe("Exact string to find. Must be unique unless replaceAll is true."),
22
+ newString: tool.schema.string().describe("Replacement string"),
23
+ replaceAll: tool.schema
24
+ .boolean()
25
+ .optional()
26
+ .describe("Replace every occurrence (default: false)"),
27
+ }),
28
+ )
29
+ .describe("Array of edits to apply in order"),
30
+ },
31
+ async execute(args, context) {
32
+ const results: string[] = [];
33
+ for (const e of args.edits) {
34
+ const fullPath = isAbsolute(e.path)
35
+ ? e.path
36
+ : join(context.directory, e.path);
37
+ try {
38
+ const content = await readFile(fullPath, "utf-8");
39
+ const occurrences = content.split(e.oldString).length - 1;
40
+ if (occurrences === 0) {
41
+ results.push(`FAILED: ${e.path} - oldString not found`);
42
+ continue;
43
+ }
44
+ if (occurrences > 1 && !e.replaceAll) {
45
+ results.push(
46
+ `FAILED: ${e.path} - oldString appears ${occurrences} times; pass replaceAll: true or include more surrounding context`,
47
+ );
48
+ continue;
49
+ }
50
+ const updated = e.replaceAll
51
+ ? content.split(e.oldString).join(e.newString)
52
+ : content.replace(e.oldString, e.newString);
53
+ await writeFile(fullPath, updated, "utf-8");
54
+ results.push(`OK: ${e.path}`);
55
+ } catch (err) {
56
+ const msg = err instanceof Error ? err.message : String(err);
57
+ results.push(`FAILED: ${e.path} - ${msg}`);
58
+ }
59
+ }
60
+ return results.join("\n");
61
+ },
62
+ });
package/dist/out.js CHANGED
@@ -11701,7 +11701,8 @@ var require_constants5 = __commonJS({
11701
11701
  exports2.WRITE_TOOLS = /* @__PURE__ */ new Set([
11702
11702
  "write",
11703
11703
  "edit",
11704
- "batch-write"
11704
+ "batch-write",
11705
+ "multi-edit"
11705
11706
  ]);
11706
11707
  exports2.BUILDER_TO_EXTENSION_LABEL = {
11707
11708
  dashboardPage: "dashboard page",
@@ -11867,23 +11868,25 @@ MINIMIZE TEXT OUTPUT \u2014 CRITICAL:
11867
11868
  TOOL USAGE:
11868
11869
  - \`validate\` for all validation (tsc + build).
11869
11870
  - \`uuid\` to generate UUIDs (supports count param for multiple). Do NOT use bash.
11870
- - \`batch-write\` to create new files. NEVER use \`write\` for new files (corrupts newlines).
11871
- - \`batch-read\` to read multiple files at once.
11872
- - \`multiedit\` to apply multiple edits across files. Use \`edit\` only for a single edit to one file.
11871
+ - File operations \u2014 pick the BATCH tool whenever you have 2+ files; the single-file variants are 5\u201310\xD7 slower:
11872
+ - \`batch-write\` to create N new files in one call. NEVER call \`write\` more than once per turn \u2014 use batch-write instead. \`write\` is reserved for single-file creation.
11873
+ - \`batch-read\` to read N files in one call. NEVER call \`read\` more than once per turn \u2014 use batch-read instead.
11874
+ - \`multi-edit\` to apply N find-and-replace edits across one or more files in one call. NEVER call \`edit\` more than once per turn \u2014 use multi-edit instead. \`edit\` is reserved for a single edit to one file.
11875
+ - For tools where no batch variant exists (e.g. \`grep\`, \`glob\`), still emit MULTIPLE \`tool_use\` blocks in a SINGLE response when the calls are independent. Sequential turns waste a model round-trip per call.
11873
11876
  - Before calling MCP tools, check if loaded skills already cover the API. Only use MCP for gaps.
11874
11877
  - When using MCP to look up a Wix SDK method, ALWAYS call \`ReadFullDocsMethodSchema\` (not just \`ReadFullDocsArticle\`). The schema is the source of truth for parameter shapes. Code examples in docs may use incorrect call signatures.
11875
11878
  - NEVER run preview, dev, release, or promote commands.
11876
11879
 
11877
11880
  IMPLEMENTATION WORKFLOW:
11878
11881
  1. **Plan**: Determine extension types using the \`wix-app\` skill. Generate ALL UUIDs upfront.
11879
- 2. **Build**: Create each extension using \`batch-write\`. Build all extensions before registering.
11882
+ 2. **Build**: Create every extension file in a SINGLE \`batch-write\` call. Build all extensions before registering.
11880
11883
  3. **Register**: Register all extensions in \`src/extensions.ts\`.
11881
11884
  4. **Validate**: Run \`validate\` (typecheck only). Fix any errors and re-validate until tsc passes. Then run \`validate({ runBuild: true })\` ONCE to verify the build. Pass \`installDeps: true\` ONLY when you added a new dependency to package.json in this iteration; otherwise omit it (node_modules is pre-installed).
11882
11885
  5. **Stop**: Once validation passes, STOP. Do NOT refactor, clean up, or verify.
11883
11886
 
11884
11887
  EFFICIENCY:
11885
- - Batch ALL tool calls that can run in parallel into the same step.
11886
- - Use \`multiedit\` to fix multiple errors at once.
11888
+ - Always prefer the BATCH variant: \`batch-write\` over multiple \`write\`s, \`batch-read\` over multiple \`read\`s, \`multi-edit\` over multiple \`edit\`s.
11889
+ - When fixing type errors across N files, use ONE \`multi-edit\` call with all N edits.
11887
11890
 
11888
11891
  FILE CREATION:
11889
11892
  - NEVER rewrite the same file twice. Once written, move on.
@@ -13027,6 +13030,10 @@ var require_config = __commonJS({
13027
13030
  "batch-write"
13028
13031
  /* OpenCodeTool.BatchWrite */
13029
13032
  ]: "deny",
13033
+ [
13034
+ "multi-edit"
13035
+ /* OpenCodeTool.MultiEdit */
13036
+ ]: "deny",
13030
13037
  [
13031
13038
  "validate"
13032
13039
  /* OpenCodeTool.Validate */
@@ -13148,6 +13155,16 @@ var require_parser = __commonJS({
13148
13155
  }
13149
13156
  const tool = event.part.tool;
13150
13157
  const metadata = event.part.state.metadata;
13158
+ if (tool === "batch-write") {
13159
+ for (const f of event.part.state.input?.files ?? []) {
13160
+ addFileChange(f.path, ditto_codegen_types_12.ExtensionGenerationOperation.INSERT);
13161
+ }
13162
+ }
13163
+ if (tool === "multi-edit") {
13164
+ for (const e of event.part.state.input?.edits ?? []) {
13165
+ addFileChange(e.path, ditto_codegen_types_12.ExtensionGenerationOperation.EDIT);
13166
+ }
13167
+ }
13151
13168
  const operation = (0, constants_1.resolveFileOperation)(tool, metadata);
13152
13169
  if (operation) {
13153
13170
  const filePath = event.part.state.input?.filePath;
@@ -13259,6 +13276,9 @@ var require_parser = __commonJS({
13259
13276
  if (input.files) {
13260
13277
  return { files: input.files.map(({ path }) => ({ path })) };
13261
13278
  }
13279
+ if (input.edits) {
13280
+ return { edits: input.edits.map(({ path }) => ({ path })) };
13281
+ }
13262
13282
  if (input.filePath)
13263
13283
  return { filePath: input.filePath };
13264
13284
  return {
@@ -13369,7 +13389,8 @@ var require_extension_handler = __commonJS({
13369
13389
  files: extensionFiles.map((f) => ({
13370
13390
  path: f.path,
13371
13391
  operation: f.operation
13372
- }))
13392
+ })),
13393
+ extensionType: tracked.extensionType
13373
13394
  }
13374
13395
  };
13375
13396
  }
@@ -13399,7 +13420,7 @@ var require_extension_handler = __commonJS({
13399
13420
  if (!this.extensionTypesByPath.has(normalizedPath)) {
13400
13421
  this.extensionTypesByPath.set(normalizedPath, extension.extensionType);
13401
13422
  await (0, init_task_1.completeInitTask)(ctx);
13402
- await this.createExtensionTask(normalizedPath, extension.label, filePath, ctx);
13423
+ await this.createExtensionTask(normalizedPath, extension.label, extension.extensionType, filePath, ctx);
13403
13424
  }
13404
13425
  if (!isCompleted)
13405
13426
  return;
@@ -13433,12 +13454,12 @@ var require_extension_handler = __commonJS({
13433
13454
  }
13434
13455
  }
13435
13456
  /** Registers a RUNNING sub-task in the job service for a detected extension. */
13436
- async createExtensionTask(normalizedPath, extensionType, originalPath, ctx) {
13457
+ async createExtensionTask(normalizedPath, extensionLabel, extensionType, originalPath, ctx) {
13437
13458
  const jobContext = ctx.getJobContext();
13438
13459
  if (!jobContext)
13439
13460
  return;
13440
13461
  const extensionName = (0, constants_1.extractExtensionName)(originalPath);
13441
- const description = `Creating ${extensionName} ${extensionType}`;
13462
+ const description = `Creating ${extensionName} ${extensionLabel}`;
13442
13463
  const extensionKey = `ext-${normalizedPath}`;
13443
13464
  const taskId = (0, crypto_12.randomUUID)();
13444
13465
  const extensionFolder = normalizedPath.substring(0, normalizedPath.lastIndexOf("/") + 1);
@@ -13446,7 +13467,8 @@ var require_extension_handler = __commonJS({
13446
13467
  taskId,
13447
13468
  description,
13448
13469
  status: ditto_codegen_types_12.Status.RUNNING,
13449
- extensionFolder
13470
+ extensionFolder,
13471
+ extensionType
13450
13472
  };
13451
13473
  ctx.trackedTasks.set(extensionKey, tracked);
13452
13474
  try {
@@ -13675,9 +13697,8 @@ var require_task_tracker = __commonJS({
13675
13697
  }
13676
13698
  async handleWriteEvent(tool, state) {
13677
13699
  const isCompleted = state.status === "completed";
13678
- const fileStates = tool === "batch-write" ? this.expandBatchWrite(state) : [state];
13679
- const effectiveTool = tool === "batch-write" ? "write" : tool;
13680
- for (const fileState of fileStates) {
13700
+ const expanded = this.expandWriteEvent(tool, state);
13701
+ for (const { state: fileState, effectiveTool } of expanded) {
13681
13702
  if (isCompleted) {
13682
13703
  this.trackFileChange(effectiveTool, fileState);
13683
13704
  }
@@ -13685,12 +13706,35 @@ var require_task_tracker = __commonJS({
13685
13706
  await this.extensionHandler.handle(effectiveTool, fileState, ctx);
13686
13707
  }
13687
13708
  }
13688
- expandBatchWrite(state) {
13689
- return (state.input?.files ?? []).map((f) => ({
13690
- status: state.status,
13691
- input: { filePath: f.path, content: f.content },
13692
- metadata: { exists: false }
13693
- }));
13709
+ /**
13710
+ * Expands a write-family tool event into per-file synthetic states so the
13711
+ * downstream extension/file-change collectors can stay tool-agnostic:
13712
+ * batch-write N synthetic Write states (INSERT, exists=false)
13713
+ * multi-edit → N synthetic Edit states (EDIT)
13714
+ * write/edit → passed through unchanged
13715
+ */
13716
+ expandWriteEvent(tool, state) {
13717
+ if (tool === "batch-write") {
13718
+ return (state.input?.files ?? []).map((f) => ({
13719
+ state: {
13720
+ status: state.status,
13721
+ input: { filePath: f.path, content: f.content },
13722
+ metadata: { exists: false }
13723
+ },
13724
+ effectiveTool: "write"
13725
+ }));
13726
+ }
13727
+ if (tool === "multi-edit") {
13728
+ return (state.input?.edits ?? []).map((e) => ({
13729
+ state: {
13730
+ status: state.status,
13731
+ input: { filePath: e.path, new_string: e.newString },
13732
+ metadata: { exists: true }
13733
+ },
13734
+ effectiveTool: "edit"
13735
+ }));
13736
+ }
13737
+ return [{ state, effectiveTool: tool }];
13694
13738
  }
13695
13739
  trackFileChange(tool, state) {
13696
13740
  const filePath = state.input?.filePath || state.title;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/ditto-codegen-public",
3
- "version": "1.0.327",
3
+ "version": "1.0.329",
4
4
  "description": "AI-powered Wix CLI app generator - standalone executable",
5
5
  "scripts": {
6
6
  "build": "node build.mjs",
@@ -28,5 +28,5 @@
28
28
  "esbuild": "^0.27.2",
29
29
  "vitest": "^4.0.16"
30
30
  },
31
- "falconPackageHash": "9a805eff0a678722d7889d0105313ad61c44e1c48cbc5d1ec0009099"
31
+ "falconPackageHash": "27be60515add05720642903acc4d87771920b49663a972e4bc664868"
32
32
  }