synthkit 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
synthkit/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """Synthkit: Convert AI-generated Markdown into production-ready documents."""
2
+
3
+ __version__ = "0.1.0"
synthkit/base.py ADDED
@@ -0,0 +1,89 @@
1
+ """Base converter with shared logic for all Synthkit converters."""
2
+
3
+ import subprocess
4
+ import sys
5
+ from collections.abc import Callable
6
+ from pathlib import Path
7
+
8
+ import pypandoc
9
+
10
+ BASE_FORMAT = "markdown+lists_without_preceding_blankline"
11
+
12
+
13
+ def build_format(hard_breaks: bool = False) -> str:
14
+ fmt = BASE_FORMAT
15
+ if hard_breaks:
16
+ fmt += "+hard_line_breaks"
17
+ return fmt
18
+
19
+
20
+ def config_path(tool_name: str, filename: str) -> Path | None:
21
+ p = Path.home() / ".config" / tool_name / filename
22
+ return p if p.is_file() else None
23
+
24
+
25
+ def bundled_style_css() -> Path:
26
+ return Path(__file__).parent.parent.parent / "style.css"
27
+
28
+
29
+ def cleanup_mermaid_err() -> None:
30
+ err = Path("mermaid-filter.err")
31
+ if err.is_file() and err.stat().st_size == 0:
32
+ err.unlink()
33
+
34
+
35
+ def get_pandoc_bin() -> str:
36
+ return str(pypandoc.get_pandoc_path())
37
+
38
+
39
+ def run_pandoc(
40
+ args: list[str], env: dict[str, str] | None = None
41
+ ) -> subprocess.CompletedProcess[bytes]:
42
+ return subprocess.run([get_pandoc_bin(), *args], capture_output=False, env=env)
43
+
44
+
45
+ def mermaid_args(mermaid: bool) -> list[str]:
46
+ if mermaid:
47
+ return ["--filter", "mermaid-filter"]
48
+ return []
49
+
50
+
51
+ def batch_convert(
52
+ files: tuple[str, ...],
53
+ hard_breaks: bool,
54
+ mermaid: bool,
55
+ convert_one: Callable[[Path, bool, bool], None],
56
+ ) -> None:
57
+ if not files:
58
+ print("No input files provided.", file=sys.stderr)
59
+ sys.exit(1)
60
+
61
+ success = 0
62
+ fail = 0
63
+
64
+ for filepath in files:
65
+ path = Path(filepath)
66
+ if path.suffix != ".md":
67
+ print(f"Skipping non-markdown file: {filepath}")
68
+ continue
69
+ if not path.is_file():
70
+ print(f"Warning: File '{filepath}' not found, skipping.")
71
+ fail += 1
72
+ continue
73
+
74
+ try:
75
+ convert_one(path, hard_breaks, mermaid)
76
+ success += 1
77
+ except ConversionError as e:
78
+ print(f"Error: {e}")
79
+ fail += 1
80
+
81
+ if mermaid:
82
+ cleanup_mermaid_err()
83
+ print(f"\nDone: {success} succeeded, {fail} failed.")
84
+ if fail > 0:
85
+ sys.exit(1)
86
+
87
+
88
+ class ConversionError(Exception):
89
+ pass
synthkit/cli.py ADDED
@@ -0,0 +1,106 @@
1
+ """Click CLI for Synthkit."""
2
+
3
+ from pathlib import Path
4
+
5
+ import click
6
+
7
+ from . import doc, email, html, pdf
8
+ from .base import batch_convert
9
+
10
+ _MERMAID_HELP = "Enable Mermaid diagram rendering (requires mermaid-filter)."
11
+
12
+
13
+ @click.group()
14
+ def main() -> None:
15
+ """Synthkit: Convert AI-generated Markdown into production-ready documents."""
16
+
17
+
18
+ @main.command("doc")
19
+ @click.option("--hard-breaks", is_flag=True, help="Preserve line breaks in source.")
20
+ @click.option("--mermaid", is_flag=True, help=_MERMAID_HELP)
21
+ @click.argument("files", nargs=-1, type=click.Path())
22
+ def doc_subcmd(hard_breaks: bool, mermaid: bool, files: tuple[str, ...]) -> None:
23
+ """Convert Markdown to Word (.docx)."""
24
+ batch_convert(files, hard_breaks, mermaid, doc.convert)
25
+
26
+
27
+ @main.command("email")
28
+ @click.option("--hard-breaks", is_flag=True, help="Preserve line breaks in source.")
29
+ @click.option("--mermaid", is_flag=True, help=_MERMAID_HELP)
30
+ @click.argument("file", type=click.Path())
31
+ def email_subcmd(hard_breaks: bool, mermaid: bool, file: str) -> None:
32
+ """Convert Markdown to clipboard-ready email."""
33
+ email.convert(Path(file), hard_breaks, mermaid)
34
+
35
+
36
+ @main.command("html")
37
+ @click.option("--hard-breaks", is_flag=True, help="Preserve line breaks in source.")
38
+ @click.option("--mermaid", is_flag=True, help=_MERMAID_HELP)
39
+ @click.argument("files", nargs=-1, type=click.Path())
40
+ def html_subcmd(hard_breaks: bool, mermaid: bool, files: tuple[str, ...]) -> None:
41
+ """Convert Markdown to HTML."""
42
+ batch_convert(files, hard_breaks, mermaid, html.convert)
43
+
44
+
45
+ @main.command("pdf")
46
+ @click.option("--hard-breaks", is_flag=True, help="Preserve line breaks in source.")
47
+ @click.option("--mermaid", is_flag=True, help=_MERMAID_HELP)
48
+ @click.argument("files", nargs=-1, type=click.Path())
49
+ def pdf_subcmd(hard_breaks: bool, mermaid: bool, files: tuple[str, ...]) -> None:
50
+ """Convert Markdown to PDF."""
51
+ batch_convert(files, hard_breaks, mermaid, pdf.convert)
52
+
53
+
54
+ # Backward-compatible standalone entry points
55
+
56
+
57
+ def md2doc_cmd() -> None:
58
+ """Standalone md2doc entry point."""
59
+
60
+ @click.command()
61
+ @click.option("--hard-breaks", is_flag=True, help="Preserve line breaks in source.")
62
+ @click.option("--mermaid", is_flag=True, help=_MERMAID_HELP)
63
+ @click.argument("files", nargs=-1, type=click.Path())
64
+ def _cmd(hard_breaks: bool, mermaid: bool, files: tuple[str, ...]) -> None:
65
+ batch_convert(files, hard_breaks, mermaid, doc.convert)
66
+
67
+ _cmd()
68
+
69
+
70
+ def md2email_cmd() -> None:
71
+ """Standalone md2email entry point."""
72
+
73
+ @click.command()
74
+ @click.option("--hard-breaks", is_flag=True, help="Preserve line breaks in source.")
75
+ @click.option("--mermaid", is_flag=True, help=_MERMAID_HELP)
76
+ @click.argument("file", type=click.Path())
77
+ def _cmd(hard_breaks: bool, mermaid: bool, file: str) -> None:
78
+ email.convert(Path(file), hard_breaks, mermaid)
79
+
80
+ _cmd()
81
+
82
+
83
+ def md2html_cmd() -> None:
84
+ """Standalone md2html entry point."""
85
+
86
+ @click.command()
87
+ @click.option("--hard-breaks", is_flag=True, help="Preserve line breaks in source.")
88
+ @click.option("--mermaid", is_flag=True, help=_MERMAID_HELP)
89
+ @click.argument("files", nargs=-1, type=click.Path())
90
+ def _cmd(hard_breaks: bool, mermaid: bool, files: tuple[str, ...]) -> None:
91
+ batch_convert(files, hard_breaks, mermaid, html.convert)
92
+
93
+ _cmd()
94
+
95
+
96
+ def md2pdf_cmd() -> None:
97
+ """Standalone md2pdf entry point."""
98
+
99
+ @click.command()
100
+ @click.option("--hard-breaks", is_flag=True, help="Preserve line breaks in source.")
101
+ @click.option("--mermaid", is_flag=True, help=_MERMAID_HELP)
102
+ @click.argument("files", nargs=-1, type=click.Path())
103
+ def _cmd(hard_breaks: bool, mermaid: bool, files: tuple[str, ...]) -> None:
104
+ batch_convert(files, hard_breaks, mermaid, pdf.convert)
105
+
106
+ _cmd()
synthkit/doc.py ADDED
@@ -0,0 +1,24 @@
1
+ """Markdown to Word (.docx) conversion."""
2
+
3
+ from pathlib import Path
4
+
5
+ from .base import ConversionError, build_format, config_path, mermaid_args, run_pandoc
6
+
7
+
8
+ def convert(path: Path, hard_breaks: bool = False, mermaid: bool = False) -> None:
9
+ output = path.with_suffix(".docx").name
10
+ fmt = build_format(hard_breaks)
11
+
12
+ args = [str(path), "-f", fmt, "-t", "docx", *mermaid_args(mermaid)]
13
+
14
+ ref = config_path("md2doc", "reference.docx")
15
+ if ref:
16
+ args += [f"--reference-doc={ref}"]
17
+
18
+ args += ["-o", output]
19
+
20
+ print(f"Converting {path} to {output}...")
21
+ result = run_pandoc(args)
22
+ if result.returncode != 0:
23
+ raise ConversionError(f"Pandoc failed to generate {output}")
24
+ print(f"Successfully created: {output}")
synthkit/email.py ADDED
@@ -0,0 +1,77 @@
1
+ """Markdown to clipboard email conversion."""
2
+
3
+ import platform
4
+ import subprocess
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ import pyperclip
9
+
10
+ from .base import (
11
+ build_format,
12
+ cleanup_mermaid_err,
13
+ config_path,
14
+ get_pandoc_bin,
15
+ mermaid_args,
16
+ )
17
+
18
+
19
+ def convert(path: Path, hard_breaks: bool = False, mermaid: bool = False) -> None:
20
+ # Smart file finding: accept with or without .md extension
21
+ if not path.is_file() and path.with_suffix(".md").is_file():
22
+ path = path.with_suffix(".md")
23
+
24
+ if not path.is_file():
25
+ print(f"Error: Could not find file '{path}' or '{path.with_suffix('.md')}'")
26
+ sys.exit(1)
27
+
28
+ fmt = build_format(hard_breaks)
29
+
30
+ args = [
31
+ get_pandoc_bin(),
32
+ str(path),
33
+ "-f",
34
+ fmt,
35
+ "-t",
36
+ "html",
37
+ "-s",
38
+ "--self-contained",
39
+ *mermaid_args(mermaid),
40
+ ]
41
+
42
+ style = config_path("md2email", "style.css")
43
+ if style:
44
+ args += [f"--css={style}"]
45
+
46
+ print(f"Converting {path} to clipboard...")
47
+ result = subprocess.run(args, capture_output=True, text=True)
48
+ if result.returncode != 0:
49
+ print(f"Error: Pandoc failed to convert {path}")
50
+ if result.stderr:
51
+ print(result.stderr, file=sys.stderr)
52
+ sys.exit(1)
53
+
54
+ html = result.stdout
55
+
56
+ # On macOS, convert to RTF via textutil for richer paste support
57
+ if platform.system() == "Darwin":
58
+ try:
59
+ rtf_result = subprocess.run(
60
+ ["textutil", "-stdin", "-format", "html", "-convert", "rtf", "-stdout"],
61
+ input=html,
62
+ capture_output=True,
63
+ text=True,
64
+ )
65
+ if rtf_result.returncode == 0:
66
+ subprocess.run(["pbcopy"], input=rtf_result.stdout, text=True, check=True)
67
+ print("Success! Formatted text copied to clipboard.")
68
+ if mermaid:
69
+ cleanup_mermaid_err()
70
+ return
71
+ except FileNotFoundError:
72
+ pass # Fall through to pyperclip
73
+
74
+ pyperclip.copy(html)
75
+ print("Success! HTML copied to clipboard.")
76
+ if mermaid:
77
+ cleanup_mermaid_err()
synthkit/html.py ADDED
@@ -0,0 +1,24 @@
1
+ """Markdown to HTML conversion."""
2
+
3
+ from pathlib import Path
4
+
5
+ from .base import ConversionError, build_format, config_path, mermaid_args, run_pandoc
6
+
7
+
8
+ def convert(path: Path, hard_breaks: bool = False, mermaid: bool = False) -> None:
9
+ output = path.with_suffix(".html").name
10
+ fmt = build_format(hard_breaks)
11
+
12
+ args = [str(path), "-f", fmt, "-t", "html", "-s", *mermaid_args(mermaid)]
13
+
14
+ style = config_path("md2html", "style.css")
15
+ if style:
16
+ args += [f"--css={style}", "--self-contained"]
17
+
18
+ args += ["-o", output]
19
+
20
+ print(f"Converting {path} to {output}...")
21
+ result = run_pandoc(args)
22
+ if result.returncode != 0:
23
+ raise ConversionError(f"Pandoc failed to generate {output}")
24
+ print(f"Successfully created: {output}")
synthkit/pdf.py ADDED
@@ -0,0 +1,134 @@
1
+ """Markdown to PDF conversion via weasyprint."""
2
+
3
+ import os
4
+ import platform
5
+ import shutil
6
+ import subprocess
7
+ from pathlib import Path
8
+
9
+ from .base import ConversionError, build_format, config_path, mermaid_args, run_pandoc
10
+
11
+ _INSTALL_HELP = {
12
+ "Darwin": ("On macOS, install them with:\n brew install pango"),
13
+ "Linux": (
14
+ "On Ubuntu/Debian, install them with:\n"
15
+ " sudo apt install libpango1.0-dev libcairo2-dev libgdk-pixbuf2.0-dev\n"
16
+ "On Fedora/RHEL:\n"
17
+ " sudo dnf install pango-devel cairo-devel gdk-pixbuf2-devel"
18
+ ),
19
+ }
20
+
21
+ _WEASYPRINT_DOCS = "https://doc.courtbouillon.org/weasyprint/stable/first_steps.html#installation"
22
+
23
+
24
+ def _brew_lib_path() -> str | None:
25
+ """Return Homebrew lib directory if available."""
26
+ try:
27
+ result = subprocess.run(["brew", "--prefix"], capture_output=True, timeout=5)
28
+ if result.returncode == 0:
29
+ prefix = result.stdout.decode().strip()
30
+ lib_dir = os.path.join(prefix, "lib")
31
+ if os.path.isdir(lib_dir):
32
+ return lib_dir
33
+ except (FileNotFoundError, subprocess.TimeoutExpired):
34
+ pass
35
+ return None
36
+
37
+
38
+ def _weasyprint_env() -> dict[str, str] | None:
39
+ """Build environment with library paths for weasyprint on macOS."""
40
+ if platform.system() != "Darwin":
41
+ return None
42
+ brew_lib = _brew_lib_path()
43
+ if not brew_lib:
44
+ return None
45
+ env = os.environ.copy()
46
+ existing = env.get("DYLD_FALLBACK_LIBRARY_PATH", "")
47
+ if brew_lib not in existing:
48
+ env["DYLD_FALLBACK_LIBRARY_PATH"] = f"{brew_lib}:{existing}" if existing else brew_lib
49
+ return env
50
+
51
+
52
+ def _check_weasyprint_deps() -> dict[str, str] | None:
53
+ """Verify weasyprint's system deps are available. Returns env dict if needed."""
54
+ env = None
55
+ try:
56
+ result = subprocess.run(
57
+ ["weasyprint", "--info"],
58
+ capture_output=True,
59
+ timeout=10,
60
+ )
61
+ if result.returncode != 0:
62
+ stderr = result.stderr.decode(errors="replace")
63
+ if "gobject" in stderr or "pango" in stderr or "cairo" in stderr:
64
+ # Try again with Homebrew library path on macOS
65
+ env = _weasyprint_env()
66
+ if env:
67
+ retry = subprocess.run(
68
+ ["weasyprint", "--info"],
69
+ capture_output=True,
70
+ timeout=10,
71
+ env=env,
72
+ )
73
+ if retry.returncode == 0:
74
+ return env # libs found with env fix
75
+
76
+ # Still broken — report helpful error
77
+ system = platform.system()
78
+ hint = _INSTALL_HELP.get(system, "")
79
+ msg = (
80
+ "weasyprint is installed but cannot find its system libraries "
81
+ "(pango, cairo, gobject).\n"
82
+ )
83
+ if hint:
84
+ msg += f"\n{hint}\n"
85
+ msg += f"\nFor full details see: {_WEASYPRINT_DOCS}"
86
+ raise ConversionError(msg)
87
+ except FileNotFoundError:
88
+ pass # handled by the shutil.which check below
89
+ except subprocess.TimeoutExpired:
90
+ pass # let pandoc attempt it
91
+ return env
92
+
93
+
94
+ def convert(path: Path, hard_breaks: bool = False, mermaid: bool = False) -> None:
95
+ output = path.with_suffix(".pdf").name
96
+ fmt = build_format(hard_breaks)
97
+
98
+ if not shutil.which("weasyprint"):
99
+ raise ConversionError(
100
+ "weasyprint not found on PATH. Install it with: pip install weasyprint"
101
+ )
102
+
103
+ env = _check_weasyprint_deps()
104
+
105
+ args = [
106
+ str(path),
107
+ "-f",
108
+ fmt,
109
+ "-t",
110
+ "html",
111
+ "--pdf-engine=weasyprint",
112
+ *mermaid_args(mermaid),
113
+ ]
114
+
115
+ style = config_path("md2pdf", "style.css")
116
+ if style:
117
+ args += [f"--css={style}"]
118
+
119
+ args += ["-o", output]
120
+
121
+ print(f"Converting {path} to {output}...")
122
+ result = run_pandoc(args, env=env)
123
+ if result.returncode != 0:
124
+ system = platform.system()
125
+ hint = _INSTALL_HELP.get(system, "")
126
+ msg = f"Pandoc failed to generate {output}"
127
+ if hint:
128
+ msg += (
129
+ "\n\nThis may be caused by missing system dependencies for weasyprint.\n"
130
+ f"{hint}\n"
131
+ f"\nFor full details see: {_WEASYPRINT_DOCS}"
132
+ )
133
+ raise ConversionError(msg)
134
+ print(f"Successfully created: {output}")
@@ -0,0 +1,165 @@
1
+ Metadata-Version: 2.4
2
+ Name: synthkit
3
+ Version: 0.1.0
4
+ Summary: Convert AI-generated Markdown into production-ready documents
5
+ License-Expression: MIT
6
+ Requires-Python: >=3.10
7
+ Requires-Dist: click
8
+ Requires-Dist: pypandoc-binary
9
+ Requires-Dist: pyperclip
10
+ Requires-Dist: weasyprint
11
+ Provides-Extra: dev
12
+ Requires-Dist: mypy; extra == 'dev'
13
+ Requires-Dist: pytest; extra == 'dev'
14
+ Requires-Dist: pytest-cov; extra == 'dev'
15
+ Requires-Dist: ruff; extra == 'dev'
16
+ Description-Content-Type: text/markdown
17
+
18
+ # Synthkit
19
+
20
+ [![PyPI](https://img.shields.io/pypi/v/synthkit.svg)](https://pypi.org/project/synthkit/)
21
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/downloads/)
22
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://opensource.org/licenses/MIT)
23
+ [![Tests](https://github.com/rappdw/synthkit/actions/workflows/tests.yml/badge.svg)](https://github.com/rappdw/synthkit/actions/workflows/tests.yml)
24
+ [![Coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/rappdw/02d0a91962d09fd9473da34fccd76237/raw/synthkit-coverage.json)](https://github.com/rappdw/synthkit/actions/workflows/tests.yml)
25
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
26
+ [![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/)
27
+
28
+ A "last-mile" toolkit for working with generative AI. Synthkit bridges the gap between raw LLM output and production-ready deliverables through:
29
+
30
+ - **Document conversion** — Transform AI-generated Markdown into Word, HTML, PDF, or clipboard-ready email
31
+ - **Prompt templates** — Curated templates for structured AI interactions (reports, emails, analysis)
32
+ - **Guidelines** — Reference standards and style guides to steer AI output quality
33
+
34
+ ## Document Conversion
35
+
36
+ ### Installation
37
+
38
+ ```bash
39
+ # Run directly with uvx (no install needed)
40
+ uvx synthkit html document.md
41
+
42
+ # Or install globally
43
+ uv tool install synthkit
44
+
45
+ # Or install with pip
46
+ pip install synthkit
47
+ ```
48
+
49
+ Pandoc is bundled automatically via [`pypandoc_binary`](https://pypi.org/project/pypandoc-binary/) — no separate install needed.
50
+
51
+ #### System dependencies for PDF
52
+
53
+ PDF conversion uses [WeasyPrint](https://weasyprint.org/), which requires system libraries:
54
+
55
+ | Platform | Install command |
56
+ |----------|----------------|
57
+ | **macOS** | `brew install pango` |
58
+ | **Ubuntu/Debian** | `apt install libpango1.0-dev libcairo2-dev libgdk-pixbuf2.0-dev` |
59
+ | **Windows** | See [WeasyPrint docs](https://doc.courtbouillon.org/weasyprint/stable/first_steps.html#windows) |
60
+
61
+ `doc`, `html`, and `email` commands work without these.
62
+
63
+ ### Usage
64
+
65
+ #### Unified CLI
66
+
67
+ ```bash
68
+ synthkit doc report.md # → report.docx
69
+ synthkit html report.md # → report.html
70
+ synthkit pdf report.md # → report.pdf
71
+ synthkit email report.md # → clipboard
72
+
73
+ # Batch processing
74
+ synthkit doc *.md
75
+ synthkit html *.md --hard-breaks
76
+
77
+ # Mermaid diagrams (requires mermaid-filter)
78
+ synthkit html report.md --mermaid
79
+ ```
80
+
81
+ #### Backward-compatible commands
82
+
83
+ ```bash
84
+ md2doc report.md
85
+ md2html report.md
86
+ md2pdf report.md
87
+ md2email report.md
88
+ ```
89
+
90
+ #### Options
91
+
92
+ | Flag | Description |
93
+ |------|-------------|
94
+ | `--hard-breaks` | Preserve line breaks from source markdown |
95
+ | `--mermaid` | Enable Mermaid diagram rendering (requires [`mermaid-filter`](https://github.com/raghur/mermaid-filter)) |
96
+
97
+ ### Configuration
98
+
99
+ Each converter looks for optional config files under `~/.config/<toolname>/`:
100
+
101
+ | Converter | Config Files |
102
+ |-----------|-------------|
103
+ | `doc` | `~/.config/md2doc/reference.docx` |
104
+ | `email` | `~/.config/md2email/style.css` |
105
+ | `html` | `~/.config/md2html/style.css` |
106
+ | `pdf` | `~/.config/md2pdf/style.css` |
107
+
108
+ ## Prompt Templates
109
+
110
+ The `prompt-templates/` directory contains curated prompt templates for structured AI interactions. These are optimized for Markdown-first responses to ensure compatibility with the document conversion tools.
111
+
112
+ Copy the contents of any template into your LLM of choice (Claude, Gemini, ChatGPT, etc.) to get consistently structured output ready for conversion.
113
+
114
+ ## Guidelines
115
+
116
+ The `guidelines/` directory contains reference standards and style guides that can be provided as context to AI models to steer output quality and consistency.
117
+
118
+ ## Testing
119
+
120
+ ```bash
121
+ # Run tests
122
+ uv run --extra dev pytest
123
+
124
+ # With coverage
125
+ uv run --extra dev pytest --cov=synthkit --cov-report=term-missing
126
+ ```
127
+
128
+ Tests run automatically on push/PR to `main` across Python 3.10-3.13 on Linux, macOS, and Windows.
129
+
130
+ ## Repository Structure
131
+
132
+ ```
133
+ ├── .github/workflows/
134
+ │ ├── tests.yml # CI: test on push/PR to main
135
+ │ └── publish.yml # CD: publish to PyPI on release
136
+ ├── pyproject.toml
137
+ ├── src/synthkit/ # Python package
138
+ │ ├── cli.py # Click CLI with subcommands
139
+ │ ├── base.py # Shared conversion logic
140
+ │ ├── doc.py # Word conversion
141
+ │ ├── email.py # Email clipboard conversion
142
+ │ ├── html.py # HTML conversion
143
+ │ └── pdf.py # PDF conversion (via WeasyPrint)
144
+ ├── tests/ # Test suite (pytest)
145
+ │ ├── conftest.py # Shared fixtures
146
+ │ ├── test_base.py # Base module tests
147
+ │ ├── test_cli.py # CLI + integration tests
148
+ │ ├── test_doc.py # Word converter tests
149
+ │ ├── test_email.py # Email converter tests
150
+ │ ├── test_html.py # HTML converter tests
151
+ │ └── test_pdf.py # PDF converter tests
152
+ ├── style.css # Default stylesheet
153
+ ├── prompt-templates/ # AI interaction prompt templates
154
+ └── guidelines/ # Reference guidelines
155
+ ```
156
+
157
+ ## Dependencies
158
+
159
+ | Package | Purpose | Bundled? |
160
+ |---------|---------|----------|
161
+ | [`click`](https://click.palletsprojects.com/) | CLI framework | pip |
162
+ | [`pypandoc_binary`](https://pypi.org/project/pypandoc-binary/) | Pandoc document converter | pip (includes binary) |
163
+ | [`pyperclip`](https://pypi.org/project/pyperclip/) | Cross-platform clipboard | pip |
164
+ | [`weasyprint`](https://weasyprint.org/) | PDF engine | pip (needs system libs) |
165
+ | [`mermaid-filter`](https://github.com/raghur/mermaid-filter) | Mermaid diagrams | Optional, external |
@@ -0,0 +1,11 @@
1
+ synthkit/__init__.py,sha256=MdHu9RiZYInmSDUdVZAKcZDCixIcdsM49x5cJgwHqIk,102
2
+ synthkit/base.py,sha256=rjcpiCTepgizEs2ygNxTIR3ZHBBA8Z8a7fKwbwx0_dI,2147
3
+ synthkit/cli.py,sha256=YYAEKZ1PxvZVdhIHv-4xjjAYb_bf34HDHoB48WCDGoo,3800
4
+ synthkit/doc.py,sha256=eSL6lHbC_bPblMY2MDU3LIN5xC4GpedKm9wOuRivzg0,752
5
+ synthkit/email.py,sha256=WfjrYAewg_qYFz6LW6CtQwLdfGnAAYyTLRq6xB_KQlo,2152
6
+ synthkit/html.py,sha256=BABUCG_gX-WWrfrOo4a_AmAtdfevtmxXRLJaxVwTpWk,762
7
+ synthkit/pdf.py,sha256=UpGQSRlFxxYTDJCN6HqfoOpN5pbcNtdSWTHguhnWbtI,4468
8
+ synthkit-0.1.0.dist-info/METADATA,sha256=sqU82aulOLONJ9Zei-xrBqZA9M24VuJK7-k6SByQ8aI,6422
9
+ synthkit-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
10
+ synthkit-0.1.0.dist-info/entry_points.txt,sha256=zB7h-Oabqqkac6L2XPCF-JWDp3IAPy-_7S-1Qe9BN6A,185
11
+ synthkit-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,6 @@
1
+ [console_scripts]
2
+ md2doc = synthkit.cli:md2doc_cmd
3
+ md2email = synthkit.cli:md2email_cmd
4
+ md2html = synthkit.cli:md2html_cmd
5
+ md2pdf = synthkit.cli:md2pdf_cmd
6
+ synthkit = synthkit.cli:main