As a user, I wanted to…
…write a Confluence page in markdown that includes screenshots, and have cfl "just work" the way every other markdown tool does — point at a local image file in the markdown source, run cfl page edit, and end up with the screenshot inlined on the page.
What I tried
I was updating an existing page with cfl page edit <page-id> --file content.md. The markdown referenced two screenshots I'd already uploaded to the same page as attachments, using standard markdown image syntax:
Selecting CheckSync opens the pre-enrollment splash:

After enrollment, the same entry opens the dashboard view:

I had already uploaded both PNGs to the page via cfl attachment upload --page <id> --file … immediately before, so the attachment names matched exactly.
What happened
The page updated successfully (no error, exit 0), but the two  lines were silently dropped from the rendered page. What survived was only the alt text, promoted to a standalone paragraph:
Selecting CheckSync opens the pre-enrollment splash:
Q2 UUX — CheckSync pre-enrollment splash
After enrollment, the same entry opens the dashboard view:
Q2 UUX — CheckSync post-enrollment Positive Pay Automation view
No warning, no log line, no non-zero exit — the failure mode is a confidently-broken page with caption-shaped paragraphs where the images should be. I only noticed because I went back to look at the rendered page.
What I had to do instead
To get the images to actually appear, I had to:
- Re-fetch the page in storage format:
cfl page view <id> --raw --content-only > page.xhtml
- Hand-patch in the Confluence storage macros where the images should go:
<ac:image ac:align="center" ac:layout="center">
<ri:attachment ri:filename="q2-checksync-uux-pre-enrollment.png" />
</ac:image>
- Re-upload with
cfl page edit <id> --file page.xhtml --storage
That worked, but it's a workflow I had to discover by inspecting raw XHTML — not something a typical user is going to find on their own.
What I'd expect / would love
In rough order of value:
- At minimum, fail loud. If the markdown→ADF converter encounters an image reference it can't resolve into a real image macro, it should error out (or at least warn to stderr) instead of silently dropping the node and keeping the alt text. The current behavior produces a page that looks intentional but is wrong, and there's no signal that anything went sideways.
- Bare filename →
<ri:attachment>. When a markdown image reference is a bare filename (no scheme, no path) and an attachment by that name already exists on the target page, translate it to <ac:image><ri:attachment ri:filename="…"/></ac:image>. This handles the "I uploaded separately, now I just want to reference it from markdown" case.
- Local path → auto-upload + reference. When a markdown image reference is a local file path (relative to the markdown source file), upload it as an attachment to the target page and emit the same
<ac:image><ri:attachment/></ac:image> macro. Idempotent on re-edit (skip upload if same filename + hash already attached). This is the "just works" path that matches how every other markdown editor handles local images.
- Leave
http(s):// URLs alone. External image URLs should pass through as <ac:image><ri:url ri:value="…"/></ac:image> (or whatever the current ADF equivalent is).
- Caption support (nice-to-have). Markdown's
 could render as image macro + a following italic caption paragraph, mirroring what I ended up hand-writing.
Tradeoffs worth thinking through:
- Path resolution: relative to the markdown file (what every other tool does) vs. relative to cwd. File-relative is the safer default.
- Re-edit idempotency: don't double-upload on every
cfl page edit pass; key on filename + hash or filename + size.
- Sizing: optional, but
file.png?width=800 style query-string suffix maps cleanly onto ac:width/ac:height if you want to support it.
Even just shipping (1) would have saved me the round-trip — silent-drop is the actual sharp edge.
Versions
cfl (whatever ships at HEAD as of June 2026)
- Target: Confluence Cloud, page is cloud-editor (ADF) format
- Editing was via
cfl page edit --file content.md (no --legacy, no --no-markdown, no --storage)
As a user, I wanted to…
…write a Confluence page in markdown that includes screenshots, and have
cfl"just work" the way every other markdown tool does — point at a local image file in the markdown source, runcfl page edit, and end up with the screenshot inlined on the page.What I tried
I was updating an existing page with
cfl page edit <page-id> --file content.md. The markdown referenced two screenshots I'd already uploaded to the same page as attachments, using standard markdown image syntax:I had already uploaded both PNGs to the page via
cfl attachment upload --page <id> --file …immediately before, so the attachment names matched exactly.What happened
The page updated successfully (no error, exit 0), but the two
lines were silently dropped from the rendered page. What survived was only the alt text, promoted to a standalone paragraph:No warning, no log line, no non-zero exit — the failure mode is a confidently-broken page with caption-shaped paragraphs where the images should be. I only noticed because I went back to look at the rendered page.
What I had to do instead
To get the images to actually appear, I had to:
cfl page view <id> --raw --content-only > page.xhtmlcfl page edit <id> --file page.xhtml --storageThat worked, but it's a workflow I had to discover by inspecting raw XHTML — not something a typical user is going to find on their own.
What I'd expect / would love
In rough order of value:
<ri:attachment>. When a markdown image reference is a bare filename (no scheme, no path) and an attachment by that name already exists on the target page, translate it to<ac:image><ri:attachment ri:filename="…"/></ac:image>. This handles the "I uploaded separately, now I just want to reference it from markdown" case.<ac:image><ri:attachment/></ac:image>macro. Idempotent on re-edit (skip upload if same filename + hash already attached). This is the "just works" path that matches how every other markdown editor handles local images.http(s)://URLs alone. External image URLs should pass through as<ac:image><ri:url ri:value="…"/></ac:image>(or whatever the current ADF equivalent is).could render as image macro + a following italic caption paragraph, mirroring what I ended up hand-writing.Tradeoffs worth thinking through:
cfl page editpass; key on filename + hash or filename + size.file.png?width=800style query-string suffix maps cleanly ontoac:width/ac:heightif you want to support it.Even just shipping (1) would have saved me the round-trip — silent-drop is the actual sharp edge.
Versions
cfl(whatever ships at HEAD as of June 2026)cfl page edit --file content.md(no--legacy, no--no-markdown, no--storage)