meltify 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.
- meltify-0.1.0/LICENSE +21 -0
- meltify-0.1.0/PKG-INFO +142 -0
- meltify-0.1.0/README.md +112 -0
- meltify-0.1.0/pyproject.toml +79 -0
- meltify-0.1.0/pyproject.toml.orig +56 -0
- meltify-0.1.0/src/meltify/__init__.py +6 -0
- meltify-0.1.0/src/meltify/__main__.py +5 -0
- meltify-0.1.0/src/meltify/cli.py +99 -0
- meltify-0.1.0/src/meltify/commands/__init__.py +0 -0
- meltify-0.1.0/src/meltify/commands/brief.py +181 -0
- meltify-0.1.0/src/meltify/commands/check.py +305 -0
- meltify-0.1.0/src/meltify/commands/doctor.py +255 -0
- meltify-0.1.0/src/meltify/commands/hidden.py +71 -0
- meltify-0.1.0/src/meltify/commands/media.py +230 -0
- meltify-0.1.0/src/meltify/commands/ocr.py +206 -0
- meltify-0.1.0/src/meltify/commands/read.py +134 -0
- meltify-0.1.0/src/meltify/commands/submit.py +344 -0
- meltify-0.1.0/src/meltify/config.py +140 -0
- meltify-0.1.0/src/meltify/converters/__init__.py +82 -0
- meltify-0.1.0/src/meltify/converters/mail.py +49 -0
- meltify-0.1.0/src/meltify/converters/office.py +17 -0
- meltify-0.1.0/src/meltify/converters/pdf.py +32 -0
- meltify-0.1.0/src/meltify/converters/sheet.py +53 -0
- meltify-0.1.0/src/meltify/converters/text.py +42 -0
- meltify-0.1.0/src/meltify/data/__init__.py +0 -0
- meltify-0.1.0/src/meltify/data/brief_en.md +21 -0
- meltify-0.1.0/src/meltify/data/brief_ko.md +21 -0
- meltify-0.1.0/src/meltify/data/defaults.toml +61 -0
- meltify-0.1.0/src/meltify/engines/__init__.py +0 -0
- meltify-0.1.0/src/meltify/engines/asr.py +148 -0
- meltify-0.1.0/src/meltify/engines/consensus.py +60 -0
- meltify-0.1.0/src/meltify/engines/ocr.py +177 -0
- meltify-0.1.0/src/meltify/evidence.py +114 -0
- meltify-0.1.0/src/meltify/ffmpeg.py +114 -0
- meltify-0.1.0/src/meltify/files.py +90 -0
- meltify-0.1.0/src/meltify/forensics/__init__.py +0 -0
- meltify-0.1.0/src/meltify/forensics/pdf.py +120 -0
- meltify-0.1.0/src/meltify/imaging.py +94 -0
- meltify-0.1.0/src/meltify/lang.py +36 -0
- meltify-0.1.0/src/meltify/llm.py +117 -0
- meltify-0.1.0/src/meltify/output.py +90 -0
- meltify-0.1.0/src/meltify/safe.py +56 -0
meltify-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 jeon-jihyeon
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
meltify-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: meltify
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Melt files of any format into prompt-ready text where every line cites its source
|
|
5
|
+
Keywords: llm,prompt,ocr,pdf,transcription,claude-code,agent-skills
|
|
6
|
+
Author: jeon-jihyeon
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: pymupdf>=1.26
|
|
10
|
+
Requires-Dist: openpyxl>=3.1
|
|
11
|
+
Requires-Dist: httpx>=0.27
|
|
12
|
+
Requires-Dist: jsonschema>=4.23
|
|
13
|
+
Requires-Dist: pillow>=11
|
|
14
|
+
Requires-Dist: numpy>=2
|
|
15
|
+
Requires-Dist: ocrmac>=1.0.1 ; sys_platform == 'darwin'
|
|
16
|
+
Requires-Dist: meltify[office,media,asr-mlx] ; extra == 'all'
|
|
17
|
+
Requires-Dist: mlx-whisper>=0.4 ; platform_machine == 'arm64' and sys_platform == 'darwin' and extra == 'asr-mlx'
|
|
18
|
+
Requires-Dist: yt-dlp>=2026.8.19 ; extra == 'media'
|
|
19
|
+
Requires-Dist: paddleocr>=3.2 ; extra == 'ocr-paddle'
|
|
20
|
+
Requires-Dist: paddlepaddle>=3.2 ; extra == 'ocr-paddle'
|
|
21
|
+
Requires-Dist: markitdown[docx,pptx,outlook]>=0.1.8 ; extra == 'office'
|
|
22
|
+
Requires-Python: >=3.11
|
|
23
|
+
Project-URL: Repository, https://github.com/jeon-jihyeon/meltify
|
|
24
|
+
Provides-Extra: all
|
|
25
|
+
Provides-Extra: asr-mlx
|
|
26
|
+
Provides-Extra: media
|
|
27
|
+
Provides-Extra: ocr-paddle
|
|
28
|
+
Provides-Extra: office
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
<h1 align="center">meltify</h1>
|
|
32
|
+
|
|
33
|
+
<p align="center">Melt files of any format into prompt-ready text where every line cites its source</p>
|
|
34
|
+
|
|
35
|
+
<p align="center">
|
|
36
|
+
<a href="https://github.com/jeon-jihyeon/meltify/actions/workflows/test.yml"><img alt="test" src="https://github.com/jeon-jihyeon/meltify/actions/workflows/test.yml/badge.svg"></a>
|
|
37
|
+
<a href="LICENSE"><img alt="MIT" src="https://img.shields.io/badge/license-MIT-blue"></a>
|
|
38
|
+
</p>
|
|
39
|
+
|
|
40
|
+
Agents misread blurry digits, miss text a PDF hides, can't watch video, and paraphrase the one condition that mattered. meltify turns images, PDFs, office files, mail, video and audio into markdown an agent can quote, puts a citation on every block, and checks answers before they go out.
|
|
41
|
+
|
|
42
|
+
## Quickstart
|
|
43
|
+
|
|
44
|
+
Claude Code:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
/plugin marketplace add jeon-jihyeon/meltify
|
|
48
|
+
/plugin install meltify@meltify
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Codex, Gemini CLI, Cursor and other agents that read Agent Skills:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
npx skills add jeon-jihyeon/meltify
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Command line only:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
uvx meltify read ./inputs
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The skills call the `meltify` command. If it isn't installed, the plugin launcher runs it through `uvx`, so [uv](https://docs.astral.sh/uv/) is the only thing you need.
|
|
64
|
+
|
|
65
|
+
## What it does
|
|
66
|
+
|
|
67
|
+
| Command | Input | Output |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| `read` | files and folders of mixed formats | one cited markdown file per item, plus a list of what still needs OCR, transcription or a hidden-text check |
|
|
70
|
+
| `ocr` | images and scanned PDF pages | each engine's lines with positions, and the values the engines disagree on |
|
|
71
|
+
| `hidden` | PDFs | text a reader doesn't see and why, with page and position |
|
|
72
|
+
| `media` | video, audio or a video URL | timestamped full-resolution frames and a timestamped transcript |
|
|
73
|
+
| `submit` | candidate files and a scoring endpoint | rate-limited submissions that skip duplicates and keep the best score |
|
|
74
|
+
| `check` | an answer file or a string | violations of a schema, counts, unique keys and text rules |
|
|
75
|
+
| `brief` | a problem statement | every condition line quoted with its line number, plus a board for juggling several problems |
|
|
76
|
+
| `doctor` | nothing | which engines, binaries and keys are available, and how to add the rest |
|
|
77
|
+
|
|
78
|
+
Each command has a skill of the same name, such as `meltify-ocr`, that tells the agent when to run it and what to do with the result.
|
|
79
|
+
|
|
80
|
+
## Citations
|
|
81
|
+
|
|
82
|
+
Every result carries `src` and a one-line `cite`:
|
|
83
|
+
|
|
84
|
+
| Input | Cite |
|
|
85
|
+
|---|---|
|
|
86
|
+
| PDF text | `report.pdf#p3@pt(72,95,140,103)` |
|
|
87
|
+
| Image region | `menu.png@px(120,40,380,72)` |
|
|
88
|
+
| Spreadsheet cell | `calendar.xlsx#Calendar!B7` |
|
|
89
|
+
| Mail attachment | `handover.eml#att=cal.xlsx#Calendar` |
|
|
90
|
+
| Text line | `notes.txt:42` |
|
|
91
|
+
| Media span | `call.m4a@00:01:23.4-00:01:27.0` |
|
|
92
|
+
| JSON value | `answers.json#$[3].reason` |
|
|
93
|
+
|
|
94
|
+
Every command prints a table by default, and the full result with `--json`. Exit codes:
|
|
95
|
+
|
|
96
|
+
- `0`: success
|
|
97
|
+
- `1`: violations, failed checks or other errors
|
|
98
|
+
- `2`: usage error or bad input
|
|
99
|
+
- `3`: a needed engine, key, binary or base module is missing
|
|
100
|
+
|
|
101
|
+
## Engines
|
|
102
|
+
|
|
103
|
+
| Task | Local | Paid API |
|
|
104
|
+
|---|---|---|
|
|
105
|
+
| OCR | Apple Vision on macOS, PaddleOCR | Gemini, Claude, any OpenAI-compatible endpoint |
|
|
106
|
+
| Speech | MLX Whisper on Apple Silicon, whisper.cpp | any OpenAI-compatible transcription endpoint |
|
|
107
|
+
|
|
108
|
+
`auto` uses local engines only. Paid engines run only when you name them in `meltify.toml` or on the command line. An agent can also feed in what it reads in an image with `meltify ocr --reading agent=FILE`, so its own reading gets cross-checked against the local engines without any API key.
|
|
109
|
+
|
|
110
|
+
Optional parts install on request:
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
meltify doctor --install office # docx, pptx and Outlook msg
|
|
114
|
+
meltify doctor --install media # video URLs through yt-dlp
|
|
115
|
+
meltify doctor --install asr-mlx # local speech on Apple Silicon
|
|
116
|
+
meltify doctor --install ocr-paddle # PaddleOCR
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`media` needs ffmpeg, and YouTube downloads need deno.
|
|
120
|
+
|
|
121
|
+
## Configuration
|
|
122
|
+
|
|
123
|
+
Settings are layered, lowest precedence first:
|
|
124
|
+
|
|
125
|
+
1. The packaged defaults
|
|
126
|
+
2. `$XDG_CONFIG_HOME/meltify/config.toml` (usually `~/.config/meltify/config.toml`)
|
|
127
|
+
3. The nearest `meltify.toml`, searched from the working directory up to the git root, or the file you pass with `--config`. Outside a git repository, only the working directory is searched
|
|
128
|
+
4. `MELTIFY_<KEY>` and `MELTIFY_<SECTION>_<KEY>` environment variables
|
|
129
|
+
5. Command-line flags
|
|
130
|
+
|
|
131
|
+
See [examples/meltify.toml](examples/meltify.toml) for a sample.
|
|
132
|
+
|
|
133
|
+
## Limits
|
|
134
|
+
|
|
135
|
+
- Text drawn inside an image is just pixels, so `hidden` finds it only through `--contrast` rendering and OCR
|
|
136
|
+
- OCR agreement means the engines read the same value, not that the value is right. A value read only by LLM engines is never marked agreed
|
|
137
|
+
- Speech recognition and OCR quality depend on the engine and the input
|
|
138
|
+
- The plugin launcher is a POSIX shell script, so Windows isn't supported
|
|
139
|
+
|
|
140
|
+
## License
|
|
141
|
+
|
|
142
|
+
MIT. meltify depends on [PyMuPDF](https://github.com/pymupdf/PyMuPDF), which is AGPL-3.0, so distributing a product that bundles it comes with AGPL obligations.
|
meltify-0.1.0/README.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
<h1 align="center">meltify</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center">Melt files of any format into prompt-ready text where every line cites its source</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://github.com/jeon-jihyeon/meltify/actions/workflows/test.yml"><img alt="test" src="https://github.com/jeon-jihyeon/meltify/actions/workflows/test.yml/badge.svg"></a>
|
|
7
|
+
<a href="LICENSE"><img alt="MIT" src="https://img.shields.io/badge/license-MIT-blue"></a>
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
Agents misread blurry digits, miss text a PDF hides, can't watch video, and paraphrase the one condition that mattered. meltify turns images, PDFs, office files, mail, video and audio into markdown an agent can quote, puts a citation on every block, and checks answers before they go out.
|
|
11
|
+
|
|
12
|
+
## Quickstart
|
|
13
|
+
|
|
14
|
+
Claude Code:
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
/plugin marketplace add jeon-jihyeon/meltify
|
|
18
|
+
/plugin install meltify@meltify
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Codex, Gemini CLI, Cursor and other agents that read Agent Skills:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
npx skills add jeon-jihyeon/meltify
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Command line only:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
uvx meltify read ./inputs
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The skills call the `meltify` command. If it isn't installed, the plugin launcher runs it through `uvx`, so [uv](https://docs.astral.sh/uv/) is the only thing you need.
|
|
34
|
+
|
|
35
|
+
## What it does
|
|
36
|
+
|
|
37
|
+
| Command | Input | Output |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| `read` | files and folders of mixed formats | one cited markdown file per item, plus a list of what still needs OCR, transcription or a hidden-text check |
|
|
40
|
+
| `ocr` | images and scanned PDF pages | each engine's lines with positions, and the values the engines disagree on |
|
|
41
|
+
| `hidden` | PDFs | text a reader doesn't see and why, with page and position |
|
|
42
|
+
| `media` | video, audio or a video URL | timestamped full-resolution frames and a timestamped transcript |
|
|
43
|
+
| `submit` | candidate files and a scoring endpoint | rate-limited submissions that skip duplicates and keep the best score |
|
|
44
|
+
| `check` | an answer file or a string | violations of a schema, counts, unique keys and text rules |
|
|
45
|
+
| `brief` | a problem statement | every condition line quoted with its line number, plus a board for juggling several problems |
|
|
46
|
+
| `doctor` | nothing | which engines, binaries and keys are available, and how to add the rest |
|
|
47
|
+
|
|
48
|
+
Each command has a skill of the same name, such as `meltify-ocr`, that tells the agent when to run it and what to do with the result.
|
|
49
|
+
|
|
50
|
+
## Citations
|
|
51
|
+
|
|
52
|
+
Every result carries `src` and a one-line `cite`:
|
|
53
|
+
|
|
54
|
+
| Input | Cite |
|
|
55
|
+
|---|---|
|
|
56
|
+
| PDF text | `report.pdf#p3@pt(72,95,140,103)` |
|
|
57
|
+
| Image region | `menu.png@px(120,40,380,72)` |
|
|
58
|
+
| Spreadsheet cell | `calendar.xlsx#Calendar!B7` |
|
|
59
|
+
| Mail attachment | `handover.eml#att=cal.xlsx#Calendar` |
|
|
60
|
+
| Text line | `notes.txt:42` |
|
|
61
|
+
| Media span | `call.m4a@00:01:23.4-00:01:27.0` |
|
|
62
|
+
| JSON value | `answers.json#$[3].reason` |
|
|
63
|
+
|
|
64
|
+
Every command prints a table by default, and the full result with `--json`. Exit codes:
|
|
65
|
+
|
|
66
|
+
- `0`: success
|
|
67
|
+
- `1`: violations, failed checks or other errors
|
|
68
|
+
- `2`: usage error or bad input
|
|
69
|
+
- `3`: a needed engine, key, binary or base module is missing
|
|
70
|
+
|
|
71
|
+
## Engines
|
|
72
|
+
|
|
73
|
+
| Task | Local | Paid API |
|
|
74
|
+
|---|---|---|
|
|
75
|
+
| OCR | Apple Vision on macOS, PaddleOCR | Gemini, Claude, any OpenAI-compatible endpoint |
|
|
76
|
+
| Speech | MLX Whisper on Apple Silicon, whisper.cpp | any OpenAI-compatible transcription endpoint |
|
|
77
|
+
|
|
78
|
+
`auto` uses local engines only. Paid engines run only when you name them in `meltify.toml` or on the command line. An agent can also feed in what it reads in an image with `meltify ocr --reading agent=FILE`, so its own reading gets cross-checked against the local engines without any API key.
|
|
79
|
+
|
|
80
|
+
Optional parts install on request:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
meltify doctor --install office # docx, pptx and Outlook msg
|
|
84
|
+
meltify doctor --install media # video URLs through yt-dlp
|
|
85
|
+
meltify doctor --install asr-mlx # local speech on Apple Silicon
|
|
86
|
+
meltify doctor --install ocr-paddle # PaddleOCR
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`media` needs ffmpeg, and YouTube downloads need deno.
|
|
90
|
+
|
|
91
|
+
## Configuration
|
|
92
|
+
|
|
93
|
+
Settings are layered, lowest precedence first:
|
|
94
|
+
|
|
95
|
+
1. The packaged defaults
|
|
96
|
+
2. `$XDG_CONFIG_HOME/meltify/config.toml` (usually `~/.config/meltify/config.toml`)
|
|
97
|
+
3. The nearest `meltify.toml`, searched from the working directory up to the git root, or the file you pass with `--config`. Outside a git repository, only the working directory is searched
|
|
98
|
+
4. `MELTIFY_<KEY>` and `MELTIFY_<SECTION>_<KEY>` environment variables
|
|
99
|
+
5. Command-line flags
|
|
100
|
+
|
|
101
|
+
See [examples/meltify.toml](examples/meltify.toml) for a sample.
|
|
102
|
+
|
|
103
|
+
## Limits
|
|
104
|
+
|
|
105
|
+
- Text drawn inside an image is just pixels, so `hidden` finds it only through `--contrast` rendering and OCR
|
|
106
|
+
- OCR agreement means the engines read the same value, not that the value is right. A value read only by LLM engines is never marked agreed
|
|
107
|
+
- Speech recognition and OCR quality depend on the engine and the input
|
|
108
|
+
- The plugin launcher is a POSIX shell script, so Windows isn't supported
|
|
109
|
+
|
|
110
|
+
## License
|
|
111
|
+
|
|
112
|
+
MIT. meltify depends on [PyMuPDF](https://github.com/pymupdf/PyMuPDF), which is AGPL-3.0, so distributing a product that bundles it comes with AGPL obligations.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "meltify"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Melt files of any format into prompt-ready text where every line cites its source"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
license-files = ["LICENSE"]
|
|
9
|
+
keywords = [
|
|
10
|
+
"llm",
|
|
11
|
+
"prompt",
|
|
12
|
+
"ocr",
|
|
13
|
+
"pdf",
|
|
14
|
+
"transcription",
|
|
15
|
+
"claude-code",
|
|
16
|
+
"agent-skills",
|
|
17
|
+
]
|
|
18
|
+
dependencies = [
|
|
19
|
+
"pymupdf>=1.26",
|
|
20
|
+
"openpyxl>=3.1",
|
|
21
|
+
"httpx>=0.27",
|
|
22
|
+
"jsonschema>=4.23",
|
|
23
|
+
"pillow>=11",
|
|
24
|
+
"numpy>=2",
|
|
25
|
+
"ocrmac>=1.0.1; sys_platform == 'darwin'",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[[project.authors]]
|
|
29
|
+
name = "jeon-jihyeon"
|
|
30
|
+
|
|
31
|
+
[project.optional-dependencies]
|
|
32
|
+
office = ["markitdown[docx,pptx,outlook]>=0.1.8"]
|
|
33
|
+
media = ["yt-dlp>=2026.8.19"]
|
|
34
|
+
asr-mlx = ["mlx-whisper>=0.4; sys_platform == 'darwin' and platform_machine == 'arm64'"]
|
|
35
|
+
ocr-paddle = [
|
|
36
|
+
"paddleocr>=3.2",
|
|
37
|
+
"paddlepaddle>=3.2",
|
|
38
|
+
]
|
|
39
|
+
all = ["meltify[office,media,asr-mlx]"]
|
|
40
|
+
|
|
41
|
+
[project.urls]
|
|
42
|
+
Repository = "https://github.com/jeon-jihyeon/meltify"
|
|
43
|
+
|
|
44
|
+
[project.scripts]
|
|
45
|
+
meltify = "meltify.cli:main"
|
|
46
|
+
|
|
47
|
+
[build-system]
|
|
48
|
+
requires = ["uv_build>=0.12,<0.13"]
|
|
49
|
+
build-backend = "uv_build"
|
|
50
|
+
|
|
51
|
+
[dependency-groups]
|
|
52
|
+
dev = [
|
|
53
|
+
"pytest>=8",
|
|
54
|
+
"ruff>=0.6",
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
[tool.ruff]
|
|
58
|
+
line-length = 100
|
|
59
|
+
target-version = "py311"
|
|
60
|
+
|
|
61
|
+
[tool.ruff.lint]
|
|
62
|
+
select = [
|
|
63
|
+
"E",
|
|
64
|
+
"F",
|
|
65
|
+
"I",
|
|
66
|
+
"UP",
|
|
67
|
+
"B",
|
|
68
|
+
"SIM",
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
[tool.pytest.ini_options]
|
|
72
|
+
testpaths = ["tests"]
|
|
73
|
+
pythonpath = ["."]
|
|
74
|
+
filterwarnings = ["ignore:builtin type .* has no __module__ attribute:DeprecationWarning"]
|
|
75
|
+
markers = [
|
|
76
|
+
"llm: calls a paid LLM API and runs only with MELTIFY_TEST_LLM=1",
|
|
77
|
+
"heavy: needs a large model download",
|
|
78
|
+
"macos: needs macOS frameworks",
|
|
79
|
+
]
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "meltify"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Melt files of any format into prompt-ready text where every line cites its source"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
license-files = ["LICENSE"]
|
|
9
|
+
authors = [{ name = "jeon-jihyeon" }]
|
|
10
|
+
keywords = ["llm", "prompt", "ocr", "pdf", "transcription", "claude-code", "agent-skills"]
|
|
11
|
+
dependencies = [
|
|
12
|
+
"pymupdf>=1.26",
|
|
13
|
+
"openpyxl>=3.1",
|
|
14
|
+
"httpx>=0.27",
|
|
15
|
+
"jsonschema>=4.23",
|
|
16
|
+
"pillow>=11",
|
|
17
|
+
"numpy>=2",
|
|
18
|
+
"ocrmac>=1.0.1; sys_platform == 'darwin'",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
[project.optional-dependencies]
|
|
22
|
+
office = ["markitdown[docx,pptx,outlook]>=0.1.8"]
|
|
23
|
+
media = ["yt-dlp>=2026.8.19"]
|
|
24
|
+
asr-mlx = ["mlx-whisper>=0.4; sys_platform == 'darwin' and platform_machine == 'arm64'"]
|
|
25
|
+
ocr-paddle = ["paddleocr>=3.2", "paddlepaddle>=3.2"]
|
|
26
|
+
all = ["meltify[office,media,asr-mlx]"]
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Repository = "https://github.com/jeon-jihyeon/meltify"
|
|
30
|
+
|
|
31
|
+
[project.scripts]
|
|
32
|
+
meltify = "meltify.cli:main"
|
|
33
|
+
|
|
34
|
+
[build-system]
|
|
35
|
+
requires = ["uv_build>=0.12,<0.13"]
|
|
36
|
+
build-backend = "uv_build"
|
|
37
|
+
|
|
38
|
+
[dependency-groups]
|
|
39
|
+
dev = ["pytest>=8", "ruff>=0.6"]
|
|
40
|
+
|
|
41
|
+
[tool.ruff]
|
|
42
|
+
line-length = 100
|
|
43
|
+
target-version = "py311"
|
|
44
|
+
|
|
45
|
+
[tool.ruff.lint]
|
|
46
|
+
select = ["E", "F", "I", "UP", "B", "SIM"]
|
|
47
|
+
|
|
48
|
+
[tool.pytest.ini_options]
|
|
49
|
+
testpaths = ["tests"]
|
|
50
|
+
pythonpath = ["."]
|
|
51
|
+
filterwarnings = ["ignore:builtin type .* has no __module__ attribute:DeprecationWarning"]
|
|
52
|
+
markers = [
|
|
53
|
+
"llm: calls a paid LLM API and runs only with MELTIFY_TEST_LLM=1",
|
|
54
|
+
"heavy: needs a large model download",
|
|
55
|
+
"macos: needs macOS frameworks",
|
|
56
|
+
]
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import importlib
|
|
5
|
+
import sys
|
|
6
|
+
from collections.abc import Sequence
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from types import ModuleType
|
|
9
|
+
|
|
10
|
+
from meltify import __version__, config
|
|
11
|
+
from meltify.evidence import MISSING, USAGE, Envelope
|
|
12
|
+
from meltify.output import emit
|
|
13
|
+
from meltify.safe import MissingTool
|
|
14
|
+
|
|
15
|
+
# Each module exposes NAME, HELP, COLUMNS, add_arguments and run.
|
|
16
|
+
# Heavy imports live inside run, so --help and --version start fast
|
|
17
|
+
COMMANDS: list[str] = ["read", "ocr", "hidden", "media", "submit", "check", "brief", "doctor"]
|
|
18
|
+
|
|
19
|
+
USAGE_ERROR = 2
|
|
20
|
+
|
|
21
|
+
EXIT_CODES = """exit codes:
|
|
22
|
+
0 success
|
|
23
|
+
1 violations, failed checks or other errors
|
|
24
|
+
2 usage error or bad input
|
|
25
|
+
3 a needed engine, key, binary or base module is missing"""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _modules() -> list[ModuleType]:
|
|
29
|
+
return [importlib.import_module(f"meltify.commands.{name}") for name in COMMANDS]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _limit(raw: str) -> int:
|
|
33
|
+
n = int(raw)
|
|
34
|
+
if n < 0:
|
|
35
|
+
raise argparse.ArgumentTypeError(f"must be 0 or more, got {n}")
|
|
36
|
+
return n
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def build_parser(modules: Sequence[ModuleType]) -> argparse.ArgumentParser:
|
|
40
|
+
common = argparse.ArgumentParser(add_help=False)
|
|
41
|
+
common.add_argument("--json", action="store_true", help="print the full result envelope")
|
|
42
|
+
common.add_argument("--out", type=Path, help="output directory for artifacts")
|
|
43
|
+
common.add_argument("--config", type=Path, help="project config file instead of meltify.toml")
|
|
44
|
+
common.add_argument(
|
|
45
|
+
"--limit", type=_limit, default=50, help="rows shown in the table, 0 for all"
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
parser = argparse.ArgumentParser(
|
|
49
|
+
prog="meltify",
|
|
50
|
+
description="Melt files of any format into prompt-ready text, with a citation per item",
|
|
51
|
+
epilog=EXIT_CODES,
|
|
52
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
53
|
+
)
|
|
54
|
+
parser.add_argument("--version", action="version", version=f"meltify {__version__}")
|
|
55
|
+
sub = parser.add_subparsers(dest="command", metavar="COMMAND")
|
|
56
|
+
for m in modules:
|
|
57
|
+
p = sub.add_parser(
|
|
58
|
+
m.NAME,
|
|
59
|
+
help=m.HELP,
|
|
60
|
+
description=m.HELP,
|
|
61
|
+
epilog=EXIT_CODES,
|
|
62
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
63
|
+
parents=[common],
|
|
64
|
+
)
|
|
65
|
+
m.add_arguments(p)
|
|
66
|
+
p.set_defaults(_module=m)
|
|
67
|
+
return parser
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
71
|
+
modules = _modules()
|
|
72
|
+
parser = build_parser(modules)
|
|
73
|
+
args = parser.parse_args(argv)
|
|
74
|
+
if not getattr(args, "_module", None):
|
|
75
|
+
parser.print_help(sys.stderr)
|
|
76
|
+
return USAGE_ERROR
|
|
77
|
+
module = args._module
|
|
78
|
+
|
|
79
|
+
overrides = {"out_dir": str(args.out)} if args.out is not None else None
|
|
80
|
+
try:
|
|
81
|
+
settings = config.load(overrides, project=args.config)
|
|
82
|
+
except config.ConfigError as e:
|
|
83
|
+
# Report it in the envelope, so --json callers still get valid JSON
|
|
84
|
+
env = Envelope(command=module.NAME, version=__version__)
|
|
85
|
+
env.error(USAGE, f"config: {e}")
|
|
86
|
+
emit(env, as_json=args.json, columns=module.COLUMNS, limit=args.limit)
|
|
87
|
+
return env.exit_code
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
env = module.run(args, settings)
|
|
91
|
+
except MissingTool as e:
|
|
92
|
+
env = Envelope(command=module.NAME, version=__version__)
|
|
93
|
+
env.error(MISSING, str(e), e.hint)
|
|
94
|
+
except (FileNotFoundError, IsADirectoryError, ValueError) as e:
|
|
95
|
+
# Report bad input in the envelope, so --json callers still get valid JSON
|
|
96
|
+
env = Envelope(command=module.NAME, version=__version__)
|
|
97
|
+
env.error(USAGE, f"{type(e).__name__}: {e}")
|
|
98
|
+
emit(env, as_json=args.json, columns=env.columns or module.COLUMNS, limit=args.limit)
|
|
99
|
+
return env.exit_code
|
|
File without changes
|