# cms-edit: Feature Requests Archive — `rtf replace` Improvements

> **Archive notice (2026-03-26):** All items in this file have been implemented. FR-1 (`--protect-pattern`), FR-2 (`--find-plain`), FR-3 (`--ignore-marks`), and FR-4 (`--in-table-col`) are all available in `rtf replace`. Retained for historical context.

These requests were identified during real-world compliance automation work where `cms-edit rtf replace` was used to surgically insert Quidget codes into credit card affiliate articles. Each issue caused a failed or unsafe replacement and required a discard-and-redo.

---

## FR-1 — `rtf replace`: `--mode all` must skip content inside existing quidget spans

### Problem

Once a quidget code has been inserted into a text node (e.g. `{*credit_card_id*:*2061159*,*field*:*annual_fees*,*api*:*cc*,*case*:*uf*,*placeholder*:*$95*}`), a subsequent `--find '$95' --mode all` matches the `$95` character sequence inside the placeholder value of the already-inserted code. This nests a new quidget code inside the old one, producing:

```
{*credit_card_id*:*2061159*,...,*placeholder*:*{*credit_card_id*:*2061159*,...,*placeholder*:*$95*}*}
```

The document is now corrupt and the quidget will not render correctly.

### Reproducing scenario

```bash
# Step 1 — insert a quidget code for one occurrence of $95
cms-edit rtf replace @c1 body \
  --find 'justify the $95 annual fee' \
  --replace-plain 'justify the {*credit_card_id*:*2061159*,*field*:*annual_fees*,*api*:*cc*,*case*:*uf*,*placeholder*:*$95*} annual fee' \
  --mode exactlyOne

# Step 2 — now try to replace remaining $95 occurrences
cms-edit rtf replace @c1 body \
  --find '$95' \
  --replace-plain '{*credit_card_id*:*2061159*,*field*:*annual_fees*,*api*:*cc*,*case*:*uf*,*placeholder*:*$95*}' \
  --mode all
# ❌ Step 2 also matches $95 inside the placeholder written in Step 1
```

### Proposed solution

When scanning text nodes for `--find` matches, treat any span matching `{*...*}` as opaque — skip over it entirely. The tool already understands quidget token syntax (it writes them via `--replace-plain`); it should also recognise them as protected spans during search, the same way a text editor's Find would not match inside a non-editable region.

---

## FR-2 — `rtf replace`: add `--find-plain` to search for literal asterisks

### Problem

`--replace-plain` exists so that quidget tokens containing `*` are written as literal characters rather than parsed as Markdown bold markers. There is no equivalent flag for `--find`. This makes it impossible to search for any string that contains `*`, including:

- Existing quidget codes (e.g. to verify or fix a previously inserted code)
- Any content that legitimately contains asterisks

As a practical consequence, when FR-1's corruption does occur, there is no surgical way to fix the corrupted node — it can only be corrected by discarding the entire session and starting over.

### Reproducing scenario

```bash
# Try to fix a corrupted quidget by searching for its text — fails silently
cms-edit rtf replace @c1 body \
  --find 'placeholder*:*{*credit_card_id' \
  --replace-plain 'placeholder*:*$95*}' \
  --mode all
# ❌ Warning: No occurrence of --find in "body" on @c1; session unchanged for this field.
# (asterisks are parsed as Markdown bold markers, not literal characters)
```

### Proposed solution

Add a `--find-plain` flag (mirroring `--replace-plain`) that treats the `--find` argument as a literal string with no Markdown interpretation. Combined with FR-1, this would allow both safe searching and recovery from corruption.

```bash
# Desired behaviour with --find-plain
cms-edit rtf replace @c1 body \
  --find-plain 'placeholder*:*{*credit_card_id*:*2061159*,...' \
  --replace-plain 'placeholder*:*$95*}' \
  --mode exactlyOne
```

---

## FR-3 — `rtf replace`: `--ignore-marks` to match across inline formatting boundaries

### Problem

The `--find` search operates per text node. A paragraph stored as `**Annual fee: **$95` is two adjacent text nodes — one bold (`Annual fee: `) and one normal (`$95`) — so `--find "Annual fee: $95"` finds nothing. This affects every bold-label + plain-value pattern in credit card articles:

- `**Annual fee: **$95`
- `**Foreign transaction fees: **None`
- `**Current welcome bonus: **75,000 points…`

These are extremely common in affiliate content. Because the label cannot be used as context, the search string must be the bare value (`$95`), which is then often not unique across the document.

### Reproducing scenario

```bash
# Article body contains: **Annual fee: **$95
cms-edit rtf replace @c1 body \
  --find 'Annual fee: $95' \
  --replace-plain 'Annual fee: {*credit_card_id*:*2061159*,*field*:*annual_fees*,...*}' \
  --mode exactlyOne
# ❌ Error: No occurrence of find string in document
# (because "Annual fee: " and "$95" are in different text nodes)
```

### Proposed solution

Add an `--ignore-marks` flag that, for the purpose of matching only, concatenates the text content of adjacent sibling nodes within the same block (paragraph, list item, table cell). The replacement then applies to the matching text while preserving the original inline marks in the output.

**Alternative (lower effort):** If the full implementation is complex, at minimum document this limitation in the `rtf replace --help` output with a concrete example, so that LLMs using the tool do not waste retries on searches that can never succeed.

---

## FR-4 — `rtf replace`: table column targeting

### Problem

When a comparison table contains the same plain-text value in multiple columns — for example, both the primary card and a competitor showing `None` for foreign transaction fees — there is no way to target only the column belonging to the primary card. Any `--find "None"` replacement hits every matching cell in every column.

In the specific case that prompted this request: a Chase Sapphire Preferred vs. Chase Sapphire Reserve comparison table has `None` in both the CSP and CSR foreign-transaction-fee cells. Replacing all occurrences would incorrectly tag the CSR cell with the CSP card's quidget code.

### Proposed solution

**Option A (preferred):** A `--in-table-col N` flag (1-indexed) that restricts matches to cells in the Nth column of any table encountered in the field.

```bash
cms-edit rtf replace @c1 body \
  --find 'None' \
  --replace-plain '{*credit_card_id*:*2061159*,*field*:*foreign_transaction_fee*,...*}' \
  --in-table-col 2 \
  --mode all
```

**Option B:** An `--after "text"` flag that requires the match to appear after a specific string in document order, scoped to the nearest enclosing table row.

---

## Minor: `diff` output for rich text fields

The `diff` command currently shows a single-line before/after delta for an entire rich text body field, making it impossible to verify which text runs actually changed before saving.

**Requested:** Show a count of changed text runs (e.g. `7 text run(s) changed`) and/or show abbreviated before/after excerpts for each changed span. This would let an LLM confirm that exactly the expected number of replacements took effect without requiring a full re-read of the field.
