From 4692639972ac74f39389c2d9767aa0bd2072bdf6 Mon Sep 17 00:00:00 2001 From: Eugene Blikh Date: Thu, 26 Mar 2026 08:56:18 +0300 Subject: [PATCH] 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 --- .gitignore | 2 +- CLAUDE.md | 52 +++++++++++++++++++++++++++++++++++ README.md | 8 ++++-- cmd/{ => mdcx}/completions.go | 2 +- cmd/{ => mdcx}/convert.go | 2 +- cmd/{ => mdcx}/embed.go | 2 +- cmd/{ => mdcx}/extract.go | 2 +- cmd/{ => mdcx}/fmt.go | 2 +- cmd/mdcx/main.go | 5 ++++ cmd/{ => mdcx}/pull.go | 2 +- cmd/{ => mdcx}/push.go | 2 +- cmd/{ => mdcx}/root.go | 2 +- justfile | 17 ++++++++++++ main.go | 7 ----- 14 files changed, 89 insertions(+), 18 deletions(-) create mode 100644 CLAUDE.md rename cmd/{ => mdcx}/completions.go (99%) rename cmd/{ => mdcx}/convert.go (98%) rename cmd/{ => mdcx}/embed.go (99%) rename cmd/{ => mdcx}/extract.go (99%) rename cmd/{ => mdcx}/fmt.go (99%) create mode 100644 cmd/mdcx/main.go rename cmd/{ => mdcx}/pull.go (99%) rename cmd/{ => mdcx}/push.go (99%) rename cmd/{ => mdcx}/root.go (98%) create mode 100644 justfile delete mode 100644 main.go diff --git a/.gitignore b/.gitignore index c594bb7e6bb2f4a1bbf8b4675e10d3b8d03f029d..4ad9582aca8ed4c02c8f0bcf242bc9ecd7ecb381 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ # Binary -mdcx +/mdcx # Example/test Confluence documents (not part of the project) 0-root.xml diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000000000000000000000000000000000..265cdbd3586ffe3fe8f8bc49acbb6920cfdecf0a --- /dev/null +++ b/CLAUDE.md @@ -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 `` attributes and `` markers. The md→xml renderer reconstructs proper `ac:*/ri:*` tags from these. + +**CDATA preprocessing** — `xml2md.go` replaces `` with `` 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** — `` / `` 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 `
`) +- Task lists detected by checkbox presence, rendered as `` +- Code blocks → `` with CDATA body +- XML namespaces (`ac:`, `ri:`) are string-constructed, not from an XML library diff --git a/README.md b/README.md index aea555e8e3389ca7f8455151a454355e01324e5a..cd8f4a98d3f9478c547e0b5aaa8c0380fa7178ac 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/cmd/completions.go b/cmd/mdcx/completions.go similarity index 99% rename from cmd/completions.go rename to cmd/mdcx/completions.go index be3aef73db8fed0d6c2736fa393c7df25b29aa15..f8022dc5bfbe0835a1067f765b444a1d2f7313d6 100644 --- a/cmd/completions.go +++ b/cmd/mdcx/completions.go @@ -1,4 +1,4 @@ -package cmd +package main import ( "github.com/spf13/cobra" diff --git a/cmd/convert.go b/cmd/mdcx/convert.go similarity index 98% rename from cmd/convert.go rename to cmd/mdcx/convert.go index 6f32d2dd31749fbd588b332fc15f75db550fd0cf..d63f9413bdbc949e45cae9c73ab7de9cf453ff32 100644 --- a/cmd/convert.go +++ b/cmd/mdcx/convert.go @@ -1,4 +1,4 @@ -package cmd +package main import ( "fmt" diff --git a/cmd/embed.go b/cmd/mdcx/embed.go similarity index 99% rename from cmd/embed.go rename to cmd/mdcx/embed.go index df986c0ee74045bf7f2a85f344ffe21f257fd3ef..9454a5b510cded1c01fcc98fbc07aee4a983057f 100644 --- a/cmd/embed.go +++ b/cmd/mdcx/embed.go @@ -1,4 +1,4 @@ -package cmd +package main import ( "fmt" diff --git a/cmd/extract.go b/cmd/mdcx/extract.go similarity index 99% rename from cmd/extract.go rename to cmd/mdcx/extract.go index 4cd45b9c46b3a2b6c537309c7acb656c9463fa57..5d4951c0f1bce84ffa4818f28ba0b7bef15af308 100644 --- a/cmd/extract.go +++ b/cmd/mdcx/extract.go @@ -1,4 +1,4 @@ -package cmd +package main import ( "fmt" diff --git a/cmd/fmt.go b/cmd/mdcx/fmt.go similarity index 99% rename from cmd/fmt.go rename to cmd/mdcx/fmt.go index 8352bdbec52e3eef5e9a81ae0159e924c2047481..7bf79aef3065c3c849723a67814dccfdf0f1886c 100644 --- a/cmd/fmt.go +++ b/cmd/mdcx/fmt.go @@ -1,4 +1,4 @@ -package cmd +package main import ( "fmt" diff --git a/cmd/mdcx/main.go b/cmd/mdcx/main.go new file mode 100644 index 0000000000000000000000000000000000000000..736ef31021492f58129f0a03bcd61e044b5165f9 --- /dev/null +++ b/cmd/mdcx/main.go @@ -0,0 +1,5 @@ +package main + +func main() { + Execute() +} diff --git a/cmd/pull.go b/cmd/mdcx/pull.go similarity index 99% rename from cmd/pull.go rename to cmd/mdcx/pull.go index dcb8206fe2e57806567a2c524cca9970c98ccdac..f4fa260d8db0b3e56c3f9b55c2834852d4bbcc6b 100644 --- a/cmd/pull.go +++ b/cmd/mdcx/pull.go @@ -1,4 +1,4 @@ -package cmd +package main import ( "fmt" diff --git a/cmd/push.go b/cmd/mdcx/push.go similarity index 99% rename from cmd/push.go rename to cmd/mdcx/push.go index 099438c97e50f636e0bf5a3a3d62013a649b4853..60a2034c0c608285539f2aecec3a2e36c03a58b7 100644 --- a/cmd/push.go +++ b/cmd/mdcx/push.go @@ -1,4 +1,4 @@ -package cmd +package main import ( "fmt" diff --git a/cmd/root.go b/cmd/mdcx/root.go similarity index 98% rename from cmd/root.go rename to cmd/mdcx/root.go index 90b6cafbd374cc8ce942d6ff82ec5b00850ad761..d3ee39f096f8eba2bdd96797a623023f2fb79af9 100644 --- a/cmd/root.go +++ b/cmd/mdcx/root.go @@ -1,4 +1,4 @@ -package cmd +package main import ( "fmt" diff --git a/justfile b/justfile new file mode 100644 index 0000000000000000000000000000000000000000..2cee0e699e89be4c57c4c5464a374ddcbb06bd5a --- /dev/null +++ b/justfile @@ -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 ./... diff --git a/main.go b/main.go deleted file mode 100644 index 64e6929b3679341fb6c655f276205d06dcdb48c2..0000000000000000000000000000000000000000 --- a/main.go +++ /dev/null @@ -1,7 +0,0 @@ -package main - -import "sourcecraft.dev/bigbes/confluence-md-utilities/cmd" - -func main() { - cmd.Execute() -}