bcify 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.
- bcify/__init__.py +5 -0
- bcify/cli.py +57 -0
- bcify/generator.py +149 -0
- bcify-0.1.0.dist-info/METADATA +67 -0
- bcify-0.1.0.dist-info/RECORD +9 -0
- bcify-0.1.0.dist-info/WHEEL +5 -0
- bcify-0.1.0.dist-info/entry_points.txt +2 -0
- bcify-0.1.0.dist-info/licenses/LICENSE +58 -0
- bcify-0.1.0.dist-info/top_level.txt +1 -0
bcify/__init__.py
ADDED
bcify/cli.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from bcify.generator import build_scaffold_script
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
10
|
+
parser = argparse.ArgumentParser(
|
|
11
|
+
prog="bcify",
|
|
12
|
+
description="Generate a Bytecraft .bc scaffold from a project repository.",
|
|
13
|
+
)
|
|
14
|
+
parser.add_argument("source", help="Path to the source repository or project folder.")
|
|
15
|
+
parser.add_argument(
|
|
16
|
+
"-o",
|
|
17
|
+
"--output",
|
|
18
|
+
help="Path to the output .bc file. Defaults to <repo-name>.bc in the current directory.",
|
|
19
|
+
)
|
|
20
|
+
parser.add_argument(
|
|
21
|
+
"--root-name",
|
|
22
|
+
help="Folder name used in set-working-folder. Defaults to the source folder name.",
|
|
23
|
+
)
|
|
24
|
+
parser.add_argument(
|
|
25
|
+
"--include-hidden",
|
|
26
|
+
action="store_true",
|
|
27
|
+
help="Include hidden files and directories except default ignored tool folders.",
|
|
28
|
+
)
|
|
29
|
+
return parser
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def main() -> int:
|
|
33
|
+
parser = build_parser()
|
|
34
|
+
args = parser.parse_args()
|
|
35
|
+
|
|
36
|
+
source = Path(args.source).expanduser().resolve()
|
|
37
|
+
if not source.exists():
|
|
38
|
+
parser.error(f"Source path does not exist: {source}")
|
|
39
|
+
if not source.is_dir():
|
|
40
|
+
parser.error(f"Source path must be a directory: {source}")
|
|
41
|
+
|
|
42
|
+
output = Path(args.output).expanduser().resolve() if args.output else Path.cwd() / f"{source.name}.bc"
|
|
43
|
+
root_name = args.root_name or source.name
|
|
44
|
+
|
|
45
|
+
script = build_scaffold_script(
|
|
46
|
+
source_dir=source,
|
|
47
|
+
root_name=root_name,
|
|
48
|
+
include_hidden=args.include_hidden,
|
|
49
|
+
)
|
|
50
|
+
output.write_text(script, encoding="utf-8", newline="\n")
|
|
51
|
+
|
|
52
|
+
print(f"Wrote scaffold: {output}")
|
|
53
|
+
return 0
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
if __name__ == "__main__":
|
|
57
|
+
raise SystemExit(main())
|
bcify/generator.py
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
DEFAULT_IGNORED_DIRS = {
|
|
8
|
+
".git",
|
|
9
|
+
".hg",
|
|
10
|
+
".svn",
|
|
11
|
+
".idea",
|
|
12
|
+
".vscode",
|
|
13
|
+
".pytest_cache",
|
|
14
|
+
"__pycache__",
|
|
15
|
+
"node_modules",
|
|
16
|
+
".venv",
|
|
17
|
+
"venv",
|
|
18
|
+
"dist",
|
|
19
|
+
"build",
|
|
20
|
+
"target",
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
DEFAULT_IGNORED_FILES = {
|
|
24
|
+
".DS_Store",
|
|
25
|
+
"Thumbs.db",
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
TEXT_CHUNK_SIZE = 8192
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass(frozen=True)
|
|
32
|
+
class RepoEntry:
|
|
33
|
+
path: Path
|
|
34
|
+
relative_path: Path
|
|
35
|
+
kind: str
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def build_scaffold_script(source_dir: Path, root_name: str, include_hidden: bool = False) -> str:
|
|
39
|
+
entries = collect_repo_entries(source_dir=source_dir, include_hidden=include_hidden)
|
|
40
|
+
lines = [
|
|
41
|
+
f"# Generated by bcify from: {source_dir}",
|
|
42
|
+
'# Non-text files were skipped automatically.',
|
|
43
|
+
f'set-working-folder "{escape_string(root_name)}"',
|
|
44
|
+
"",
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
for entry in entries:
|
|
48
|
+
relative = entry.relative_path.as_posix()
|
|
49
|
+
if entry.kind == "dir":
|
|
50
|
+
lines.append(f'make-folder "{escape_string(relative)}"')
|
|
51
|
+
continue
|
|
52
|
+
|
|
53
|
+
content = entry.path.read_text(encoding=detect_text_encoding(entry.path))
|
|
54
|
+
lines.extend(
|
|
55
|
+
[
|
|
56
|
+
f'make-file "{escape_string(relative)}" with ---',
|
|
57
|
+
normalize_multiline_content(content),
|
|
58
|
+
"---",
|
|
59
|
+
]
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
return "\n".join(lines).rstrip() + "\n"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def collect_repo_entries(source_dir: Path, include_hidden: bool = False) -> list[RepoEntry]:
|
|
66
|
+
directories: list[RepoEntry] = []
|
|
67
|
+
files: list[RepoEntry] = []
|
|
68
|
+
|
|
69
|
+
for path in sorted(source_dir.rglob("*")):
|
|
70
|
+
relative = path.relative_to(source_dir)
|
|
71
|
+
|
|
72
|
+
if should_skip_path(relative, path, include_hidden=include_hidden):
|
|
73
|
+
continue
|
|
74
|
+
|
|
75
|
+
if path.is_dir():
|
|
76
|
+
directories.append(RepoEntry(path=path, relative_path=relative, kind="dir"))
|
|
77
|
+
continue
|
|
78
|
+
|
|
79
|
+
if path.is_file() and is_probably_text(path):
|
|
80
|
+
files.append(RepoEntry(path=path, relative_path=relative, kind="file"))
|
|
81
|
+
|
|
82
|
+
return directories + files
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def should_skip_path(relative: Path, path: Path, include_hidden: bool) -> bool:
|
|
86
|
+
parts = relative.parts
|
|
87
|
+
if any(part in DEFAULT_IGNORED_DIRS for part in parts):
|
|
88
|
+
return True
|
|
89
|
+
|
|
90
|
+
name = path.name
|
|
91
|
+
if name in DEFAULT_IGNORED_FILES:
|
|
92
|
+
return True
|
|
93
|
+
|
|
94
|
+
if include_hidden:
|
|
95
|
+
return False
|
|
96
|
+
|
|
97
|
+
return any(part.startswith(".") for part in parts)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def is_probably_text(path: Path) -> bool:
|
|
101
|
+
try:
|
|
102
|
+
chunk = path.read_bytes()[:TEXT_CHUNK_SIZE]
|
|
103
|
+
except OSError:
|
|
104
|
+
return False
|
|
105
|
+
|
|
106
|
+
if not chunk:
|
|
107
|
+
return True
|
|
108
|
+
|
|
109
|
+
if b"\x00" in chunk:
|
|
110
|
+
return False
|
|
111
|
+
|
|
112
|
+
try:
|
|
113
|
+
chunk.decode("utf-8")
|
|
114
|
+
return True
|
|
115
|
+
except UnicodeDecodeError:
|
|
116
|
+
pass
|
|
117
|
+
|
|
118
|
+
for encoding in ("utf-16", "utf-16-le", "utf-16-be"):
|
|
119
|
+
try:
|
|
120
|
+
chunk.decode(encoding)
|
|
121
|
+
return True
|
|
122
|
+
except UnicodeDecodeError:
|
|
123
|
+
continue
|
|
124
|
+
|
|
125
|
+
printable = sum(
|
|
126
|
+
byte in b"\t\n\r\f\b" or 32 <= byte <= 126
|
|
127
|
+
for byte in chunk
|
|
128
|
+
)
|
|
129
|
+
return printable / len(chunk) >= 0.85
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def detect_text_encoding(path: Path) -> str:
|
|
133
|
+
raw = path.read_bytes()
|
|
134
|
+
for encoding in ("utf-8", "utf-16", "utf-16-le", "utf-16-be"):
|
|
135
|
+
try:
|
|
136
|
+
raw.decode(encoding)
|
|
137
|
+
return encoding
|
|
138
|
+
except UnicodeDecodeError:
|
|
139
|
+
continue
|
|
140
|
+
return "utf-8"
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def normalize_multiline_content(content: str) -> str:
|
|
144
|
+
normalized = content.replace("\r\n", "\n").replace("\r", "\n")
|
|
145
|
+
return normalized.rstrip("\n")
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def escape_string(value: str) -> str:
|
|
149
|
+
return value.replace("\\", "\\\\").replace('"', '\\"')
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bcify
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Generate Bytecraft .bc scaffolds from existing project repositories.
|
|
5
|
+
Author: Sourasish das
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: bytecraft,scaffold,generator,cli
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
License-File: LICENSE
|
|
16
|
+
Provides-Extra: dev
|
|
17
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# Bcify
|
|
21
|
+
|
|
22
|
+
`Bcify` turns an existing project repository into a Bytecraft `.bc` scaffold script.
|
|
23
|
+
|
|
24
|
+
It walks the repo, keeps probable text files, skips binary content, and emits a script
|
|
25
|
+
made of `make-folder` and `make-file` commands that can recreate the text scaffold.
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install -e .
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
bcify path/to/repo
|
|
37
|
+
bcify path/to/repo --output project.bc
|
|
38
|
+
bcify path/to/repo --root-name cloned-project --include-hidden
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
By default, `bcify`:
|
|
42
|
+
|
|
43
|
+
- writes `<repo-name>.bc` in the current working directory
|
|
44
|
+
- skips common generated or tool directories like `.git`, `node_modules`, `.venv`, and `dist`
|
|
45
|
+
- ignores files that do not look like text
|
|
46
|
+
- preserves empty text files
|
|
47
|
+
|
|
48
|
+
## Example
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
bcify ../my-app --output my-app.bc
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Example output:
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
# Generated by bcify from: ../my-app
|
|
58
|
+
set-working-folder "my-app"
|
|
59
|
+
|
|
60
|
+
make-folder "src"
|
|
61
|
+
make-file "README.md" with ---
|
|
62
|
+
# my-app
|
|
63
|
+
---
|
|
64
|
+
make-file "src/main.py" with ---
|
|
65
|
+
print("hello")
|
|
66
|
+
---
|
|
67
|
+
```
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
bcify/__init__.py,sha256=1Sa3FDLJdrBOgsuxRU0-7fjPF7sqP2sy5e6zk1hRbkQ,71
|
|
2
|
+
bcify/cli.py,sha256=Cv1jYvH2Hu02XvxKxJLxDf8Rua4QmJ5H4zRmH8OvuEo,1713
|
|
3
|
+
bcify/generator.py,sha256=6esEJx5MHE5jCGyhs3iOyP20KiALX4smR4WLsqpiyPA,3669
|
|
4
|
+
bcify-0.1.0.dist-info/licenses/LICENSE,sha256=SydTxzsJaC7cNdZOpuvip-zPvWkDDgmai_8At4aKle0,3483
|
|
5
|
+
bcify-0.1.0.dist-info/METADATA,sha256=_Q1pSG2wv-v2ZVCWJkk616wekXSrCx5-SxgT56I61rU,1655
|
|
6
|
+
bcify-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
7
|
+
bcify-0.1.0.dist-info/entry_points.txt,sha256=rR1QS5xYXcfbMki8qc7ZkW1sRiEd9QvcZHemzQA5pPU,41
|
|
8
|
+
bcify-0.1.0.dist-info/top_level.txt,sha256=5fMEG4DpKwAY_ahNE34uZDGdU82FFoDrvT2g1N8LRFU,6
|
|
9
|
+
bcify-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
Server-Lab Open-Control License (SOCL) 1.0
|
|
2
|
+
====================================
|
|
3
|
+
Copyright (c) 2025 Sourasish Das
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
of this software and associated documentation files (the "Software"), to use,
|
|
6
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
7
|
+
the Software, and to permit persons to whom the Software is furnished to do
|
|
8
|
+
so, subject to the following conditions:
|
|
9
|
+
1. Attribution.
|
|
10
|
+
The original copyright notice and this license text must be retained in
|
|
11
|
+
all copies or substantial portions of the Software. If you distribute
|
|
12
|
+
compiled or packaged forms of the Software, you must include the copyright
|
|
13
|
+
notice and a copy of this license in a file distributed with those forms.
|
|
14
|
+
2. Open-source grant.
|
|
15
|
+
The rights granted in this license are intended to be open-source in nature:
|
|
16
|
+
recipients may use, study, modify, and redistribute the Software under the
|
|
17
|
+
terms set forth herein.
|
|
18
|
+
3. Ownership and control.
|
|
19
|
+
Copyright and all intellectual property rights in the Software are and remain
|
|
20
|
+
the exclusive property of the Copyright Owner named above. The Copyright
|
|
21
|
+
Owner retains sole authority to relicense, alter, or otherwise change the
|
|
22
|
+
licensing terms that apply to future distributions of the Software.
|
|
23
|
+
4. License changes; effective date.
|
|
24
|
+
The Copyright Owner may change or replace this license at any time and
|
|
25
|
+
without notice. Any such change applies only to versions or distributions
|
|
26
|
+
of the Software that the Copyright Owner explicitly distributes after the
|
|
27
|
+
change takes effect. Copies of the Software already distributed by the
|
|
28
|
+
Copyright Owner under a prior version of this license remain governed by
|
|
29
|
+
the terms under which those copies were originally distributed.
|
|
30
|
+
5. No trademark rights.
|
|
31
|
+
This license does not grant any rights to use the Copyright Owner's trade
|
|
32
|
+
names, trademarks, service marks, or logos.
|
|
33
|
+
6. No warranty.
|
|
34
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE COPYRIGHT OWNER DISCLAIMS ALL
|
|
35
|
+
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
36
|
+
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
|
|
37
|
+
NON-INFRINGEMENT. USE OF THE SOFTWARE IS AT YOUR OWN RISK.
|
|
38
|
+
7. Limitation of liability.
|
|
39
|
+
IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
40
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
41
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
42
|
+
DEALINGS IN THE SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
43
|
+
8. Contributor submissions.
|
|
44
|
+
By submitting a contribution to the project, a contributor grants the
|
|
45
|
+
Copyright Owner a perpetual, irrevocable, worldwide, royalty-free,
|
|
46
|
+
transferable license to use, reproduce, modify, distribute, sublicense,
|
|
47
|
+
and otherwise exploit the contribution as part of the Software.
|
|
48
|
+
9. Termination.
|
|
49
|
+
This license and the rights granted hereunder will terminate automatically
|
|
50
|
+
if you fail to comply with any term of this license. Termination does not
|
|
51
|
+
affect any rights that were already granted to recipients prior to the
|
|
52
|
+
termination event with respect to copies distributed while the license was
|
|
53
|
+
in effect for that distribution.
|
|
54
|
+
10. Governing law.
|
|
55
|
+
This license does not select a governing law; legal disputes shall be
|
|
56
|
+
governed by the laws applicable where enforcement is sought, subject to
|
|
57
|
+
applicable conflict-of-law rules.
|
|
58
|
+
[END OF LICENSE]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
bcify
|