From f1b44efca9c3ae12c97405c83c2afd69b8c99799 Mon Sep 17 00:00:00 2001 From: Oliver Jakoubek Date: Thu, 5 Mar 2026 10:19:30 +0100 Subject: [PATCH 1/8] docs: update CHANGELOG.md for v0.1.0 --- CHANGELOG.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..3a8c5d3 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,25 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [v0.1.0] - 2026-03-05 + +### Added + +- Initial release: CLI tool to convert one or more CSV files into a single Excel (.xlsx) workbook +- Each CSV file becomes a separate worksheet named after the source filename +- Auto-detection of CSV delimiter (`,`, `;`, `\t`) based on first-line analysis +- Support for UTF-8 and Windows-1252 encoding (`-enc` flag) +- Configurable output file path (`-o` flag) +- Tolerant CSV parsing with `LazyQuotes` for malformed files +- Build system via [Mage](https://magefile.org/) with targets for Linux, Windows, and install +- Version info injected at build time via git tags (`ldflags`) + +### Documentation + +- README with installation instructions, flag reference, and usage examples From 37ad9502412f874d3e2f5935b70a9f0869f960ac Mon Sep 17 00:00:00 2001 From: Oliver Jakoubek Date: Thu, 5 Mar 2026 10:56:18 +0100 Subject: [PATCH 2/8] fix: parse -o flag correctly to resolve output filename Fix bug where -o flag value was read as pointer instead of string value, causing memory addresses to be printed and output path to resolve to ".xlsx". --- AGENTS.md | 73 +++++++++++++++++++++++++++++++++++ main.go | 22 ++++++----- main_test.go | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 191 insertions(+), 10 deletions(-) create mode 100644 AGENTS.md create mode 100644 main_test.go diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..bea7dbe --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,73 @@ +# Agent Instructions + +## Plans + +- Make the plan extremely concise. Sacrifice grammar for the sake of concision. +- At the end of each plan, give me a list of unresolved questions to answer, if any. + +## Issue Tracking + +This project uses **br** (beads_rust) for issue tracking and **bv** (beads_viewer) for triage/prioritization. Issues are stored in `.beads/` and tracked in git. + +### Tool Roles + +| Tool | Role | Key Commands | +|------|------|--------------| +| `br` | Issue management (CRUD) | `br ready`, `br show`, `br update`, `br close`, `br sync` | +| `bv` | Triage & prioritization | `bv --robot-triage` | + +### Quick Reference + +```bash +br ready # Find available work +br show # View issue details +br update --status in_progress # Claim work +br close # Complete work +br sync # Sync with git +``` + +### What to work on next? + +```bash +bv --robot-triage # AI-powered recommendation for next issue (preferred) +br ready # Fallback: plain list of unblocked issues +``` + +> **Warning:** bare `bv` (without flags) launches an interactive TUI that blocks the session. Always use `bv --robot-triage` or other non-interactive flags in automated contexts. + +### Key Concepts + +- **Dependencies**: Issues can block other issues. `br ready` shows only unblocked work. +- **Priority**: P0=critical, P1=high, P2=medium, P3=low, P4=backlog (use numbers 0-4, not words) +- **Types**: task, bug, feature, epic, chore, docs, question +- **Blocking**: `br dep add ` to add dependencies + + +### Creating issues + +You might be tasked with creating a new issue or you discover new tasks by yourself. Use the `/create-single-issue` command accordingly the create a new issue. + +## Main Workflow + +### Work on issue + +You are tasked by the operator to work on an issue. +It's either a specific issue (`/start-issue `) or the *next* issue (`/start-next-issue`). +The command tells you to open the issue, enter plan mode and then implement the plan. +The command tells you explicitly *NOT* to: close the issue, commit or push anything (because this is subject to `/finish-issue`). + +### Operator tests manually + +After finishing the implementation the operator tests the solution. + +### Finish issue + +After testing you are tasked by the operator to finish the issue (`/finish-issue`). +You close the issue and create a commit. + +## Release Workflow + +Once in a while the operator uses these commands: + +- `/tag-version` - you create a new version using the `git tag` mechanism +- `/update-changelog` - you update the CHANGELOG.md according to the changes of the last version diff --git a/main.go b/main.go index 7e98611..8fd8b0a 100644 --- a/main.go +++ b/main.go @@ -26,10 +26,13 @@ func main() { os.Exit(1) } - fmt.Println(sep) - fmt.Println(enc) - fmt.Println(out) + if err := run(files, *out, *sep, *enc); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} +func run(files []string, out, sep, enc string) error { xlsx := excelize.NewFile() firstSheet := true @@ -49,11 +52,11 @@ func main() { defer f.Close() var reader io.Reader = f - if *enc == "windows1252" { + if enc == "windows1252" { reader = transform.NewReader(f, charmap.Windows1252.NewDecoder()) } - delimiter := detectDelimiter(path, *sep) + delimiter := detectDelimiter(path, sep) csvReader := csv.NewReader(reader) csvReader.Comma = delimiter @@ -85,12 +88,11 @@ func main() { fmt.Printf("✓ %s → Reiter \"%s\" (%d Zeilen)\n", path, sheetName, row-1) } - if err := xlsx.SaveAs(*out); err != nil { - fmt.Fprintf(os.Stderr, "Fehler beim Speichern: %v\n", err) - os.Exit(1) + if err := xlsx.SaveAs(out); err != nil { + return fmt.Errorf("Fehler beim Speichern: %w", err) } - fmt.Printf("✅ Gespeichert: %s\n", *out) - + fmt.Printf("✅ Gespeichert: %s\n", out) + return nil } func detectDelimiter(path, hint string) rune { diff --git a/main_test.go b/main_test.go new file mode 100644 index 0000000..b95978e --- /dev/null +++ b/main_test.go @@ -0,0 +1,106 @@ +package main + +import ( + "os" + "path/filepath" + "testing" + + "github.com/xuri/excelize/v2" +) + +func writeTempCSV(t *testing.T, dir, name, content string) string { + t.Helper() + path := filepath.Join(dir, name) + if err := os.WriteFile(path, []byte(content), 0644); err != nil { + t.Fatalf("WriteFile: %v", err) + } + return path +} + +func TestRun_OutputPath(t *testing.T) { + dir := t.TempDir() + csv := writeTempCSV(t, dir, "data.csv", "name,age\nAlice,30\nBob,25\n") + out := filepath.Join(dir, "result.xlsx") + + if err := run([]string{csv}, out, "auto", "utf8"); err != nil { + t.Fatalf("run() error: %v", err) + } + + if _, err := os.Stat(out); os.IsNotExist(err) { + t.Fatalf("output file not created: %s", out) + } +} + +func TestRun_SheetContent(t *testing.T) { + dir := t.TempDir() + csv := writeTempCSV(t, dir, "sheet1.csv", "col1;col2\nfoo;bar\n") + out := filepath.Join(dir, "out.xlsx") + + if err := run([]string{csv}, out, ";", "utf8"); err != nil { + t.Fatalf("run() error: %v", err) + } + + f, err := excelize.OpenFile(out) + if err != nil { + t.Fatalf("OpenFile: %v", err) + } + + val, err := f.GetCellValue("sheet1", "A1") + if err != nil { + t.Fatalf("GetCellValue: %v", err) + } + if val != "col1" { + t.Errorf("A1 = %q, want %q", val, "col1") + } + + val, err = f.GetCellValue("sheet1", "B2") + if err != nil { + t.Fatalf("GetCellValue: %v", err) + } + if val != "bar" { + t.Errorf("B2 = %q, want %q", val, "bar") + } +} + +func TestRun_MultipleSheets(t *testing.T) { + dir := t.TempDir() + csv1 := writeTempCSV(t, dir, "alpha.csv", "x\n1\n") + csv2 := writeTempCSV(t, dir, "beta.csv", "y\n2\n") + out := filepath.Join(dir, "multi.xlsx") + + if err := run([]string{csv1, csv2}, out, "auto", "utf8"); err != nil { + t.Fatalf("run() error: %v", err) + } + + f, err := excelize.OpenFile(out) + if err != nil { + t.Fatalf("OpenFile: %v", err) + } + + sheets := f.GetSheetList() + if len(sheets) != 2 { + t.Fatalf("got %d sheets, want 2", len(sheets)) + } + if sheets[0] != "alpha" || sheets[1] != "beta" { + t.Errorf("sheets = %v, want [alpha beta]", sheets) + } +} + +func TestRun_CustomOutputFilename(t *testing.T) { + dir := t.TempDir() + csv := writeTempCSV(t, dir, "input.csv", "a,b\n1,2\n") + out := filepath.Join(dir, "custom_name.xlsx") + + if err := run([]string{csv}, out, ",", "utf8"); err != nil { + t.Fatalf("run() error: %v", err) + } + + if _, err := os.Stat(out); os.IsNotExist(err) { + t.Fatalf("expected output at %s, not found", out) + } + // ensure default name was NOT created + defaultOut := filepath.Join(dir, "output.xlsx") + if _, err := os.Stat(defaultOut); !os.IsNotExist(err) { + t.Errorf("unexpected file created at %s", defaultOut) + } +} From dfb721117fe64071c8de04ca7dded64e6bf6b96c Mon Sep 17 00:00:00 2001 From: Oliver Jakoubek Date: Thu, 5 Mar 2026 10:57:23 +0100 Subject: [PATCH 3/8] Added IDE an beads issues --- .beads/.gitignore | 11 +++++++++++ .beads/config.yaml | 1 + .beads/issues.jsonl | 2 ++ .beads/metadata.json | 4 ++++ .idea/.gitignore | 10 ++++++++++ .idea/csv2excel.iml | 9 +++++++++ .idea/go.imports.xml | 11 +++++++++++ .idea/modules.xml | 8 ++++++++ .idea/vcs.xml | 6 ++++++ 9 files changed, 62 insertions(+) create mode 100644 .beads/.gitignore create mode 100644 .beads/config.yaml create mode 100644 .beads/issues.jsonl create mode 100644 .beads/metadata.json create mode 100644 .idea/.gitignore create mode 100644 .idea/csv2excel.iml create mode 100644 .idea/go.imports.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml diff --git a/.beads/.gitignore b/.beads/.gitignore new file mode 100644 index 0000000..f32e807 --- /dev/null +++ b/.beads/.gitignore @@ -0,0 +1,11 @@ +# Database +*.db +*.db-shm +*.db-wal + +# Lock files +*.lock + +# Temporary +last-touched +*.tmp diff --git a/.beads/config.yaml b/.beads/config.yaml new file mode 100644 index 0000000..c3abc97 --- /dev/null +++ b/.beads/config.yaml @@ -0,0 +1 @@ +issue_prefix: csv diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl new file mode 100644 index 0000000..952da85 --- /dev/null +++ b/.beads/issues.jsonl @@ -0,0 +1,2 @@ +{"id":"bd-3rr","title":"Fix -o flag: output filename not parsed correctly","description":"## Bug Report\n\nUsing the `-o` flag to specify an output filename causes errors. Instead of using the provided filename, the tool attempts to open/save `.xlsx` (empty basename) and prints memory addresses.\n\n## Reproduction\n\n```\ncsv2excel.exe -enc=windows1252 -o=test.xlsx .\\WDK-SiWo-20260103.csv\n```\n\n## Actual Output\n\n```\n0x3eb5d9d181b0\n0x3eb5d9d181c0\n0x3eb5d9d181d0\nFehler beim Öffnen .xlsx: open .xlsx: The system cannot find the file specified.\n✓ .\\WDK-SiWo-20260103.csv → Reiter \"WDK-SiWo-20260103\" (5324 Zeilen)\nFehler beim Speichern: unsupported workbook file format\n```\n\n## Analysis\n\n- Memory addresses being printed suggest a pointer/value is being passed where a string is expected (e.g. `fmt.Println(&flag)` instead of `fmt.Println(*flag)`)\n- The output path resolves to `.xlsx` instead of `test.xlsx`, meaning the flag value is not read correctly\n- The `unsupported workbook file format` error is a consequence of trying to open a non-existent file as a workbook\n\n## Acceptance Criteria\n- [ ] `csv2excel -o=test.xlsx input.csv` writes output to `test.xlsx`\n- [ ] No memory addresses are printed to stdout/stderr\n- [ ] Error message is shown if the output path is invalid/unwritable\n- [ ] Tests written and passing","status":"closed","priority":1,"issue_type":"bug","created_at":"2026-03-05T09:37:26.921804683Z","created_by":"oli","updated_at":"2026-03-05T09:56:12.843074108Z","closed_at":"2026-03-05T09:56:12.842163406Z","close_reason":"done","source_repo":".","compaction_level":0,"original_size":0} +{"id":"bd-bxt","title":"Add --version flag to CLI","description":"## Description\n\nAdd a `--version` flag to the `csv2excel` CLI that outputs version information including the version tag and build timestamp.\n\n## Expected Output\n\n```\ncsv2excel version v0.10.0-1-g3eb502a built 2026-02-19T14:30:13Z\n```\n\nThe version string should be injected at build time using Go linker flags (`-ldflags`), so that `git describe` and a build timestamp are embedded into the binary.\n\n## Implementation Notes\n\n- Define `version` and `buildDate` variables in `main.go` (or a dedicated `version.go`)\n- Inject values via `-ldflags \"-X main.version=... -X main.buildDate=...\"` during `mage Build`\n- Handle the `--version` / `-v` flag and print the version line, then exit 0\n\n## Acceptance Criteria\n- [ ] `csv2excel --version` prints a line matching `csv2excel version built `\n- [ ] Version and build date are injected at build time via ldflags in the Mage build targets\n- [ ] Running `csv2excel --version` exits with code 0\n- [ ] Default (unset) values produce a sensible fallback (e.g. `dev` / `unknown`)\n- [ ] Tests written and passing (if applicable)","status":"open","priority":2,"issue_type":"feature","created_at":"2026-03-05T09:22:40.538253362Z","created_by":"oli","updated_at":"2026-03-05T09:22:40.538253362Z","source_repo":".","compaction_level":0,"original_size":0} diff --git a/.beads/metadata.json b/.beads/metadata.json new file mode 100644 index 0000000..c787975 --- /dev/null +++ b/.beads/metadata.json @@ -0,0 +1,4 @@ +{ + "database": "beads.db", + "jsonl_export": "issues.jsonl" +} \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..ab1f416 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/csv2excel.iml b/.idea/csv2excel.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/csv2excel.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/go.imports.xml b/.idea/go.imports.xml new file mode 100644 index 0000000..d7202f0 --- /dev/null +++ b/.idea/go.imports.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..1b6b2e4 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file From cd2bd65b796e106a55276082c29a4a39af9f113c Mon Sep 17 00:00:00 2001 From: Oliver Jakoubek Date: Thu, 5 Mar 2026 10:58:21 +0100 Subject: [PATCH 4/8] docs: update CHANGELOG.md for v0.1.1 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a8c5d3..0b4595e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [v0.1.1] - 2026-03-05 + +### Fixed + +- `-o` flag value was read as a pointer instead of a string, causing memory addresses to be printed and the output path to resolve to `.xlsx` + ## [v0.1.0] - 2026-03-05 ### Added From 3c2c3cef57e4405fe6814352563a23aaf5101efb Mon Sep 17 00:00:00 2001 From: Oliver Jakoubek Date: Thu, 5 Mar 2026 11:28:44 +0100 Subject: [PATCH 5/8] fix: add --version flag and correct ldflags module path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add --version flag to CLI (closes bd-bxt) - Fix ldflags module path: csv2excel/internal/version → code.beautifulmachines.dev/jakoubek/csv2excel/internal/version so build info (version, commit, date) is actually embedded into the binary - Add mg.Deps(Build) to Install so it always builds before installing --- magefiles/magefile.go | 3 ++- main.go | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/magefiles/magefile.go b/magefiles/magefile.go index d9357e8..0184f1f 100644 --- a/magefiles/magefile.go +++ b/magefiles/magefile.go @@ -28,7 +28,7 @@ func ldflags() (string, error) { buildDate = "unknown" } return fmt.Sprintf( - `-X csv2excel/internal/version.Version=%s -X csv2excel/internal/version.Commit=%s -X csv2excel/internal/version.BuildDate=%s`, + `-X code.beautifulmachines.dev/jakoubek/csv2excel/internal/version.Version=%s -X code.beautifulmachines.dev/jakoubek/csv2excel/internal/version.Commit=%s -X code.beautifulmachines.dev/jakoubek/csv2excel/internal/version.BuildDate=%s`, version, commit, buildDate, ), nil } @@ -77,6 +77,7 @@ func BuildWindows() error { // Install installs the binary to $GOBIN or $GOPATH/bin. func Install() error { + mg.Deps(Build) fmt.Println("Installing", binaryName, "...") flags, err := ldflags() if err != nil { diff --git a/main.go b/main.go index 8fd8b0a..e57192a 100644 --- a/main.go +++ b/main.go @@ -12,14 +12,22 @@ import ( "github.com/xuri/excelize/v2" "golang.org/x/text/encoding/charmap" "golang.org/x/text/transform" + + "code.beautifulmachines.dev/jakoubek/csv2excel/internal/version" ) func main() { sep := flag.String("sep", "auto", "Trennzeichen: auto, ',', ';', '\\t'") enc := flag.String("enc", "utf8", "Encoding: utf8, windows1252") out := flag.String("o", "output.xlsx", "Ausgabedatei") + showVersion := flag.Bool("version", false, "Version anzeigen") flag.Parse() + if *showVersion { + fmt.Printf("csv2excel %s (commit %s, built %s)\n", version.Version, version.Commit, version.BuildDate) + os.Exit(0) + } + files := flag.Args() if len(files) == 0 { fmt.Fprintln(os.Stderr, "Verwendung: csv2xlsx [flags] datei1.csv datei2.csv ...") From ad8f7937b1fb92125b55c94d265c46d0d3159bd0 Mon Sep 17 00:00:00 2001 From: Oliver Jakoubek Date: Thu, 5 Mar 2026 11:35:58 +0100 Subject: [PATCH 6/8] fix: use time.Now() for BuildDate instead of shell date command (Windows-kompatibel) --- magefiles/magefile.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/magefiles/magefile.go b/magefiles/magefile.go index 0184f1f..4cbb769 100644 --- a/magefiles/magefile.go +++ b/magefiles/magefile.go @@ -7,6 +7,7 @@ import ( "os" "path/filepath" "runtime" + "time" "github.com/magefile/mage/mg" "github.com/magefile/mage/sh" @@ -23,10 +24,7 @@ func ldflags() (string, error) { if err != nil { commit = "none" } - buildDate, err := sh.Output("date", "-u", "+%Y-%m-%dT%H:%M:%SZ") - if err != nil { - buildDate = "unknown" - } + buildDate := time.Now().UTC().Format(time.RFC3339) return fmt.Sprintf( `-X code.beautifulmachines.dev/jakoubek/csv2excel/internal/version.Version=%s -X code.beautifulmachines.dev/jakoubek/csv2excel/internal/version.Commit=%s -X code.beautifulmachines.dev/jakoubek/csv2excel/internal/version.BuildDate=%s`, version, commit, buildDate, From 88aa81ff465f4b82b0338061d5dfa6092cf54148 Mon Sep 17 00:00:00 2001 From: Oliver Jakoubek Date: Thu, 5 Mar 2026 12:05:12 +0100 Subject: [PATCH 7/8] fix: make -o flag robust against PowerShell argument splitting PowerShell splits `-o=file.xlsx` into `-o=file` + `.xlsx` as separate args. Fix: auto-append .xlsx if output has no extension, and filter out .xlsx files from input args with a helpful warning. --- main.go | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index e57192a..7461ccc 100644 --- a/main.go +++ b/main.go @@ -28,9 +28,15 @@ func main() { os.Exit(0) } + // PowerShell kann "-o=testdatei.xlsx" in "-o=testdatei" + ".xlsx" splitten + if filepath.Ext(*out) == "" { + *out += ".xlsx" + } + files := flag.Args() if len(files) == 0 { - fmt.Fprintln(os.Stderr, "Verwendung: csv2xlsx [flags] datei1.csv datei2.csv ...") + fmt.Fprintln(os.Stderr, "Verwendung: csv2excel [flags] datei1.csv datei2.csv ...") + fmt.Fprintln(os.Stderr, "Tipp: In PowerShell Leerzeichen statt = verwenden: -o ausgabe.xlsx") os.Exit(1) } @@ -44,6 +50,19 @@ func run(files []string, out, sep, enc string) error { xlsx := excelize.NewFile() firstSheet := true + var csvFiles []string + for _, f := range files { + if strings.EqualFold(filepath.Ext(f), ".xlsx") { + fmt.Fprintf(os.Stderr, "Warnung: %s übersprungen – Excel-Datei als Input? Meintest du: -o=%s\n", f, f) + continue + } + csvFiles = append(csvFiles, f) + } + files = csvFiles + if len(files) == 0 { + return fmt.Errorf("keine CSV-Dateien zum Verarbeiten") + } + for _, path := range files { sheetName := strings.TrimSuffix(filepath.Base(path), filepath.Ext(path)) From bbdbffd78998ca986dc5073b439c67d4d7a3d93f Mon Sep 17 00:00:00 2001 From: Oliver Jakoubek Date: Thu, 5 Mar 2026 12:06:49 +0100 Subject: [PATCH 8/8] chore: close issues bd-3rr and bd-bxt (both implemented) --- .beads/issues.jsonl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index 952da85..ed93a6d 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -1,2 +1,2 @@ -{"id":"bd-3rr","title":"Fix -o flag: output filename not parsed correctly","description":"## Bug Report\n\nUsing the `-o` flag to specify an output filename causes errors. Instead of using the provided filename, the tool attempts to open/save `.xlsx` (empty basename) and prints memory addresses.\n\n## Reproduction\n\n```\ncsv2excel.exe -enc=windows1252 -o=test.xlsx .\\WDK-SiWo-20260103.csv\n```\n\n## Actual Output\n\n```\n0x3eb5d9d181b0\n0x3eb5d9d181c0\n0x3eb5d9d181d0\nFehler beim Öffnen .xlsx: open .xlsx: The system cannot find the file specified.\n✓ .\\WDK-SiWo-20260103.csv → Reiter \"WDK-SiWo-20260103\" (5324 Zeilen)\nFehler beim Speichern: unsupported workbook file format\n```\n\n## Analysis\n\n- Memory addresses being printed suggest a pointer/value is being passed where a string is expected (e.g. `fmt.Println(&flag)` instead of `fmt.Println(*flag)`)\n- The output path resolves to `.xlsx` instead of `test.xlsx`, meaning the flag value is not read correctly\n- The `unsupported workbook file format` error is a consequence of trying to open a non-existent file as a workbook\n\n## Acceptance Criteria\n- [ ] `csv2excel -o=test.xlsx input.csv` writes output to `test.xlsx`\n- [ ] No memory addresses are printed to stdout/stderr\n- [ ] Error message is shown if the output path is invalid/unwritable\n- [ ] Tests written and passing","status":"closed","priority":1,"issue_type":"bug","created_at":"2026-03-05T09:37:26.921804683Z","created_by":"oli","updated_at":"2026-03-05T09:56:12.843074108Z","closed_at":"2026-03-05T09:56:12.842163406Z","close_reason":"done","source_repo":".","compaction_level":0,"original_size":0} -{"id":"bd-bxt","title":"Add --version flag to CLI","description":"## Description\n\nAdd a `--version` flag to the `csv2excel` CLI that outputs version information including the version tag and build timestamp.\n\n## Expected Output\n\n```\ncsv2excel version v0.10.0-1-g3eb502a built 2026-02-19T14:30:13Z\n```\n\nThe version string should be injected at build time using Go linker flags (`-ldflags`), so that `git describe` and a build timestamp are embedded into the binary.\n\n## Implementation Notes\n\n- Define `version` and `buildDate` variables in `main.go` (or a dedicated `version.go`)\n- Inject values via `-ldflags \"-X main.version=... -X main.buildDate=...\"` during `mage Build`\n- Handle the `--version` / `-v` flag and print the version line, then exit 0\n\n## Acceptance Criteria\n- [ ] `csv2excel --version` prints a line matching `csv2excel version built `\n- [ ] Version and build date are injected at build time via ldflags in the Mage build targets\n- [ ] Running `csv2excel --version` exits with code 0\n- [ ] Default (unset) values produce a sensible fallback (e.g. `dev` / `unknown`)\n- [ ] Tests written and passing (if applicable)","status":"open","priority":2,"issue_type":"feature","created_at":"2026-03-05T09:22:40.538253362Z","created_by":"oli","updated_at":"2026-03-05T09:22:40.538253362Z","source_repo":".","compaction_level":0,"original_size":0} +{"id":"bd-3rr","title":"Fix -o flag: output filename not parsed correctly","description":"## Bug Report\n\nUsing the `-o` flag to specify an output filename causes errors. Instead of using the provided filename, the tool attempts to open/save `.xlsx` (empty basename) and prints memory addresses.\n\n## Reproduction\n\n```\ncsv2excel.exe -enc=windows1252 -o=test.xlsx .\\WDK-SiWo-20260103.csv\n```\n\n## Actual Output\n\n```\n0x3eb5d9d181b0\n0x3eb5d9d181c0\n0x3eb5d9d181d0\nFehler beim Öffnen .xlsx: open .xlsx: The system cannot find the file specified.\n✓ .\\WDK-SiWo-20260103.csv → Reiter \"WDK-SiWo-20260103\" (5324 Zeilen)\nFehler beim Speichern: unsupported workbook file format\n```\n\n## Analysis\n\n- Memory addresses being printed suggest a pointer/value is being passed where a string is expected (e.g. `fmt.Println(&flag)` instead of `fmt.Println(*flag)`)\n- The output path resolves to `.xlsx` instead of `test.xlsx`, meaning the flag value is not read correctly\n- The `unsupported workbook file format` error is a consequence of trying to open a non-existent file as a workbook\n\n## Acceptance Criteria\n- [ ] `csv2excel -o=test.xlsx input.csv` writes output to `test.xlsx`\n- [ ] No memory addresses are printed to stdout/stderr\n- [ ] Error message is shown if the output path is invalid/unwritable\n- [ ] Tests written and passing","status":"closed","priority":1,"issue_type":"bug","created_at":"2026-03-05T09:37:26.921804683Z","created_by":"oli","updated_at":"2026-03-05T12:10:00.000000000Z","closed_at":"2026-03-05T12:10:00.000000000Z","close_reason":"done","source_repo":".","compaction_level":0,"original_size":0} +{"id":"bd-bxt","title":"Add --version flag to CLI","description":"## Description\n\nAdd a `--version` flag to the `csv2excel` CLI that outputs version information including the version tag and build timestamp.\n\n## Expected Output\n\n```\ncsv2excel version v0.10.0-1-g3eb502a built 2026-02-19T14:30:13Z\n```\n\nThe version string should be injected at build time using Go linker flags (`-ldflags`), so that `git describe` and a build timestamp are embedded into the binary.\n\n## Implementation Notes\n\n- Define `version` and `buildDate` variables in `main.go` (or a dedicated `version.go`)\n- Inject values via `-ldflags \"-X main.version=... -X main.buildDate=...\"` during `mage Build`\n- Handle the `--version` / `-v` flag and print the version line, then exit 0\n\n## Acceptance Criteria\n- [ ] `csv2excel --version` prints a line matching `csv2excel version built `\n- [ ] Version and build date are injected at build time via ldflags in the Mage build targets\n- [ ] Running `csv2excel --version` exits with code 0\n- [ ] Default (unset) values produce a sensible fallback (e.g. `dev` / `unknown`)\n- [ ] Tests written and passing (if applicable)","status":"closed","priority":2,"issue_type":"feature","created_at":"2026-03-05T09:22:40.538253362Z","created_by":"oli","updated_at":"2026-03-05T12:10:00.000000000Z","closed_at":"2026-03-05T12:10:00.000000000Z","close_reason":"done","source_repo":".","compaction_level":0,"original_size":0}