Skip to content

batch: edit/replace sub-ops re-tokenize on ::: — content containing ::: fails inside batch #252

Description

@fdaviddpt

Summary

batch:@file.json (and the ops wrapper form) breaks edit/replace/replace_lines sub-ops whose old/new value contains a literal :::. The same edit works fine as a standalone edit:@- call.

The @file JSON route is supposed to bypass ::: tokenization (fields are explicit: old, new, path). Standalone edit:@- honors that. But when the edit runs inside batch, the sub-op appears to be re-serialized to the colon-CLI form (edit:::OLD:::NEW:::PATH) and re-parsed — so a ::: inside the content collides with the field separator.

Repro

Payload (.max/ops.json):

{
  "continue_on_error": false,
  "ops": [
    { "op": "edit", "path": ".supertool.json",
      "old": "\"example\": \"read:OLD.php:::grep=class\"",
      "new": "\"example\": \"read:src/Foo.php:::grep=class\"" }
  ]
}
./supertool 'batch:@.max/ops.json'

Actual

--- edit:::      "example": "read:...:::grep=class":::      "example": "read:src/Foo.php:::grep=class":::.supertool.json ---
ERROR: file not found:       "example": "read:src/Foo.php

Everything after the first ::: in new is treated as PATH.

Expected

Edit applies. The JSON old/new/path fields are explicit and should never be re-tokenized — exactly as standalone edit:@- behaves.

Workaround

Run any edit whose content contains ::: as an individual edit:@- stdin call instead of inside batch.

Likely location

batch handler in supertool.py — where it dispatches each ops[] entry to the underlying op. It should pass the structured fields straight through to the op implementation rather than rebuilding a colon-CLI string. Standalone edit:@file already does this correctly; batch should reuse that path.

Impact

Silent-ish failure mid-batch: with continue_on_error: false the batch halts at the first :::-containing edit, leaving earlier edits applied and later ones skipped — easy to mistake for a partial success. Hit this compacting .supertool.json (shortening read:PATH:::grep= examples).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions