Menu

diff

n8n-decanter diff [workflow…]

Shows the actual changed lines between your local Code-node source and the workflow’s draft on n8n: --- remote (n8n) / +++ local (<file>) and @@ hunks, per node — exactly what a push would overwrite, and exactly what a pull would bring in. It reads the instance and writes nothing.

The git status / git diff split

diff is the git diff half of code sync: the lines. The git status half — is anything pending, is anything drifted, is the live version behind — is preflight, which turns the same facts into a scored verdict. Both read one shared fact computation, so they can never disagree about which nodes moved.

n8n-decanter preflight order-sync   # the summary + the verdict (the gate)
n8n-decanter diff      order-sync   # the lines behind that summary

It always exits 0

diff is an inspection view, not a gate — like git diff, its exit code says nothing about whether you should ship. It exits 0 whether every node matches or every node conflicts. (A genuine failure — an unreachable instance, an unavailable workflow — still exits 1, because the command didn’t run.)

This drops the CI exit codes the retired status verb had. A pipeline that gated on status migrates to preflight, which is the verb that grades:

n8n-decanter preflight --json                # verdict + exit 1 on not-ready
n8n-decanter preflight --fail-on=warn        # also exit 1 on a caution

Refs, multi-workflow, and the picker

diff takes refs like pull/push: several at once, each rendered under its own header, or none — in which case a terminal opens the picker and a piped run falls back to the "workflows" list in your config.

Each workflow’s block starts with <name> (<id>) [<dir>]. A ref that isn’t pulled yet is reported as such and skipped, not treated as an error.

What it prints — and what it omits

In-sync nodes are omitted entirely. Only nodes that differ get a line, so a clean workflow prints one line:

Order Sync (wf123)  [workflows/order-sync]
  no differences — every tracked node matches the draft

Everything else gets a state line, then the diff:

State lineMeans
local changes in code/<file> — push pendingyou edited locally; the draft is still at the last sync
changed remotely — pullthe draft moved; your file is still at the last sync
CONFLICT — changed both locally and remotelyboth sides moved off the last sync
local file code/<file> missingthe file .decanter.json tracks is gone from disk
remote code node unknown locally — pulla Code node exists on n8n with no local state entry
code/<file>: node <id> deleted remotelythe node this file belongs to is gone from the workflow

The last three have only one side to show, so they print the state line without a hunk. Everything else prints the unified diff underneath.

.ts nodes are compiled before comparing

For a .ts node the local side of the diff is the compiled JavaScript — the exact bytes push would send, shared/*.ts imports bundled in. So editing one shared helper shows up as a diff in every node that imports it, which is the honest answer to “what does this helper change touch?”. See TypeScript nodes. Compile warnings for a node are replayed immediately above that node’s diff.

What diff deliberately does not tell you

Three facts the retired status verb printed are not here — they aren’t line diffs, and each survives as a preflight check:

FactNow
published / unpublished, and whether the live version lags the draftthe lifecycle check
workflow.json structure snapshot out of datethe snapshot check
the roll-call of nodes that are in syncthe parity check