sfhtml 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.
- sfhtml-0.1.0/PKG-INFO +70 -0
- sfhtml-0.1.0/README.md +52 -0
- sfhtml-0.1.0/pyproject.toml +28 -0
- sfhtml-0.1.0/setup.cfg +4 -0
- sfhtml-0.1.0/sfhtml/__init__.py +200 -0
- sfhtml-0.1.0/sfhtml/cli.py +37 -0
- sfhtml-0.1.0/sfhtml.egg-info/PKG-INFO +70 -0
- sfhtml-0.1.0/sfhtml.egg-info/SOURCES.txt +9 -0
- sfhtml-0.1.0/sfhtml.egg-info/dependency_links.txt +1 -0
- sfhtml-0.1.0/sfhtml.egg-info/entry_points.txt +2 -0
- sfhtml-0.1.0/sfhtml.egg-info/top_level.txt +1 -0
sfhtml-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sfhtml
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Single-File HTML AI-Skill CLI — Python wrapper
|
|
5
|
+
Author-email: anyrust <ggen652@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/anyrust/sfhtml
|
|
8
|
+
Project-URL: Repository, https://github.com/anyrust/sfhtml
|
|
9
|
+
Keywords: html,single-file,ai,cli,editor
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
15
|
+
Classifier: Topic :: Text Processing :: Markup :: HTML
|
|
16
|
+
Requires-Python: >=3.8
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# sfhtml (Python)
|
|
20
|
+
|
|
21
|
+
Python wrapper for [sfhtml](https://github.com/anyrust/sfhtml) — Single-File HTML AI-Skill CLI.
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install sfhtml
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
> **Requires** the `sfhtml` binary. Install via `cargo install sfhtml` or download from [GitHub Releases](https://github.com/anyrust/sfhtml/releases).
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
### As a Python library
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
import sfhtml
|
|
37
|
+
|
|
38
|
+
# Scan for HTML files
|
|
39
|
+
files = sfhtml.scan("./my-project")
|
|
40
|
+
|
|
41
|
+
# Read file header
|
|
42
|
+
header = sfhtml.header("app.html")
|
|
43
|
+
|
|
44
|
+
# Apply a diff
|
|
45
|
+
result = sfhtml.apply("app.html", "patch.diff", backup=True)
|
|
46
|
+
|
|
47
|
+
# Validate
|
|
48
|
+
report = sfhtml.validate("app.html")
|
|
49
|
+
|
|
50
|
+
# Browser interaction
|
|
51
|
+
sfhtml.debug_start("app.html")
|
|
52
|
+
sfhtml.page_click("#submit-btn")
|
|
53
|
+
logs = sfhtml.page_console()
|
|
54
|
+
sfhtml.debug_stop()
|
|
55
|
+
|
|
56
|
+
# Run any sfhtml command
|
|
57
|
+
result = sfhtml.run("anchor-list", "app.html")
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### As a CLI (same as the Rust binary)
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
sfhtml scan . --recursive --json
|
|
64
|
+
sfhtml header app.html
|
|
65
|
+
sfhtml page screenshot --output shot.png
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## License
|
|
69
|
+
|
|
70
|
+
MIT
|
sfhtml-0.1.0/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# sfhtml (Python)
|
|
2
|
+
|
|
3
|
+
Python wrapper for [sfhtml](https://github.com/anyrust/sfhtml) — Single-File HTML AI-Skill CLI.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install sfhtml
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
> **Requires** the `sfhtml` binary. Install via `cargo install sfhtml` or download from [GitHub Releases](https://github.com/anyrust/sfhtml/releases).
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
### As a Python library
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
import sfhtml
|
|
19
|
+
|
|
20
|
+
# Scan for HTML files
|
|
21
|
+
files = sfhtml.scan("./my-project")
|
|
22
|
+
|
|
23
|
+
# Read file header
|
|
24
|
+
header = sfhtml.header("app.html")
|
|
25
|
+
|
|
26
|
+
# Apply a diff
|
|
27
|
+
result = sfhtml.apply("app.html", "patch.diff", backup=True)
|
|
28
|
+
|
|
29
|
+
# Validate
|
|
30
|
+
report = sfhtml.validate("app.html")
|
|
31
|
+
|
|
32
|
+
# Browser interaction
|
|
33
|
+
sfhtml.debug_start("app.html")
|
|
34
|
+
sfhtml.page_click("#submit-btn")
|
|
35
|
+
logs = sfhtml.page_console()
|
|
36
|
+
sfhtml.debug_stop()
|
|
37
|
+
|
|
38
|
+
# Run any sfhtml command
|
|
39
|
+
result = sfhtml.run("anchor-list", "app.html")
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### As a CLI (same as the Rust binary)
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
sfhtml scan . --recursive --json
|
|
46
|
+
sfhtml header app.html
|
|
47
|
+
sfhtml page screenshot --output shot.png
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## License
|
|
51
|
+
|
|
52
|
+
MIT
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "sfhtml"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Single-File HTML AI-Skill CLI — Python wrapper"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
requires-python = ">=3.8"
|
|
12
|
+
authors = [{name = "anyrust", email = "ggen652@gmail.com"}]
|
|
13
|
+
keywords = ["html", "single-file", "ai", "cli", "editor"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Topic :: Software Development :: Build Tools",
|
|
20
|
+
"Topic :: Text Processing :: Markup :: HTML",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[project.urls]
|
|
24
|
+
Homepage = "https://github.com/anyrust/sfhtml"
|
|
25
|
+
Repository = "https://github.com/anyrust/sfhtml"
|
|
26
|
+
|
|
27
|
+
[project.scripts]
|
|
28
|
+
sfhtml = "sfhtml.cli:main"
|
sfhtml-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"""sfhtml — Single-File HTML AI-Skill CLI (Python wrapper)
|
|
2
|
+
|
|
3
|
+
Provides a Python interface to the sfhtml Rust binary.
|
|
4
|
+
All commands support --json for structured output.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
import sfhtml
|
|
8
|
+
result = sfhtml.run("scan", ".", "--recursive")
|
|
9
|
+
result = sfhtml.scan(".")
|
|
10
|
+
result = sfhtml.header("app.html")
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
import subprocess
|
|
15
|
+
import shutil
|
|
16
|
+
import sys
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Any, Dict, List, Optional, Union
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _find_binary() -> str:
|
|
22
|
+
"""Find the sfhtml binary. Checks: same directory, PATH, cargo install location."""
|
|
23
|
+
# 1. Bundled binary next to this package
|
|
24
|
+
pkg_dir = Path(__file__).parent
|
|
25
|
+
for candidate in [pkg_dir / "sfhtml", pkg_dir / "sfhtml.exe"]:
|
|
26
|
+
if candidate.exists():
|
|
27
|
+
return str(candidate)
|
|
28
|
+
|
|
29
|
+
# 2. On PATH
|
|
30
|
+
found = shutil.which("sfhtml")
|
|
31
|
+
if found:
|
|
32
|
+
return found
|
|
33
|
+
|
|
34
|
+
# 3. Cargo install location
|
|
35
|
+
cargo_bin = Path.home() / ".cargo" / "bin" / "sfhtml"
|
|
36
|
+
if cargo_bin.exists():
|
|
37
|
+
return str(cargo_bin)
|
|
38
|
+
|
|
39
|
+
raise FileNotFoundError(
|
|
40
|
+
"sfhtml binary not found. Install it with:\n"
|
|
41
|
+
" cargo install sfhtml\n"
|
|
42
|
+
"Or download from: https://github.com/anyrust/sfhtml/releases"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def run(*args: str, json_output: bool = True, timeout: Optional[int] = None) -> Union[Dict, str]:
|
|
47
|
+
"""Run an sfhtml command and return the result.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
*args: Command arguments (e.g. "scan", ".", "--recursive")
|
|
51
|
+
json_output: If True, append --json and parse output as JSON
|
|
52
|
+
timeout: Timeout in seconds (None = no timeout)
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Parsed JSON dict if json_output=True, raw stdout string otherwise.
|
|
56
|
+
|
|
57
|
+
Raises:
|
|
58
|
+
FileNotFoundError: If sfhtml binary is not found
|
|
59
|
+
subprocess.CalledProcessError: If command fails
|
|
60
|
+
json.JSONDecodeError: If JSON parsing fails
|
|
61
|
+
"""
|
|
62
|
+
binary = _find_binary()
|
|
63
|
+
cmd = [binary] + list(args)
|
|
64
|
+
if json_output and "--json" not in args:
|
|
65
|
+
cmd.append("--json")
|
|
66
|
+
|
|
67
|
+
result = subprocess.run(
|
|
68
|
+
cmd,
|
|
69
|
+
capture_output=True,
|
|
70
|
+
text=True,
|
|
71
|
+
timeout=timeout,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
if result.returncode != 0:
|
|
75
|
+
raise subprocess.CalledProcessError(
|
|
76
|
+
result.returncode, cmd, result.stdout, result.stderr
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
if json_output:
|
|
80
|
+
return json.loads(result.stdout)
|
|
81
|
+
return result.stdout
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# ---------------------------------------------------------------------------
|
|
85
|
+
# Convenience functions
|
|
86
|
+
# ---------------------------------------------------------------------------
|
|
87
|
+
|
|
88
|
+
def scan(directory: str = ".", recursive: bool = True, **kwargs) -> Dict:
|
|
89
|
+
"""Scan a directory for HTML files with AI-SKILL-HEADERs."""
|
|
90
|
+
args = ["scan", directory]
|
|
91
|
+
if recursive:
|
|
92
|
+
args.append("--recursive")
|
|
93
|
+
for k, v in kwargs.items():
|
|
94
|
+
args.extend([f"--{k.replace('_', '-')}", str(v)])
|
|
95
|
+
return run(*args)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def header(file: str, section: Optional[int] = None) -> Dict:
|
|
99
|
+
"""Extract the AI-SKILL-HEADER from an HTML file."""
|
|
100
|
+
args = ["header", file]
|
|
101
|
+
if section is not None:
|
|
102
|
+
args.extend(["--section", str(section)])
|
|
103
|
+
return run(*args)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def read(file: str, start: Optional[int] = None, end: Optional[int] = None,
|
|
107
|
+
head: Optional[int] = None, tail: Optional[int] = None) -> str:
|
|
108
|
+
"""Read lines from a file."""
|
|
109
|
+
args = ["read", file]
|
|
110
|
+
if start is not None:
|
|
111
|
+
args.append(str(start))
|
|
112
|
+
if end is not None:
|
|
113
|
+
args.append(str(end))
|
|
114
|
+
if head is not None:
|
|
115
|
+
args.extend(["--head", str(head)])
|
|
116
|
+
if tail is not None:
|
|
117
|
+
args.extend(["--tail", str(tail)])
|
|
118
|
+
return run(*args, json_output=False)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def locate(file: str, anchor: str, context: int = 0) -> Dict:
|
|
122
|
+
"""Locate a code anchor in the file."""
|
|
123
|
+
args = ["locate", file, anchor, "--context", str(context)]
|
|
124
|
+
return run(*args)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def apply(file: str, diff: str, dry_run: bool = False, backup: bool = False) -> Dict:
|
|
128
|
+
"""Apply a unified diff to a file."""
|
|
129
|
+
args = ["apply", file, "--diff", diff]
|
|
130
|
+
if dry_run:
|
|
131
|
+
args.append("--dry-run")
|
|
132
|
+
if backup:
|
|
133
|
+
args.append("--backup")
|
|
134
|
+
return run(*args)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def validate(file: str, fix: bool = False) -> Dict:
|
|
138
|
+
"""Validate header-to-code consistency."""
|
|
139
|
+
args = ["validate", file]
|
|
140
|
+
if fix:
|
|
141
|
+
args.append("--fix")
|
|
142
|
+
return run(*args)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def module(file: str, depth: int = 0) -> Dict:
|
|
146
|
+
"""Scan local ES module / resource dependencies."""
|
|
147
|
+
args = ["module", file, "--depth", str(depth)]
|
|
148
|
+
return run(*args)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def search(query: str, directory: str = ".", top: int = 5) -> Dict:
|
|
152
|
+
"""Search HTML files by query."""
|
|
153
|
+
return run("search", query, "--dir", directory, "--top", str(top))
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def page_screenshot(port: int = 9222, selector: Optional[str] = None,
|
|
157
|
+
output: Optional[str] = None) -> Dict:
|
|
158
|
+
"""Capture a page screenshot."""
|
|
159
|
+
args = ["page", "screenshot", "--port", str(port)]
|
|
160
|
+
if selector:
|
|
161
|
+
args.extend(["--selector", selector])
|
|
162
|
+
if output:
|
|
163
|
+
args.extend(["--output", output])
|
|
164
|
+
return run(*args)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def page_click(selector: str, port: int = 9222) -> Dict:
|
|
168
|
+
"""Click an element on the page."""
|
|
169
|
+
return run("page", "click", selector, "--port", str(port))
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def page_eval(expression: str, port: int = 9222) -> Dict:
|
|
173
|
+
"""Evaluate JavaScript in the page."""
|
|
174
|
+
return run("page", "eval", expression, "--port", str(port))
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def page_dom(port: int = 9222, selector: Optional[str] = None) -> Dict:
|
|
178
|
+
"""Get page DOM HTML."""
|
|
179
|
+
args = ["page", "dom", "--port", str(port)]
|
|
180
|
+
if selector:
|
|
181
|
+
args.extend(["--selector", selector])
|
|
182
|
+
return run(*args)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def page_console(port: int = 9222) -> Dict:
|
|
186
|
+
"""Get console log messages."""
|
|
187
|
+
return run("page", "console", "--port", str(port))
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def debug_start(file: str, port: int = 9222, headless: bool = True) -> Dict:
|
|
191
|
+
"""Start a browser with CDP debugging."""
|
|
192
|
+
args = ["debug", "start", file, "--port", str(port)]
|
|
193
|
+
if not headless:
|
|
194
|
+
args.append("--no-headless")
|
|
195
|
+
return run(*args)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def debug_stop(port: int = 9222) -> Dict:
|
|
199
|
+
"""Stop a browser session."""
|
|
200
|
+
return run("debug", "stop", "--port", str(port))
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""CLI entry point — delegates to the sfhtml Rust binary."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
import os
|
|
5
|
+
import shutil
|
|
6
|
+
import subprocess
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _find_binary() -> str:
|
|
11
|
+
pkg_dir = Path(__file__).parent
|
|
12
|
+
for candidate in [pkg_dir / "sfhtml", pkg_dir / "sfhtml.exe"]:
|
|
13
|
+
if candidate.exists():
|
|
14
|
+
return str(candidate)
|
|
15
|
+
found = shutil.which("sfhtml")
|
|
16
|
+
if found:
|
|
17
|
+
return found
|
|
18
|
+
cargo_bin = Path.home() / ".cargo" / "bin" / "sfhtml"
|
|
19
|
+
if cargo_bin.exists():
|
|
20
|
+
return str(cargo_bin)
|
|
21
|
+
print(
|
|
22
|
+
"Error: sfhtml binary not found.\n"
|
|
23
|
+
"Install with: cargo install sfhtml\n"
|
|
24
|
+
"Or download from: https://github.com/anyrust/sfhtml/releases",
|
|
25
|
+
file=sys.stderr,
|
|
26
|
+
)
|
|
27
|
+
sys.exit(1)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def main():
|
|
31
|
+
binary = _find_binary()
|
|
32
|
+
result = subprocess.run([binary] + sys.argv[1:])
|
|
33
|
+
sys.exit(result.returncode)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
if __name__ == "__main__":
|
|
37
|
+
main()
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sfhtml
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Single-File HTML AI-Skill CLI — Python wrapper
|
|
5
|
+
Author-email: anyrust <ggen652@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/anyrust/sfhtml
|
|
8
|
+
Project-URL: Repository, https://github.com/anyrust/sfhtml
|
|
9
|
+
Keywords: html,single-file,ai,cli,editor
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
15
|
+
Classifier: Topic :: Text Processing :: Markup :: HTML
|
|
16
|
+
Requires-Python: >=3.8
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# sfhtml (Python)
|
|
20
|
+
|
|
21
|
+
Python wrapper for [sfhtml](https://github.com/anyrust/sfhtml) — Single-File HTML AI-Skill CLI.
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install sfhtml
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
> **Requires** the `sfhtml` binary. Install via `cargo install sfhtml` or download from [GitHub Releases](https://github.com/anyrust/sfhtml/releases).
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
### As a Python library
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
import sfhtml
|
|
37
|
+
|
|
38
|
+
# Scan for HTML files
|
|
39
|
+
files = sfhtml.scan("./my-project")
|
|
40
|
+
|
|
41
|
+
# Read file header
|
|
42
|
+
header = sfhtml.header("app.html")
|
|
43
|
+
|
|
44
|
+
# Apply a diff
|
|
45
|
+
result = sfhtml.apply("app.html", "patch.diff", backup=True)
|
|
46
|
+
|
|
47
|
+
# Validate
|
|
48
|
+
report = sfhtml.validate("app.html")
|
|
49
|
+
|
|
50
|
+
# Browser interaction
|
|
51
|
+
sfhtml.debug_start("app.html")
|
|
52
|
+
sfhtml.page_click("#submit-btn")
|
|
53
|
+
logs = sfhtml.page_console()
|
|
54
|
+
sfhtml.debug_stop()
|
|
55
|
+
|
|
56
|
+
# Run any sfhtml command
|
|
57
|
+
result = sfhtml.run("anchor-list", "app.html")
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### As a CLI (same as the Rust binary)
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
sfhtml scan . --recursive --json
|
|
64
|
+
sfhtml header app.html
|
|
65
|
+
sfhtml page screenshot --output shot.png
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## License
|
|
69
|
+
|
|
70
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
sfhtml
|