/engine-conflict-resolve
Ask Sutando to resolve a conflicted update of a Sutando checkout. When pulling the latest release/main conflicts with local changes, Sutando reproduces the merge in a scratch git worktree, resolves the conflicts there, proposes the resolution to the owner, and fast-forwards the
Claude Code
$ npx -y skills add sonichi/sutando --skill engine-conflict-resolve --agent claude-code
Codex
$ npx skills add sonichi/sutando --skill engine-conflict-resolve -a codex -y
Cursor
$ npx skills add sonichi/sutando --skill engine-conflict-resolve -a cursor -y
Gemini CLI
$ npx skills add sonichi/sutando --skill engine-conflict-resolve -a gemini-cli -y
GitHub Copilot
$ npx skills add sonichi/sutando --skill engine-conflict-resolve -a github-copilot -y
OpenCode
$ npx skills add sonichi/sutando --skill engine-conflict-resolve -a opencode -y
Windsurf
$ npx skills add sonichi/sutando --skill engine-conflict-resolve -a windsurf -y
SKILL.md
engine-conflict-resolve.SKILL.mdname: engine-conflict-resolve
description: Ask Sutando to resolve a conflicted update of a Sutando checkout. When pulling the latest release/main conflicts with local changes, Sutando reproduces the merge in a scratch git worktree, resolves the conflicts there, proposes the resolution to the owner, and fast-forwards the live checkout only after explicit confirmation. First entry path is the desktop app's "Resolve with Sutando" engine-update button.
engine-conflict-resolve
Anyone running Sutando from a git checkout can ask it to pull the latest main/release — and when the update conflicts with their local changes, ask Sutando to resolve the merge itself. This skill is that protocol: reproduce the merge in a scratch worktree, resolve the conflicts there semantically, propose the result, and land it in the live checkout only after the owner explicitly confirms.
The first producer of the pending state is the desktop app's git-aware engine updater: on a conflicted update it aborts completely and records `ENGINE_UPDATE_PENDING.json`; the app's "Resolve with Sutando" button then writes a task file `tasks/task-engine-conflict-<epoch>.txt` (`source: desktop-app`, `interaction_type: system_event`) whose body names the pending file and the engine checkout. That task is one entry path, not the feature: the scripts take the pending-file shape as input from any producer (a ref-based entry point — merge any two refs with no pending file — plus a chat-facing command are queued follow-ups).
**Iron rule: never modify the live checkout until the owner confirms.** All merge work happens in a scratch worktree; only `apply.py` touches the live checkout, and only behind the updater's own lock and staleness guards.
Protocol
1. Prepare — reproduce the merge in a scratch worktree
Take `--pending` and `--engine` verbatim from the task body (both paths may contain spaces — always double-quote).
python3 skills/engine-conflict-resolve/scripts/prepare.py \
--pending "<…/ENGINE_UPDATE_PENDING.json>" \
--engine "<…/engine/sutando>"
- `{"status": "clean", "merged_sha": …}` — the merge replays cleanly (the
recorded conflict no longer reproduces). Skip step 2 but still RUN step 3: only `propose.py` records the proposal `apply.py` will accept. Report "merged cleanly, no conflicts remained".
- `{"status": "conflicts", "scratch": …, "conflicting_files": […]}` — resolve
in the scratch (step 2).
- `{"status": "error", "reason": "pending-stale", …}` — the checkout moved
since the conflict was recorded. Do NOT improvise: report to the owner that the state is stale and that relaunching the app (or re-running the updater) will re-detect. Stop.
- Re-running prepare is safe: an existing scratch for the same merge is reused.
2. Resolve the conflicts IN THE SCRATCH
For each file in `conflicting_files`, read `<scratch>/<file>` — it contains standard conflict markers (`<<<<<<< HEAD` = the user's local version, `>>>>>>> <sha>` = the new release). Resolve **semantically**: understand what the user's change and the release change each intend and produce content that preserves both intents (when they are irreconcilable, prefer keeping the user's behavior and note it in the proposal). Edit the files in the scratch, then stage each one:
git -C "<scratch>" add -- "<file>"
Never edit files in the live checkout, and never run `git` in the live checkout during this step.
3. Propose — commit in the scratch and report to the owner
python3 skills/engine-conflict-resolve/scripts/propose.py --scratch "<scratch>"
- Exit 2 `{"status": "unmerged", …}` — you missed a file; go back to step 2.
- `{"status": "proposed", "merged_sha": …, "files": …, "diffstat": …,
"summary_lines": […], "proposal_record": …}` — the script has atomically recorded this exact `merged_sha` in `ENGINE_CONFLICT_PROPOSAL.json` next to the pending file; `apply.py` will land ONLY that recorded commit. If you change anything in the scratch afterwards, you MUST re-run propose.py (it overwrites the record) and re-report — the owner confirms a specific proposal, never "whatever is in the scratch now". Report the proposal. The proposal text must contain:
- the `summary_lines` (one line per conflicted file: what was kept), with
your own one-line semantic note per file where the mechanical verdict isn't self-explanatory;
- the `diffstat`;
- the `merged_sha` and the `scratch` path (the follow-up session needs both);
- a clear ask: reply **"apply"** to update the live engine, or "discard".
**Delivery needs BOTH of the following** — the task's `source` is `desktop-app`, and no bridge polls results for that source, so a result file alone is a dead letter the owner never sees: 1. Write the task's result file (`results/task-engine-conflict-<epoch>.txt`, same id as the task) — protocol hygiene: the dashboard, result-watcher, and timeout logic key off it. **State in the result which delivery channel step 2 actually used** (the room, or the fallback). 2. **Actively surface the proposal via the deterministic deliverer** — write the proposal text to a file, then:
python3 skills/engine-conflict-resolve/scripts/deliver.py \
--message-file "<proposal.txt>" \
--title "Engine update conflict — proposal ready"The destination is DECLARED, never guessed: `--room` > `$ENGINE_CONFLICT_NOTIFY_ROOM` > this skill's `manifest.json` `config` default (skills/MANIFEST.md precedence). The room must be an **owner-only** room; deliver.py never infers a "last active" room — a merge proposal is owner-only material and a guessed room may be shared. When a room resolves, the post goes through the `agent-room-ops` gateway module (`op:message`). **When no room is configured, or the post fails for any reason**, deliver.py always executes the Pending-decisions fallback: the proposal is asked
Read more
name: engine-conflict-resolve description: Ask Sutando to resolve a conflicted update of a Sutando checkout. When pulling the latest release/main conflicts with local changes, Sutando reproduces the merge in a scratch git worktree, resolves the conflicts there, proposes the resolution to the owner, and fast-forwards the live checkout only after explicit confirmation. First entry path is the desktop app's "Resolve with Sutando" engine-update button.
engine-conflict-resolve
Anyone running Sutando from a git checkout can ask it to pull the latest main/release — and when the update conflicts with their local changes, ask Sutando to resolve the merge itself. This skill is that protocol: reproduce the merge in a scratch worktree, resolve the conflicts there semantically, propose the result, and land it in the live checkout only after the owner explicitly confirms.
The first producer of the pending state is the desktop app's git-aware engine updater: on a conflicted update it aborts completely and records `ENGINE_UPDATE_PENDING.json`; the app's "Resolve with Sutando" button then writes a task file `tasks/task-engine-conflict-<epoch>.txt` (`source: desktop-app`, `interaction_type: system_event`) whose body names the pending file and the engine checkout. That task is one entry path, not the feature: the scripts take the pending-file shape as input from any producer (a ref-based entry point — merge any two refs with no pending file — plus a chat-facing command are queued follow-ups).
**Iron rule: never modify the live checkout until the owner confirms.** All merge work happens in a scratch worktree; only `apply.py` touches the live checkout, and only behind the updater's own lock and staleness guards.
Protocol
1. Prepare — reproduce the merge in a scratch worktree
Take `--pending` and `--engine` verbatim from the task body (both paths may contain spaces — always double-quote).
python3 skills/engine-conflict-resolve/scripts/prepare.py \ --pending "<…/ENGINE_UPDATE_PENDING.json>" \ --engine "<…/engine/sutando>"
- `{"status": "clean", "merged_sha": …}` — the merge replays cleanly (the
recorded conflict no longer reproduces). Skip step 2 but still RUN step 3: only `propose.py` records the proposal `apply.py` will accept. Report "merged cleanly, no conflicts remained".
- `{"status": "conflicts", "scratch": …, "conflicting_files": […]}` — resolve
in the scratch (step 2).
- `{"status": "error", "reason": "pending-stale", …}` — the checkout moved
since the conflict was recorded. Do NOT improvise: report to the owner that the state is stale and that relaunching the app (or re-running the updater) will re-detect. Stop.
- Re-running prepare is safe: an existing scratch for the same merge is reused.
2. Resolve the conflicts IN THE SCRATCH
For each file in `conflicting_files`, read `<scratch>/<file>` — it contains standard conflict markers (`<<<<<<< HEAD` = the user's local version, `>>>>>>> <sha>` = the new release). Resolve **semantically**: understand what the user's change and the release change each intend and produce content that preserves both intents (when they are irreconcilable, prefer keeping the user's behavior and note it in the proposal). Edit the files in the scratch, then stage each one:
git -C "<scratch>" add -- "<file>"
Never edit files in the live checkout, and never run `git` in the live checkout during this step.
3. Propose — commit in the scratch and report to the owner
python3 skills/engine-conflict-resolve/scripts/propose.py --scratch "<scratch>"
- Exit 2 `{"status": "unmerged", …}` — you missed a file; go back to step 2.
- `{"status": "proposed", "merged_sha": …, "files": …, "diffstat": …,
"summary_lines": […], "proposal_record": …}` — the script has atomically recorded this exact `merged_sha` in `ENGINE_CONFLICT_PROPOSAL.json` next to the pending file; `apply.py` will land ONLY that recorded commit. If you change anything in the scratch afterwards, you MUST re-run propose.py (it overwrites the record) and re-report — the owner confirms a specific proposal, never "whatever is in the scratch now". Report the proposal. The proposal text must contain:
- the `summary_lines` (one line per conflicted file: what was kept), with
your own one-line semantic note per file where the mechanical verdict isn't self-explanatory;
- the `diffstat`;
- the `merged_sha` and the `scratch` path (the follow-up session needs both);
- a clear ask: reply **"apply"** to update the live engine, or "discard".
**Delivery needs BOTH of the following** — the task's `source` is `desktop-app`, and no bridge polls results for that source, so a result file alone is a dead letter the owner never sees: 1. Write the task's result file (`results/task-engine-conflict-<epoch>.txt`, same id as the task) — protocol hygiene: the dashboard, result-watcher, and timeout logic key off it. **State in the result which delivery channel step 2 actually used** (the room, or the fallback). 2. **Actively surface the proposal via the deterministic deliverer** — write the proposal text to a file, then:
python3 skills/engine-conflict-resolve/scripts/deliver.py \
--message-file "<proposal.txt>" \
--title "Engine update conflict — proposal ready"The destination is DECLARED, never guessed: `--room` > `$ENGINE_CONFLICT_NOTIFY_ROOM` > this skill's `manifest.json` `config` default (skills/MANIFEST.md precedence). The room must be an **owner-only** room; deliver.py never infers a "last active" room — a merge proposal is owner-only material and a guessed room may be shared. When a room resolves, the post goes through the `agent-room-ops` gateway module (`op:message`). **When no room is configured, or the post fails for any reason**, deliver.py always executes the Pending-decisions fallback: the proposal is asked
My AI Stand — Realtime by Day, Rewriting Itself by Night. Summon my AI superpower. Voice, vision, screen, meetings, calls when I'm engaged. Learns my patterns, ships its own code when I'm not. Runs across my Macs, interacts with people & their Stands.
Other skills on sutando.
More skills from other plugins.
browser-use
Direct browser control via CDP for web interaction: automation, scraping, testing, screenshots, and site/app work. Also covers…
agent-browser
Browser automation CLI for AI agents. Use when the user needs to interact with websites, including navigating pages, filling…
Questions about engine-conflict-resolve.
- Which coding agents can use engine-conflict-resolve?
- engine-conflict-resolve installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.

