synthkit 0.1.0__tar.gz
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-0.1.0/.github/workflows/publish.yml +59 -0
- synthkit-0.1.0/.github/workflows/tests.yml +91 -0
- synthkit-0.1.0/.gitignore +9 -0
- synthkit-0.1.0/CLAUDE.md +68 -0
- synthkit-0.1.0/PKG-INFO +165 -0
- synthkit-0.1.0/README.md +148 -0
- synthkit-0.1.0/guidelines/.gitkeep +0 -0
- synthkit-0.1.0/prompt-templates/.gitkeep +0 -0
- synthkit-0.1.0/pyproject.toml +54 -0
- synthkit-0.1.0/src/synthkit/__init__.py +3 -0
- synthkit-0.1.0/src/synthkit/base.py +89 -0
- synthkit-0.1.0/src/synthkit/cli.py +106 -0
- synthkit-0.1.0/src/synthkit/doc.py +24 -0
- synthkit-0.1.0/src/synthkit/email.py +77 -0
- synthkit-0.1.0/src/synthkit/html.py +24 -0
- synthkit-0.1.0/src/synthkit/pdf.py +134 -0
- synthkit-0.1.0/style.css +21 -0
- synthkit-0.1.0/tests/__init__.py +0 -0
- synthkit-0.1.0/tests/conftest.py +35 -0
- synthkit-0.1.0/tests/test_base.py +158 -0
- synthkit-0.1.0/tests/test_cli.py +131 -0
- synthkit-0.1.0/tests/test_doc.py +71 -0
- synthkit-0.1.0/tests/test_email.py +122 -0
- synthkit-0.1.0/tests/test_html.py +69 -0
- synthkit-0.1.0/tests/test_pdf.py +175 -0
- synthkit-0.1.0/uv.lock +951 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Install uv
|
|
17
|
+
uses: astral-sh/setup-uv@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up Python
|
|
20
|
+
run: uv python install 3.12
|
|
21
|
+
|
|
22
|
+
- name: Build package
|
|
23
|
+
run: uv build
|
|
24
|
+
|
|
25
|
+
- name: Upload build artifacts
|
|
26
|
+
uses: actions/upload-artifact@v4
|
|
27
|
+
with:
|
|
28
|
+
name: dist
|
|
29
|
+
path: dist/
|
|
30
|
+
|
|
31
|
+
test:
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@v4
|
|
35
|
+
|
|
36
|
+
- name: Install uv
|
|
37
|
+
uses: astral-sh/setup-uv@v4
|
|
38
|
+
|
|
39
|
+
- name: Set up Python
|
|
40
|
+
run: uv python install 3.12
|
|
41
|
+
|
|
42
|
+
- name: Run tests
|
|
43
|
+
run: uv run --extra dev pytest -v
|
|
44
|
+
|
|
45
|
+
publish:
|
|
46
|
+
needs: [build, test]
|
|
47
|
+
runs-on: ubuntu-latest
|
|
48
|
+
environment: pypi
|
|
49
|
+
permissions:
|
|
50
|
+
id-token: write
|
|
51
|
+
steps:
|
|
52
|
+
- name: Download build artifacts
|
|
53
|
+
uses: actions/download-artifact@v4
|
|
54
|
+
with:
|
|
55
|
+
name: dist
|
|
56
|
+
path: dist/
|
|
57
|
+
|
|
58
|
+
- name: Publish to PyPI
|
|
59
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
lint:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Install uv
|
|
19
|
+
uses: astral-sh/setup-uv@v4
|
|
20
|
+
|
|
21
|
+
- name: Set up Python
|
|
22
|
+
run: uv python install 3.12
|
|
23
|
+
|
|
24
|
+
- name: Ruff lint
|
|
25
|
+
run: uv run --extra dev ruff check src/ tests/
|
|
26
|
+
|
|
27
|
+
- name: Ruff format
|
|
28
|
+
run: uv run --extra dev ruff format --check src/ tests/
|
|
29
|
+
|
|
30
|
+
typecheck:
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v4
|
|
34
|
+
|
|
35
|
+
- name: Install uv
|
|
36
|
+
uses: astral-sh/setup-uv@v4
|
|
37
|
+
|
|
38
|
+
- name: Set up Python
|
|
39
|
+
run: uv python install 3.12
|
|
40
|
+
|
|
41
|
+
- name: Mypy
|
|
42
|
+
run: uv run --extra dev mypy
|
|
43
|
+
|
|
44
|
+
test:
|
|
45
|
+
runs-on: ${{ matrix.os }}
|
|
46
|
+
strategy:
|
|
47
|
+
fail-fast: false
|
|
48
|
+
matrix:
|
|
49
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
50
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
51
|
+
steps:
|
|
52
|
+
- uses: actions/checkout@v4
|
|
53
|
+
|
|
54
|
+
- name: Install uv
|
|
55
|
+
uses: astral-sh/setup-uv@v4
|
|
56
|
+
|
|
57
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
58
|
+
run: uv python install ${{ matrix.python-version }}
|
|
59
|
+
|
|
60
|
+
- name: Run tests
|
|
61
|
+
run: uv run --extra dev pytest -v
|
|
62
|
+
|
|
63
|
+
coverage:
|
|
64
|
+
runs-on: ubuntu-latest
|
|
65
|
+
steps:
|
|
66
|
+
- uses: actions/checkout@v4
|
|
67
|
+
|
|
68
|
+
- name: Install uv
|
|
69
|
+
uses: astral-sh/setup-uv@v4
|
|
70
|
+
|
|
71
|
+
- name: Set up Python
|
|
72
|
+
run: uv python install 3.12
|
|
73
|
+
|
|
74
|
+
- name: Run tests with coverage
|
|
75
|
+
run: |
|
|
76
|
+
uv run --extra dev pytest --cov=synthkit --cov-report=term | tee coverage-output.txt
|
|
77
|
+
COVERAGE=$(grep '^TOTAL' coverage-output.txt | awk '{print $NF}' | tr -d '%')
|
|
78
|
+
echo "COVERAGE=$COVERAGE" >> $GITHUB_ENV
|
|
79
|
+
|
|
80
|
+
- name: Update coverage badge
|
|
81
|
+
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
|
82
|
+
uses: schneegans/dynamic-badges-action@v1.7.0
|
|
83
|
+
with:
|
|
84
|
+
auth: ${{ secrets.GIST_TOKEN }}
|
|
85
|
+
gistID: ${{ vars.COVERAGE_GIST_ID }}
|
|
86
|
+
filename: synthkit-coverage.json
|
|
87
|
+
label: coverage
|
|
88
|
+
message: ${{ env.COVERAGE }}%
|
|
89
|
+
valColorRange: ${{ env.COVERAGE }}
|
|
90
|
+
minColorRange: 50
|
|
91
|
+
maxColorRange: 100
|
synthkit-0.1.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
Synthkit is a Python package for converting AI-generated Markdown (from Claude, Gemini, etc.) into production-ready documents. It provides a unified CLI (`synthkit doc/email/html/pdf`) and backward-compatible standalone commands (`md2doc`, `md2email`, `md2html`, `md2pdf`). Installable via `uvx synthkit` or `pip install synthkit`.
|
|
8
|
+
|
|
9
|
+
## Repository Structure
|
|
10
|
+
|
|
11
|
+
- **`src/synthkit/`**: Python package source
|
|
12
|
+
- `cli.py` — Click CLI with subcommands and standalone entry points
|
|
13
|
+
- `base.py` — Shared logic (format building, config discovery, batch processing, pandoc invocation)
|
|
14
|
+
- `doc.py` — Markdown → Word (.docx) via pandoc
|
|
15
|
+
- `email.py` — Markdown → clipboard (HTML/RTF, cross-platform via pyperclip)
|
|
16
|
+
- `html.py` — Markdown → HTML via pandoc
|
|
17
|
+
- `pdf.py` — Markdown → PDF via pandoc + weasyprint
|
|
18
|
+
- **`pyproject.toml`**: Package config (hatchling build, click+pypandoc_binary+pyperclip+weasyprint deps)
|
|
19
|
+
- **`style.css`**: Default stylesheet bundled with the package
|
|
20
|
+
- **`prompt-templates/`**: Prompt templates for AI interaction use cases
|
|
21
|
+
- **`guidelines/`**: Reference guidelines and standards
|
|
22
|
+
|
|
23
|
+
## Development
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Install in development mode
|
|
27
|
+
uv pip install -e .
|
|
28
|
+
|
|
29
|
+
# Run directly
|
|
30
|
+
uv run synthkit html example.md
|
|
31
|
+
uv run md2html example.md
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Architecture
|
|
35
|
+
|
|
36
|
+
- All converters share `base.py` logic: pandoc format string (`markdown+lists_without_preceding_blankline` ± `hard_line_breaks`), config file discovery under `~/.config/<toolname>/`, batch processing with success/fail counting.
|
|
37
|
+
- Mermaid diagram support is opt-in via `--mermaid` flag (requires `mermaid-filter` installed externally).
|
|
38
|
+
- `email.py` is cross-platform: uses `textutil`+`pbcopy` on macOS for RTF clipboard, falls back to `pyperclip` (HTML) on other platforms.
|
|
39
|
+
- `pdf.py` uses `--pdf-engine=weasyprint` (CSS-styled, no LaTeX needed). Config via `~/.config/md2pdf/style.css`.
|
|
40
|
+
- Entry points defined in `pyproject.toml`: `synthkit` (unified CLI group), plus `md2doc`/`md2email`/`md2html`/`md2pdf` (standalone).
|
|
41
|
+
|
|
42
|
+
## Testing
|
|
43
|
+
|
|
44
|
+
Uses pytest. Tests are in `tests/` with shared fixtures in `conftest.py`.
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
uv run --extra dev pytest # run all tests
|
|
48
|
+
uv run --extra dev pytest -v # verbose
|
|
49
|
+
uv run --extra dev pytest -k base # run specific module
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Tests use mocking for pandoc/clipboard calls. Integration tests in `test_cli.py::TestIntegration` run actual pandoc via the bundled binary.
|
|
53
|
+
|
|
54
|
+
## Key Dependencies
|
|
55
|
+
|
|
56
|
+
### Python (pip-installed automatically)
|
|
57
|
+
- **click** (CLI framework)
|
|
58
|
+
- **pypandoc_binary** (bundles pandoc binary)
|
|
59
|
+
- **pyperclip** (cross-platform clipboard)
|
|
60
|
+
- **weasyprint** (PDF engine)
|
|
61
|
+
|
|
62
|
+
### System (required for PDF only)
|
|
63
|
+
- **pango**, **cairo**, **gobject** — required by weasyprint
|
|
64
|
+
- macOS: `brew install pango`
|
|
65
|
+
- Ubuntu/Debian: `apt install libpango1.0-dev libcairo2-dev libgdk-pixbuf2.0-dev`
|
|
66
|
+
|
|
67
|
+
### External (optional)
|
|
68
|
+
- **mermaid-filter** (Mermaid diagram rendering, opt-in via `--mermaid` flag)
|
synthkit-0.1.0/PKG-INFO
ADDED
|
@@ -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
|
+
[](https://pypi.org/project/synthkit/)
|
|
21
|
+
[](https://www.python.org/downloads/)
|
|
22
|
+
[](https://opensource.org/licenses/MIT)
|
|
23
|
+
[](https://github.com/rappdw/synthkit/actions/workflows/tests.yml)
|
|
24
|
+
[](https://github.com/rappdw/synthkit/actions/workflows/tests.yml)
|
|
25
|
+
[](https://github.com/astral-sh/ruff)
|
|
26
|
+
[](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 |
|
synthkit-0.1.0/README.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Synthkit
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/synthkit/)
|
|
4
|
+
[](https://www.python.org/downloads/)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://github.com/rappdw/synthkit/actions/workflows/tests.yml)
|
|
7
|
+
[](https://github.com/rappdw/synthkit/actions/workflows/tests.yml)
|
|
8
|
+
[](https://github.com/astral-sh/ruff)
|
|
9
|
+
[](https://mypy-lang.org/)
|
|
10
|
+
|
|
11
|
+
A "last-mile" toolkit for working with generative AI. Synthkit bridges the gap between raw LLM output and production-ready deliverables through:
|
|
12
|
+
|
|
13
|
+
- **Document conversion** — Transform AI-generated Markdown into Word, HTML, PDF, or clipboard-ready email
|
|
14
|
+
- **Prompt templates** — Curated templates for structured AI interactions (reports, emails, analysis)
|
|
15
|
+
- **Guidelines** — Reference standards and style guides to steer AI output quality
|
|
16
|
+
|
|
17
|
+
## Document Conversion
|
|
18
|
+
|
|
19
|
+
### Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Run directly with uvx (no install needed)
|
|
23
|
+
uvx synthkit html document.md
|
|
24
|
+
|
|
25
|
+
# Or install globally
|
|
26
|
+
uv tool install synthkit
|
|
27
|
+
|
|
28
|
+
# Or install with pip
|
|
29
|
+
pip install synthkit
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Pandoc is bundled automatically via [`pypandoc_binary`](https://pypi.org/project/pypandoc-binary/) — no separate install needed.
|
|
33
|
+
|
|
34
|
+
#### System dependencies for PDF
|
|
35
|
+
|
|
36
|
+
PDF conversion uses [WeasyPrint](https://weasyprint.org/), which requires system libraries:
|
|
37
|
+
|
|
38
|
+
| Platform | Install command |
|
|
39
|
+
|----------|----------------|
|
|
40
|
+
| **macOS** | `brew install pango` |
|
|
41
|
+
| **Ubuntu/Debian** | `apt install libpango1.0-dev libcairo2-dev libgdk-pixbuf2.0-dev` |
|
|
42
|
+
| **Windows** | See [WeasyPrint docs](https://doc.courtbouillon.org/weasyprint/stable/first_steps.html#windows) |
|
|
43
|
+
|
|
44
|
+
`doc`, `html`, and `email` commands work without these.
|
|
45
|
+
|
|
46
|
+
### Usage
|
|
47
|
+
|
|
48
|
+
#### Unified CLI
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
synthkit doc report.md # → report.docx
|
|
52
|
+
synthkit html report.md # → report.html
|
|
53
|
+
synthkit pdf report.md # → report.pdf
|
|
54
|
+
synthkit email report.md # → clipboard
|
|
55
|
+
|
|
56
|
+
# Batch processing
|
|
57
|
+
synthkit doc *.md
|
|
58
|
+
synthkit html *.md --hard-breaks
|
|
59
|
+
|
|
60
|
+
# Mermaid diagrams (requires mermaid-filter)
|
|
61
|
+
synthkit html report.md --mermaid
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
#### Backward-compatible commands
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
md2doc report.md
|
|
68
|
+
md2html report.md
|
|
69
|
+
md2pdf report.md
|
|
70
|
+
md2email report.md
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
#### Options
|
|
74
|
+
|
|
75
|
+
| Flag | Description |
|
|
76
|
+
|------|-------------|
|
|
77
|
+
| `--hard-breaks` | Preserve line breaks from source markdown |
|
|
78
|
+
| `--mermaid` | Enable Mermaid diagram rendering (requires [`mermaid-filter`](https://github.com/raghur/mermaid-filter)) |
|
|
79
|
+
|
|
80
|
+
### Configuration
|
|
81
|
+
|
|
82
|
+
Each converter looks for optional config files under `~/.config/<toolname>/`:
|
|
83
|
+
|
|
84
|
+
| Converter | Config Files |
|
|
85
|
+
|-----------|-------------|
|
|
86
|
+
| `doc` | `~/.config/md2doc/reference.docx` |
|
|
87
|
+
| `email` | `~/.config/md2email/style.css` |
|
|
88
|
+
| `html` | `~/.config/md2html/style.css` |
|
|
89
|
+
| `pdf` | `~/.config/md2pdf/style.css` |
|
|
90
|
+
|
|
91
|
+
## Prompt Templates
|
|
92
|
+
|
|
93
|
+
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.
|
|
94
|
+
|
|
95
|
+
Copy the contents of any template into your LLM of choice (Claude, Gemini, ChatGPT, etc.) to get consistently structured output ready for conversion.
|
|
96
|
+
|
|
97
|
+
## Guidelines
|
|
98
|
+
|
|
99
|
+
The `guidelines/` directory contains reference standards and style guides that can be provided as context to AI models to steer output quality and consistency.
|
|
100
|
+
|
|
101
|
+
## Testing
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
# Run tests
|
|
105
|
+
uv run --extra dev pytest
|
|
106
|
+
|
|
107
|
+
# With coverage
|
|
108
|
+
uv run --extra dev pytest --cov=synthkit --cov-report=term-missing
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Tests run automatically on push/PR to `main` across Python 3.10-3.13 on Linux, macOS, and Windows.
|
|
112
|
+
|
|
113
|
+
## Repository Structure
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
├── .github/workflows/
|
|
117
|
+
│ ├── tests.yml # CI: test on push/PR to main
|
|
118
|
+
│ └── publish.yml # CD: publish to PyPI on release
|
|
119
|
+
├── pyproject.toml
|
|
120
|
+
├── src/synthkit/ # Python package
|
|
121
|
+
│ ├── cli.py # Click CLI with subcommands
|
|
122
|
+
│ ├── base.py # Shared conversion logic
|
|
123
|
+
│ ├── doc.py # Word conversion
|
|
124
|
+
│ ├── email.py # Email clipboard conversion
|
|
125
|
+
│ ├── html.py # HTML conversion
|
|
126
|
+
│ └── pdf.py # PDF conversion (via WeasyPrint)
|
|
127
|
+
├── tests/ # Test suite (pytest)
|
|
128
|
+
│ ├── conftest.py # Shared fixtures
|
|
129
|
+
│ ├── test_base.py # Base module tests
|
|
130
|
+
│ ├── test_cli.py # CLI + integration tests
|
|
131
|
+
│ ├── test_doc.py # Word converter tests
|
|
132
|
+
│ ├── test_email.py # Email converter tests
|
|
133
|
+
│ ├── test_html.py # HTML converter tests
|
|
134
|
+
│ └── test_pdf.py # PDF converter tests
|
|
135
|
+
├── style.css # Default stylesheet
|
|
136
|
+
├── prompt-templates/ # AI interaction prompt templates
|
|
137
|
+
└── guidelines/ # Reference guidelines
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Dependencies
|
|
141
|
+
|
|
142
|
+
| Package | Purpose | Bundled? |
|
|
143
|
+
|---------|---------|----------|
|
|
144
|
+
| [`click`](https://click.palletsprojects.com/) | CLI framework | pip |
|
|
145
|
+
| [`pypandoc_binary`](https://pypi.org/project/pypandoc-binary/) | Pandoc document converter | pip (includes binary) |
|
|
146
|
+
| [`pyperclip`](https://pypi.org/project/pyperclip/) | Cross-platform clipboard | pip |
|
|
147
|
+
| [`weasyprint`](https://weasyprint.org/) | PDF engine | pip (needs system libs) |
|
|
148
|
+
| [`mermaid-filter`](https://github.com/raghur/mermaid-filter) | Mermaid diagrams | Optional, external |
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "synthkit"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Convert AI-generated Markdown into production-ready documents"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
dependencies = [
|
|
13
|
+
"click",
|
|
14
|
+
"pypandoc_binary",
|
|
15
|
+
"pyperclip",
|
|
16
|
+
"weasyprint",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[project.scripts]
|
|
20
|
+
synthkit = "synthkit.cli:main"
|
|
21
|
+
md2doc = "synthkit.cli:md2doc_cmd"
|
|
22
|
+
md2email = "synthkit.cli:md2email_cmd"
|
|
23
|
+
md2html = "synthkit.cli:md2html_cmd"
|
|
24
|
+
md2pdf = "synthkit.cli:md2pdf_cmd"
|
|
25
|
+
|
|
26
|
+
[project.optional-dependencies]
|
|
27
|
+
dev = ["pytest", "pytest-cov", "ruff", "mypy"]
|
|
28
|
+
|
|
29
|
+
[tool.pytest.ini_options]
|
|
30
|
+
testpaths = ["tests"]
|
|
31
|
+
|
|
32
|
+
[tool.ruff]
|
|
33
|
+
target-version = "py310"
|
|
34
|
+
src = ["src"]
|
|
35
|
+
line-length = 100
|
|
36
|
+
|
|
37
|
+
[tool.ruff.lint]
|
|
38
|
+
select = ["E", "F", "I", "UP"]
|
|
39
|
+
|
|
40
|
+
[tool.mypy]
|
|
41
|
+
python_version = "3.10"
|
|
42
|
+
mypy_path = "src"
|
|
43
|
+
packages = ["synthkit"]
|
|
44
|
+
strict = false
|
|
45
|
+
warn_return_any = true
|
|
46
|
+
warn_unused_configs = true
|
|
47
|
+
disallow_untyped_defs = true
|
|
48
|
+
|
|
49
|
+
[[tool.mypy.overrides]]
|
|
50
|
+
module = ["pypandoc", "pyperclip"]
|
|
51
|
+
ignore_missing_imports = true
|
|
52
|
+
|
|
53
|
+
[tool.hatch.build.targets.wheel]
|
|
54
|
+
packages = ["src/synthkit"]
|
|
@@ -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
|