pilepack 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.
- pilepack/__init__.py +0 -0
- pilepack/__main__.py +5 -0
- pilepack/cli.py +103 -0
- pilepack/collector.py +37 -0
- pilepack/formatter.py +68 -0
- pilepack/ignorer.py +28 -0
- pilepack/reader.py +46 -0
- pilepack-0.1.0.dist-info/METADATA +148 -0
- pilepack-0.1.0.dist-info/RECORD +13 -0
- pilepack-0.1.0.dist-info/WHEEL +5 -0
- pilepack-0.1.0.dist-info/entry_points.txt +2 -0
- pilepack-0.1.0.dist-info/licenses/LICENSE +19 -0
- pilepack-0.1.0.dist-info/top_level.txt +1 -0
pilepack/__init__.py
ADDED
|
File without changes
|
pilepack/__main__.py
ADDED
pilepack/cli.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import sys
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from .collector import collect_files, build_tree
|
|
6
|
+
from .reader import read_file
|
|
7
|
+
from .formatter import render
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _generate_report(root_path: Path, include_content=True, mask_secrets=False, follow_gitignore=True, fmt="txt"):
|
|
11
|
+
print("Scanning files...", file=sys.stderr, flush=True)
|
|
12
|
+
files = collect_files(root_path, follow_gitignore=follow_gitignore)
|
|
13
|
+
tree = build_tree(files)
|
|
14
|
+
|
|
15
|
+
files_content = []
|
|
16
|
+
if include_content:
|
|
17
|
+
total = len(files)
|
|
18
|
+
print(f"Reading {total} files...", file=sys.stderr, flush=True)
|
|
19
|
+
for rel_path in files:
|
|
20
|
+
abs_path = root_path / rel_path
|
|
21
|
+
content = read_file(abs_path, mask_secrets=mask_secrets)
|
|
22
|
+
files_content.append((rel_path, content))
|
|
23
|
+
print(f"Done. Read {total} files.", file=sys.stderr, flush=True)
|
|
24
|
+
else:
|
|
25
|
+
files_content = []
|
|
26
|
+
|
|
27
|
+
root_name = root_path.name or str(root_path)
|
|
28
|
+
report = render(root_name, tree, files_content, fmt=fmt)
|
|
29
|
+
return report
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def main():
|
|
33
|
+
parser = argparse.ArgumentParser(
|
|
34
|
+
description="Pack your codebase into a single file for AI analysis"
|
|
35
|
+
)
|
|
36
|
+
parser.add_argument(
|
|
37
|
+
"root",
|
|
38
|
+
nargs="?",
|
|
39
|
+
default=".",
|
|
40
|
+
help="Root directory to scan (default: current directory)",
|
|
41
|
+
)
|
|
42
|
+
parser.add_argument(
|
|
43
|
+
"--no-content",
|
|
44
|
+
dest="include_content",
|
|
45
|
+
action="store_false",
|
|
46
|
+
help="Do not include file contents (only show tree structure)"
|
|
47
|
+
)
|
|
48
|
+
parser.add_argument(
|
|
49
|
+
"--mask-secrets",
|
|
50
|
+
action="store_true",
|
|
51
|
+
help="Mask sensitive information like passwords, tokens, etc."
|
|
52
|
+
)
|
|
53
|
+
parser.add_argument(
|
|
54
|
+
"-o", "--output",
|
|
55
|
+
type=Path,
|
|
56
|
+
help="Write output to a file instead of stdout"
|
|
57
|
+
)
|
|
58
|
+
parser.add_argument(
|
|
59
|
+
"--no-gitignore",
|
|
60
|
+
dest="follow_gitignore",
|
|
61
|
+
action="store_false",
|
|
62
|
+
help="Do NOT respect .gitignore rules (include all files)"
|
|
63
|
+
)
|
|
64
|
+
parser.add_argument(
|
|
65
|
+
"-f", "--format",
|
|
66
|
+
choices=["txt", "md"],
|
|
67
|
+
default="txt",
|
|
68
|
+
help="Output format: txt (default) or md",
|
|
69
|
+
metavar='FMT'
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
args = parser.parse_args()
|
|
73
|
+
|
|
74
|
+
root_path = Path(args.root).resolve()
|
|
75
|
+
if not root_path.is_dir():
|
|
76
|
+
print(f"Error: '{root_path}' is not a valid directory.", file=sys.stderr)
|
|
77
|
+
sys.exit(1)
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
report = _generate_report(
|
|
81
|
+
root_path,
|
|
82
|
+
include_content=args.include_content,
|
|
83
|
+
mask_secrets=args.mask_secrets,
|
|
84
|
+
follow_gitignore=args.follow_gitignore,
|
|
85
|
+
fmt=args.format,
|
|
86
|
+
)
|
|
87
|
+
except Exception as e:
|
|
88
|
+
print(f"Error generating report: {e}", file=sys.stderr)
|
|
89
|
+
sys.exit(1)
|
|
90
|
+
|
|
91
|
+
if args.output:
|
|
92
|
+
try:
|
|
93
|
+
args.output.write_text(report, encoding='utf-8')
|
|
94
|
+
print(f"Report written to {args.output}")
|
|
95
|
+
except IOError as e:
|
|
96
|
+
print(f"Error writing to file: {e}", file=sys.stderr)
|
|
97
|
+
sys.exit(1)
|
|
98
|
+
else:
|
|
99
|
+
print(report)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
if __name__ == "__main__":
|
|
103
|
+
main()
|
pilepack/collector.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from .ignorer import load_gitignore, is_ignored
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Dict, List
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def collect_files(root_path: Path, follow_gitignore: bool = True) -> list[Path]:
|
|
7
|
+
if not root_path.is_dir():
|
|
8
|
+
raise NotADirectoryError(f"{root_path} does not exist or is not a directory")
|
|
9
|
+
|
|
10
|
+
if follow_gitignore:
|
|
11
|
+
spec = load_gitignore(root_path)
|
|
12
|
+
else:
|
|
13
|
+
spec = None
|
|
14
|
+
collected = []
|
|
15
|
+
|
|
16
|
+
for item in root_path.rglob('*'):
|
|
17
|
+
if item.name == '.gitignore':
|
|
18
|
+
continue
|
|
19
|
+
if '.git' in item.parts:
|
|
20
|
+
continue
|
|
21
|
+
if spec and is_ignored(item, root_path, spec):
|
|
22
|
+
continue
|
|
23
|
+
if item.is_file():
|
|
24
|
+
rel = item.relative_to(root_path)
|
|
25
|
+
collected.append(rel)
|
|
26
|
+
return collected
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def build_tree(files: List[Path]) -> Dict:
|
|
30
|
+
tree = {}
|
|
31
|
+
for path in files:
|
|
32
|
+
parts = path.parts
|
|
33
|
+
current = tree
|
|
34
|
+
for part in parts[:-1]:
|
|
35
|
+
current = current.setdefault(part, {})
|
|
36
|
+
current[parts[-1]] = None
|
|
37
|
+
return tree
|
pilepack/formatter.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from typing import Dict, List, Optional, Tuple
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def _format_tree(tree: Dict, prefix: str = '', is_last: bool = True) -> str:
|
|
6
|
+
lines = []
|
|
7
|
+
items = sorted(tree.items(), key=lambda x: (isinstance(x[1], dict), x[0].lower()), reverse=True)
|
|
8
|
+
|
|
9
|
+
for i, (name, subtree) in enumerate(items):
|
|
10
|
+
is_last_item = (i == len(items) - 1)
|
|
11
|
+
connector = 'โโโ ' if is_last_item else 'โโโ '
|
|
12
|
+
display_name = name + '/' if isinstance(subtree, dict) else name
|
|
13
|
+
lines.append(prefix + connector + display_name)
|
|
14
|
+
|
|
15
|
+
if isinstance(subtree, dict):
|
|
16
|
+
new_prefix = prefix + (' ' if is_last_item else 'โ ')
|
|
17
|
+
lines.append(_format_tree(subtree, new_prefix, is_last_item))
|
|
18
|
+
return '\n'.join(lines)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _render_txt(root_name: str, tree: Dict, files_content: List[Tuple[Path, Optional[str]]]) -> str:
|
|
22
|
+
tree_str = _format_tree(tree)
|
|
23
|
+
output = [f"{root_name}\n{tree_str}"]
|
|
24
|
+
|
|
25
|
+
if files_content:
|
|
26
|
+
output.append("\n" + "=" * 80 + "\n")
|
|
27
|
+
|
|
28
|
+
for rel_path, content in files_content:
|
|
29
|
+
|
|
30
|
+
if content is not None:
|
|
31
|
+
output.append(f"--- FILE: {rel_path} ---")
|
|
32
|
+
output.append(content)
|
|
33
|
+
output.append("")
|
|
34
|
+
else:
|
|
35
|
+
output.append(f"--- FILE: {rel_path} [BINARY OR UNREADABLE] ---\n")
|
|
36
|
+
return "\n".join(output)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _render_md(root_name: str, tree: Dict, files_content: List[Tuple[Path, Optional[str]]]) -> str:
|
|
40
|
+
tree_str = _format_tree(tree)
|
|
41
|
+
output = [f"# {root_name}\n```\n{tree_str}\n```"]
|
|
42
|
+
|
|
43
|
+
if files_content:
|
|
44
|
+
output.append("\n---\n")
|
|
45
|
+
|
|
46
|
+
for rel_path, content in files_content:
|
|
47
|
+
|
|
48
|
+
if content is not None:
|
|
49
|
+
ext = rel_path.suffix.lower()
|
|
50
|
+
lang = {
|
|
51
|
+
'.py': 'python', '.js': 'javascript', '.ts': 'typescript',
|
|
52
|
+
'.html': 'html', '.css': 'css', '.json': 'json',
|
|
53
|
+
'.md': 'markdown', '.yaml': 'yaml', '.yml': 'yaml',
|
|
54
|
+
'.sh': 'bash', '.txt': 'text'
|
|
55
|
+
}.get(ext, 'text')
|
|
56
|
+
output.append(f"## `{rel_path}`\n```{lang}\n{content}\n```\n")
|
|
57
|
+
else:
|
|
58
|
+
output.append(f"## `{rel_path}`\n*[BINARY OR UNREADABLE]*\n")
|
|
59
|
+
return '\n'.join(output)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def render(root_name: str, tree: Dict, files_content: List[Tuple[Path, Optional[str]]], fmt: str = "txt") -> str:
|
|
63
|
+
if fmt == "txt":
|
|
64
|
+
return _render_txt(root_name, tree, files_content)
|
|
65
|
+
elif fmt == "md":
|
|
66
|
+
return _render_md(root_name, tree, files_content)
|
|
67
|
+
else:
|
|
68
|
+
raise ValueError(f"Unsupported format: {fmt}. Supported: 'txt', 'md'")
|
pilepack/ignorer.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from pathspec import PathSpec
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def load_gitignore(root_path: Path) -> PathSpec:
|
|
6
|
+
gitignore_path = root_path / '.gitignore'
|
|
7
|
+
if not gitignore_path.exists():
|
|
8
|
+
return PathSpec.from_lines('gitignore', [])
|
|
9
|
+
with open(gitignore_path, 'r', encoding='utf-8') as file:
|
|
10
|
+
return PathSpec.from_lines('gitignore', file)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def is_ignored(path, root: Path, spec: PathSpec) -> bool:
|
|
14
|
+
original = str(path)
|
|
15
|
+
path_obj = Path(original)
|
|
16
|
+
|
|
17
|
+
if not path_obj.is_absolute():
|
|
18
|
+
path_obj = root / path_obj
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
rel_path = path_obj.relative_to(root)
|
|
22
|
+
except ValueError:
|
|
23
|
+
return False
|
|
24
|
+
rel_str = rel_path.as_posix()
|
|
25
|
+
|
|
26
|
+
if original.endswith(('/', '\\')) or (path_obj.exists() and path_obj.is_dir()):
|
|
27
|
+
rel_str += '/'
|
|
28
|
+
return spec.match_file(rel_str)
|
pilepack/reader.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
SECRET_PATTERNS = [
|
|
6
|
+
(r'(password|passwd|pwd)\s*[=:]\s*["\']?([^"\'\s]+)["\']?', r'\1="***"'),
|
|
7
|
+
(r'(api_key|apikey)\s*[=:]\s*["\']?([^"\'\s]+)["\']?', r'\1="***"'),
|
|
8
|
+
(r'(token|access_token)\s*[=:]\s*["\']?([^"\'\s]+)["\']?', r'\1="***"'),
|
|
9
|
+
(r'(secret|private_key)\s*[=:]\s*["\']?([^"\'\s]+)["\']?', r'\1="***"'),
|
|
10
|
+
(r'(?:\\b[A-Za-z0-9+/]{40,}\\b)', '***'),
|
|
11
|
+
(r'(?:\\b[0-9a-f]{32,}\\b)', '***')
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
def _mask_secrets_in_text(text: str) -> str:
|
|
15
|
+
for pattern, replacement in SECRET_PATTERNS:
|
|
16
|
+
text = re.sub(pattern, replacement, text, flags=re.IGNORECASE)
|
|
17
|
+
return text
|
|
18
|
+
|
|
19
|
+
def read_file(file_path: Path, mask_secrets: bool = False) -> Optional[str]:
|
|
20
|
+
try:
|
|
21
|
+
raw_data = file_path.read_bytes()
|
|
22
|
+
except (OSError, IOError) as e:
|
|
23
|
+
print(f"Failed to read {file_path}: {e}")
|
|
24
|
+
return None
|
|
25
|
+
|
|
26
|
+
if b'\x00' in raw_data:
|
|
27
|
+
return None
|
|
28
|
+
try:
|
|
29
|
+
text = raw_data.decode('utf-8-sig')
|
|
30
|
+
except UnicodeDecodeError:
|
|
31
|
+
for enc in ('utf-8', 'cp1251', 'latin1'):
|
|
32
|
+
try:
|
|
33
|
+
text = raw_data.decode(enc)
|
|
34
|
+
break
|
|
35
|
+
except UnicodeDecodeError:
|
|
36
|
+
continue
|
|
37
|
+
else:
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
if text.startswith('\ufeff'):
|
|
41
|
+
text = text[1:]
|
|
42
|
+
|
|
43
|
+
if mask_secrets:
|
|
44
|
+
text = _mask_secrets_in_text(text)
|
|
45
|
+
|
|
46
|
+
return text
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pilepack
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Pack your codebase into a single file for AI analysis
|
|
5
|
+
Author-email: "Vasili S. Pribylov" <dartmew@yandex.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.8
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: pathspec>=0.10.0
|
|
11
|
+
Provides-Extra: test
|
|
12
|
+
Requires-Dist: pytest>=7.0; extra == "test"
|
|
13
|
+
Requires-Dist: pytest-cov; extra == "test"
|
|
14
|
+
Dynamic: license-file
|
|
15
|
+
|
|
16
|
+
# PilePack
|
|
17
|
+
|
|
18
|
+
[](https://www.python.org/downloads/)
|
|
19
|
+
[](LICENSE)
|
|
20
|
+
[](https://pypi.org/project/pilepack/)
|
|
21
|
+
[](tests/)
|
|
22
|
+
[](tests/)
|
|
23
|
+
[]()
|
|
24
|
+
|
|
25
|
+
**Pack your codebase into a single file for AI analysis**
|
|
26
|
+
Combine all your project files into one text file โ perfect for sending to LLMs (ChatGPT, Claude, Copilot, Deepseek, etc.).
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## โจ Features
|
|
31
|
+
|
|
32
|
+
- ๐ **Recursive scanning** โ walks through all files in a directory.
|
|
33
|
+
- ๐ซ **Respects .gitignore** โ optionally disable with `--no-gitignore`.
|
|
34
|
+
- ๐ณ **Tree structure** โ displays project hierarchy.
|
|
35
|
+
- ๐ **Embedded content** โ each file is shown with its path header.
|
|
36
|
+
- ๐ **Secrets masking** โ hides passwords, tokens, keys (`--mask-secrets`).
|
|
37
|
+
- ๐จ๏ธ **Two output formats** โ plain text (`txt`) or Markdown (`md`).
|
|
38
|
+
- ๐พ **Save to file** โ use `-o output.txt`.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## ๐ฆ Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install pilepack
|
|
46
|
+
```
|
|
47
|
+
From source:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
git clone https://github.com/dartmew/pilepack.git
|
|
51
|
+
cd pilepack
|
|
52
|
+
pip install -e .
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## ๐ Usage
|
|
56
|
+
Basic command โ pass a path to your project:
|
|
57
|
+
```bash
|
|
58
|
+
pilepack /path/to/your/project
|
|
59
|
+
```
|
|
60
|
+
Redirect output to a file:
|
|
61
|
+
```bash
|
|
62
|
+
pilepack . > report.txt
|
|
63
|
+
```
|
|
64
|
+
Example output (txt)
|
|
65
|
+
```text
|
|
66
|
+
myproject
|
|
67
|
+
โโโ main.py
|
|
68
|
+
โโโ utils/
|
|
69
|
+
โ โโโ helpers.py
|
|
70
|
+
โ โโโ __init__.py
|
|
71
|
+
โโโ README.md
|
|
72
|
+
|
|
73
|
+
================================================================================
|
|
74
|
+
|
|
75
|
+
--- FILE: main.py ---
|
|
76
|
+
import utils.helpers
|
|
77
|
+
|
|
78
|
+
def main():
|
|
79
|
+
print("Hello")
|
|
80
|
+
|
|
81
|
+
--- FILE: utils/helpers.py ---
|
|
82
|
+
def greet(name):
|
|
83
|
+
return f"Hi {name}"
|
|
84
|
+
```
|
|
85
|
+
Markdown format
|
|
86
|
+
```bash
|
|
87
|
+
pilepack . -f md -o report.md
|
|
88
|
+
```
|
|
89
|
+
Produces a Markdown file with syntax highlighting.
|
|
90
|
+
|
|
91
|
+
Show only structure (no file contents)
|
|
92
|
+
```bash
|
|
93
|
+
pilepack . --no-content
|
|
94
|
+
```
|
|
95
|
+
Mask secrets
|
|
96
|
+
```bash
|
|
97
|
+
pilepack . --mask-secrets
|
|
98
|
+
```
|
|
99
|
+
Replaces values of password=, api_key=, token=, and long strings (base64/hex) with ***.
|
|
100
|
+
|
|
101
|
+
Disable .gitignore
|
|
102
|
+
```bash
|
|
103
|
+
pilepack . --no-gitignore
|
|
104
|
+
```
|
|
105
|
+
## ๐ CLI Options
|
|
106
|
+
| Option | Description |
|
|
107
|
+
|--------|-------------|
|
|
108
|
+
| `root` | Directory to scan (default: current directory) |
|
|
109
|
+
| `--no-content` | Show tree structure only, skip file contents |
|
|
110
|
+
| `--mask-secrets` | Mask passwords, tokens, API keys |
|
|
111
|
+
| `-o, --output` | Write report to a file instead of stdout |
|
|
112
|
+
| `--no-gitignore` | Do not respect `.gitignore` (include all files) |
|
|
113
|
+
| `-f, --format` | Output format: `txt` (default) or `md` |
|
|
114
|
+
|
|
115
|
+
## ๐งช Testing
|
|
116
|
+
Install test dependencies and run:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
pip install -e .[test]
|
|
120
|
+
pytest
|
|
121
|
+
```
|
|
122
|
+
With coverage:
|
|
123
|
+
```bash
|
|
124
|
+
pytest --cov=pilepack
|
|
125
|
+
```
|
|
126
|
+
Current coverage: 84% (20 tests, all passing).
|
|
127
|
+
```bash
|
|
128
|
+
Name Stmts Miss Cover
|
|
129
|
+
-------------------------------------------
|
|
130
|
+
pilepack\__init__.py 0 0 100%
|
|
131
|
+
pilepack\__main__.py 3 3 0%
|
|
132
|
+
pilepack\cli.py 51 7 86%
|
|
133
|
+
pilepack\collector.py 30 1 97%
|
|
134
|
+
pilepack\formatter.py 44 3 93%
|
|
135
|
+
pilepack\ignorer.py 21 3 86%
|
|
136
|
+
pilepack\reader.py 31 12 61%
|
|
137
|
+
-------------------------------------------
|
|
138
|
+
TOTAL 180 29 84%
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## ๐ License
|
|
142
|
+
[MIT](LICENSE) ยฉ 2026 Vasili S. Pribylov
|
|
143
|
+
|
|
144
|
+
## ๐ค Contributing
|
|
145
|
+
Issues and pull requests are welcome! For major changes, please open an issue first to discuss.
|
|
146
|
+
|
|
147
|
+
## ๐ Acknowledgements
|
|
148
|
+
Inspired by the need to easily feed code into large language models.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
pilepack/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
pilepack/__main__.py,sha256=hgA7I86RlZPQ28yoxwUz5uOhsxK8fzYfaJ6XYkq2nQ4,65
|
|
3
|
+
pilepack/cli.py,sha256=nRrDwnU6D0KnwEOO_k2rnb7HqL0T0CzFsGHy2-yfYuI,3174
|
|
4
|
+
pilepack/collector.py,sha256=3uMoW68mjIFF-eaa3qUAS7CcSd1RMWX_VFAOTjPOdnM,1077
|
|
5
|
+
pilepack/formatter.py,sha256=sBv312kZjz-3etlIUOqxzj0698LTLMLZZbHrhMrRK84,2723
|
|
6
|
+
pilepack/ignorer.py,sha256=PMzl_Dx8gWUYxQHvccIBw-OAdYPn-ihbFcmTzYGkXpY,848
|
|
7
|
+
pilepack/reader.py,sha256=QEUPmCj8h_Po4Zzt0Yz6qdRyfBdK8-m6hxTzf0j19D4,1447
|
|
8
|
+
pilepack-0.1.0.dist-info/licenses/LICENSE,sha256=U6IZ7mLlAQFo7cMcX0eqWuPtgiP2RRAWfnYdfVmEc30,1079
|
|
9
|
+
pilepack-0.1.0.dist-info/METADATA,sha256=97DTfm7WtQuOSbFEtYgJznhjcEjqH7xB4lbNitv2Cd4,4282
|
|
10
|
+
pilepack-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
11
|
+
pilepack-0.1.0.dist-info/entry_points.txt,sha256=ywLekmCJYvrm_uymq5cWDu3GJXZVDUULh-q14QNRLN4,47
|
|
12
|
+
pilepack-0.1.0.dist-info/top_level.txt,sha256=PBbRF3bR-s_sO1hbRtLORxUipnbLHUytM7Lh6ecgbuI,9
|
|
13
|
+
pilepack-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2026 Vasili S. Pribylov
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pilepack
|