~bigbes/confluence-md-utilities

4692639972ac74f39389c2d9767aa0bd2072bdf6 — Eugene Blikh 2 months ago 57679c6
Move CLI to cmd/mdcx/, add justfile and CLAUDE.md

Restructure to standard Go project layout: merge package cmd
into package main under cmd/mdcx/. Fix .gitignore to use /mdcx
so it only ignores the binary at repo root. Update install path
in README.md.

false
M .gitignore => .gitignore +1 -1
@@ 1,5 1,5 @@
# Binary
mdcx
/mdcx

# Example/test Confluence documents (not part of the project)
0-root.xml

A CLAUDE.md => CLAUDE.md +52 -0
@@ 0,0 1,52 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Build & Test

```bash
just build          # build binary to ./mdcx
just install        # go install to $GOPATH/bin
just test           # go test ./...
just test-v         # verbose tests
go test ./converter/... -run TestRoundTrip  # run specific tests
```

## Architecture

**mdcx** converts Markdown ↔ Confluence storage-format XML with round-trip fidelity. The binary lives at `cmd/mdcx/`.

### Package graph

```
cmd/mdcx/      CLI (cobra commands, package main)
  ├─ converter  Core bidirectional conversion
  ├─ api        Confluence REST client (pull/push)
  ├─ template   Marker-based embed/extract
  └─ format     XML pretty-printer + ANSI colorizer

converter/
  ├─ md2xml.go  goldmark parser → confluence.Renderer → XML
  └─ xml2md.go  golang.org/x/net/html walker → Markdown

confluence/
  ├─ renderer.go  goldmark NodeRenderer (registered at priority 100)
  └─ elements.go  Macro builders: CodeMacro, InfoPanel, etc.
```

### Key design decisions

**Bidirectional round-trip preservation** — Confluence elements with no Markdown equivalent (inline comments, user mentions, attachment images, layout sections) survive round-trips via HTML `<span data-*>` attributes and `<!-- comment -->` markers. The md→xml renderer reconstructs proper `ac:*/ri:*` tags from these.

**CDATA preprocessing** — `xml2md.go` replaces `<![CDATA[...]]>` with `<cdatacontent>` fake elements before parsing, because `golang.org/x/net/html` doesn't handle CDATA. The reverse direction uses `confluence.escapeCDATA()` to split `]]>` sequences.

**Renderer state** — `confluence.Renderer` tracks `taskIDCounter`, `inTaskBody`, `inlineCommentDepth`, and pending attributes from HTML comments. This state is per-conversion and must remain consistent across the AST walk.

**Template markers** — `<!-- MD_CONTENT_START -->` / `<!-- MD_CONTENT_END -->` delimit the editable region in Confluence pages. The `push --template` flow: fetch page → replace between markers → update with version increment.

### Semantic mapping (non-obvious)

- Markdown blockquotes → Confluence info panel macro (not `<blockquote>`)
- Task lists detected by checkbox presence, rendered as `<ac:task-list>`
- Code blocks → `<ac:structured-macro ac:name="code">` with CDATA body
- XML namespaces (`ac:`, `ri:`) are string-constructed, not from an XML library

M README.md => README.md +6 -2
@@ 9,10 9,14 @@ Template-aware embedding preserves metadata tables, changelogs, inline comment m
## Install

```bash
go install sourcecraft.dev/bigbes/confluence-md-utilities@latest
go install sourcecraft.dev/bigbes/confluence-md-utilities/cmd/mdcx@latest
```

Binary name is `mdcx`.
Or build locally with [just](https://github.com/casey/just):

```bash
just install
```

## Commands


R cmd/completions.go => cmd/mdcx/completions.go +1 -1
@@ 1,4 1,4 @@
package cmd
package main

import (
	"github.com/spf13/cobra"

R cmd/convert.go => cmd/mdcx/convert.go +1 -1
@@ 1,4 1,4 @@
package cmd
package main

import (
	"fmt"

R cmd/embed.go => cmd/mdcx/embed.go +1 -1
@@ 1,4 1,4 @@
package cmd
package main

import (
	"fmt"

R cmd/extract.go => cmd/mdcx/extract.go +1 -1
@@ 1,4 1,4 @@
package cmd
package main

import (
	"fmt"

R cmd/fmt.go => cmd/mdcx/fmt.go +1 -1
@@ 1,4 1,4 @@
package cmd
package main

import (
	"fmt"

R main.go => cmd/mdcx/main.go +1 -3
@@ 1,7 1,5 @@
package main

import "sourcecraft.dev/bigbes/confluence-md-utilities/cmd"

func main() {
	cmd.Execute()
	Execute()
}

R cmd/pull.go => cmd/mdcx/pull.go +1 -1
@@ 1,4 1,4 @@
package cmd
package main

import (
	"fmt"

R cmd/push.go => cmd/mdcx/push.go +1 -1
@@ 1,4 1,4 @@
package cmd
package main

import (
	"fmt"

R cmd/root.go => cmd/mdcx/root.go +1 -1
@@ 1,4 1,4 @@
package cmd
package main

import (
	"fmt"

A justfile => justfile +17 -0
@@ 0,0 1,17 @@
module_path := "sourcecraft.dev/bigbes/confluence-md-utilities"

# Install mdcx binary locally
install:
    go install {{module_path}}/cmd/mdcx@latest

# Build mdcx binary
build:
    go build -o mdcx ./cmd/mdcx

# Run tests
test:
    go test ./...

# Run tests with verbose output
test-v:
    go test -v ./...