pytk-ai 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.
- pytk_ai-0.1.0/PKG-INFO +64 -0
- pytk_ai-0.1.0/README.md +45 -0
- pytk_ai-0.1.0/pyproject.toml +39 -0
- pytk_ai-0.1.0/setup.cfg +4 -0
- pytk_ai-0.1.0/src/pytk_ai/__init__.py +13 -0
- pytk_ai-0.1.0/src/pytk_ai/__main__.py +4 -0
- pytk_ai-0.1.0/src/pytk_ai/cli.py +146 -0
- pytk_ai-0.1.0/src/pytk_ai/config.py +13 -0
- pytk_ai-0.1.0/src/pytk_ai/data/__init__.py +1 -0
- pytk_ai-0.1.0/src/pytk_ai/data/rules.json +669 -0
- pytk_ai-0.1.0/src/pytk_ai/filters/__init__.py +77 -0
- pytk_ai-0.1.0/src/pytk_ai/filters/base.py +89 -0
- pytk_ai-0.1.0/src/pytk_ai/filters/build.py +470 -0
- pytk_ai-0.1.0/src/pytk_ai/filters/generic.py +31 -0
- pytk_ai-0.1.0/src/pytk_ai/filters/git.py +288 -0
- pytk_ai-0.1.0/src/pytk_ai/filters/go.py +216 -0
- pytk_ai-0.1.0/src/pytk_ai/filters/python.py +280 -0
- pytk_ai-0.1.0/src/pytk_ai/filters/ruby.py +187 -0
- pytk_ai-0.1.0/src/pytk_ai/filters/system.py +58 -0
- pytk_ai-0.1.0/src/pytk_ai/filters/tests.py +101 -0
- pytk_ai-0.1.0/src/pytk_ai/models.py +46 -0
- pytk_ai-0.1.0/src/pytk_ai/plan/__init__.py +12 -0
- pytk_ai-0.1.0/src/pytk_ai/plan/models.py +39 -0
- pytk_ai-0.1.0/src/pytk_ai/plan/normalize.py +153 -0
- pytk_ai-0.1.0/src/pytk_ai/plan/planner.py +281 -0
- pytk_ai-0.1.0/src/pytk_ai/plan/rules.py +43 -0
- pytk_ai-0.1.0/src/pytk_ai/plan/scanner.py +61 -0
- pytk_ai-0.1.0/src/pytk_ai/rewrite.py +50 -0
- pytk_ai-0.1.0/src/pytk_ai/runner.py +64 -0
- pytk_ai-0.1.0/src/pytk_ai/subprocess_utils.py +57 -0
- pytk_ai-0.1.0/src/pytk_ai.egg-info/PKG-INFO +64 -0
- pytk_ai-0.1.0/src/pytk_ai.egg-info/SOURCES.txt +42 -0
- pytk_ai-0.1.0/src/pytk_ai.egg-info/dependency_links.txt +1 -0
- pytk_ai-0.1.0/src/pytk_ai.egg-info/entry_points.txt +2 -0
- pytk_ai-0.1.0/src/pytk_ai.egg-info/top_level.txt +1 -0
- pytk_ai-0.1.0/tests/test_cli_run.py +46 -0
- pytk_ai-0.1.0/tests/test_filters_generic.py +49 -0
- pytk_ai-0.1.0/tests/test_filters_phase1.py +192 -0
- pytk_ai-0.1.0/tests/test_filters_phase2.py +272 -0
- pytk_ai-0.1.0/tests/test_plan_normalize.py +46 -0
- pytk_ai-0.1.0/tests/test_plan_planner.py +63 -0
- pytk_ai-0.1.0/tests/test_plan_rules.py +30 -0
- pytk_ai-0.1.0/tests/test_plan_scanner.py +26 -0
- pytk_ai-0.1.0/tests/test_runner.py +39 -0
pytk_ai-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pytk-ai
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python command planning, execution, and output filtering for PYTK-AI
|
|
5
|
+
Author: RTK AI
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: llm,cli,token,hooks,productivity
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
16
|
+
Classifier: Topic :: Utilities
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# PYTK-AI (Python)
|
|
21
|
+
|
|
22
|
+
This repository is a Python port of the PYTK-AI command rewrite and output-filtering engine.
|
|
23
|
+
|
|
24
|
+
## Migration direction
|
|
25
|
+
|
|
26
|
+
- target public CLI: `pytk-ai run <command>`
|
|
27
|
+
- target public library API: `from pytk_ai.runner import run_command`
|
|
28
|
+
- command rewrite/planning is internal implementation detail
|
|
29
|
+
- `rtk/` is read-only reference material, not a compatibility contract
|
|
30
|
+
- the old rewrite-first scaffold has been replaced by the execution-oriented `pytk-ai run` flow
|
|
31
|
+
|
|
32
|
+
## What it does
|
|
33
|
+
|
|
34
|
+
- plans shell commands internally for PYTK-AI-managed handling when useful
|
|
35
|
+
- executes raw shell commands through a small Python runner
|
|
36
|
+
- filters stdout/stderr into a compact token-efficient result
|
|
37
|
+
- preserves compound commands such as `&&`, `||`, `;`, `&`, and simple pipes
|
|
38
|
+
- exposes a small CLI plus hook-oriented JSON adapters
|
|
39
|
+
|
|
40
|
+
## Install locally
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install -e .
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## CLI usage
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pytk-ai run git status
|
|
50
|
+
pytk-ai run "cargo test && git push"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Library usage
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from pytk_ai.runner import run_command
|
|
57
|
+
|
|
58
|
+
result = run_command("git status")
|
|
59
|
+
print(result.filtered_output)
|
|
60
|
+
print(result.exit_code)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The package is intentionally small and dependency-free so it can be embedded in
|
|
64
|
+
agent toolchains and hook scripts.
|
pytk_ai-0.1.0/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# PYTK-AI (Python)
|
|
2
|
+
|
|
3
|
+
This repository is a Python port of the PYTK-AI command rewrite and output-filtering engine.
|
|
4
|
+
|
|
5
|
+
## Migration direction
|
|
6
|
+
|
|
7
|
+
- target public CLI: `pytk-ai run <command>`
|
|
8
|
+
- target public library API: `from pytk_ai.runner import run_command`
|
|
9
|
+
- command rewrite/planning is internal implementation detail
|
|
10
|
+
- `rtk/` is read-only reference material, not a compatibility contract
|
|
11
|
+
- the old rewrite-first scaffold has been replaced by the execution-oriented `pytk-ai run` flow
|
|
12
|
+
|
|
13
|
+
## What it does
|
|
14
|
+
|
|
15
|
+
- plans shell commands internally for PYTK-AI-managed handling when useful
|
|
16
|
+
- executes raw shell commands through a small Python runner
|
|
17
|
+
- filters stdout/stderr into a compact token-efficient result
|
|
18
|
+
- preserves compound commands such as `&&`, `||`, `;`, `&`, and simple pipes
|
|
19
|
+
- exposes a small CLI plus hook-oriented JSON adapters
|
|
20
|
+
|
|
21
|
+
## Install locally
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install -e .
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## CLI usage
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pytk-ai run git status
|
|
31
|
+
pytk-ai run "cargo test && git push"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Library usage
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from pytk_ai.runner import run_command
|
|
38
|
+
|
|
39
|
+
result = run_command("git status")
|
|
40
|
+
print(result.filtered_output)
|
|
41
|
+
print(result.exit_code)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The package is intentionally small and dependency-free so it can be embedded in
|
|
45
|
+
agent toolchains and hook scripts.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=69", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pytk-ai"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python command planning, execution, and output filtering for PYTK-AI"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "RTK AI" }]
|
|
13
|
+
keywords = ["llm", "cli", "token", "hooks", "productivity"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.10",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Programming Language :: Python :: 3.13",
|
|
22
|
+
"Topic :: Software Development :: Build Tools",
|
|
23
|
+
"Topic :: Utilities",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.scripts]
|
|
27
|
+
pytk-ai = "pytk_ai.cli:main"
|
|
28
|
+
|
|
29
|
+
[tool.setuptools]
|
|
30
|
+
package-dir = {"" = "src"}
|
|
31
|
+
|
|
32
|
+
[tool.setuptools.packages.find]
|
|
33
|
+
where = ["src"]
|
|
34
|
+
|
|
35
|
+
[tool.setuptools.package-data]
|
|
36
|
+
pytk_ai = ["data/*.json"]
|
|
37
|
+
|
|
38
|
+
[tool.pytest.ini_options]
|
|
39
|
+
testpaths = ["tests"]
|
pytk_ai-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .config import RunOptions
|
|
2
|
+
from .models import CommandResult, ExecutionResult, FilterResult
|
|
3
|
+
from .runner import run_command
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"CommandResult",
|
|
7
|
+
"ExecutionResult",
|
|
8
|
+
"FilterResult",
|
|
9
|
+
"RunOptions",
|
|
10
|
+
"run_command",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import shlex
|
|
6
|
+
import sys
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from .rewrite import rewrite_command
|
|
10
|
+
from .runner import run_command
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _join_command(parts: list[str]) -> str:
|
|
14
|
+
return shlex.join(parts).strip()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _read_stdin_json() -> dict[str, Any] | None:
|
|
18
|
+
raw = sys.stdin.read().strip()
|
|
19
|
+
if not raw:
|
|
20
|
+
return None
|
|
21
|
+
try:
|
|
22
|
+
payload = json.loads(raw)
|
|
23
|
+
except json.JSONDecodeError:
|
|
24
|
+
return None
|
|
25
|
+
if isinstance(payload, dict):
|
|
26
|
+
return payload
|
|
27
|
+
return None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _hook_command_from_payload(payload: dict[str, Any]) -> str | None:
|
|
31
|
+
tool_input = payload.get("tool_input")
|
|
32
|
+
if isinstance(tool_input, dict):
|
|
33
|
+
command = tool_input.get("command")
|
|
34
|
+
if isinstance(command, str):
|
|
35
|
+
return command
|
|
36
|
+
tool_args = payload.get("toolArgs")
|
|
37
|
+
if isinstance(tool_args, str):
|
|
38
|
+
try:
|
|
39
|
+
parsed = json.loads(tool_args)
|
|
40
|
+
except json.JSONDecodeError:
|
|
41
|
+
return None
|
|
42
|
+
if isinstance(parsed, dict):
|
|
43
|
+
command = parsed.get("command")
|
|
44
|
+
if isinstance(command, str):
|
|
45
|
+
return command
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _emit_claude_hook(rewritten: str) -> None:
|
|
50
|
+
payload = {
|
|
51
|
+
"hookSpecificOutput": {
|
|
52
|
+
"hookEventName": "PreToolUse",
|
|
53
|
+
"permissionDecision": "allow",
|
|
54
|
+
"permissionDecisionReason": "PYTK-AI auto-rewrite",
|
|
55
|
+
"updatedInput": {"command": rewritten},
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
sys.stdout.write(json.dumps(payload))
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _emit_cursor_hook(rewritten: str) -> None:
|
|
62
|
+
payload = {"permission": "allow", "updated_input": {"command": rewritten}}
|
|
63
|
+
sys.stdout.write(json.dumps(payload))
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _run_command(args: argparse.Namespace) -> int:
|
|
67
|
+
command = _join_command(args.args)
|
|
68
|
+
result = run_command(
|
|
69
|
+
command,
|
|
70
|
+
excluded=tuple(args.exclude or ()),
|
|
71
|
+
max_output_lines=args.max_output_lines,
|
|
72
|
+
)
|
|
73
|
+
if result.filtered_output:
|
|
74
|
+
sys.stdout.write(result.filtered_output)
|
|
75
|
+
if not result.filtered_output.endswith("\n"):
|
|
76
|
+
sys.stdout.write("\n")
|
|
77
|
+
return result.exit_code
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _run_hook(args: argparse.Namespace) -> int:
|
|
81
|
+
payload = _read_stdin_json()
|
|
82
|
+
if not payload:
|
|
83
|
+
if args.agent == "cursor":
|
|
84
|
+
sys.stdout.write("{}")
|
|
85
|
+
return 0
|
|
86
|
+
|
|
87
|
+
command = _hook_command_from_payload(payload)
|
|
88
|
+
if not command:
|
|
89
|
+
if args.agent == "cursor":
|
|
90
|
+
sys.stdout.write("{}")
|
|
91
|
+
return 0
|
|
92
|
+
|
|
93
|
+
result = rewrite_command(command)
|
|
94
|
+
if result is None or not result.changed:
|
|
95
|
+
if args.agent == "cursor":
|
|
96
|
+
sys.stdout.write("{}")
|
|
97
|
+
return 0
|
|
98
|
+
|
|
99
|
+
if args.agent == "cursor":
|
|
100
|
+
_emit_cursor_hook(result.output)
|
|
101
|
+
else:
|
|
102
|
+
_emit_claude_hook(result.output)
|
|
103
|
+
return 0
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
107
|
+
parser = argparse.ArgumentParser(prog="pytk-ai", description="PYTK-AI command runner")
|
|
108
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
109
|
+
|
|
110
|
+
run = sub.add_parser("run", help="execute a raw shell command and filter output")
|
|
111
|
+
run.add_argument("args", nargs=argparse.REMAINDER, help="raw command tokens")
|
|
112
|
+
run.add_argument(
|
|
113
|
+
"--exclude",
|
|
114
|
+
action="append",
|
|
115
|
+
default=[],
|
|
116
|
+
help="base commands to never plan as PYTK-AI-managed",
|
|
117
|
+
)
|
|
118
|
+
run.add_argument(
|
|
119
|
+
"--max-output-lines",
|
|
120
|
+
type=int,
|
|
121
|
+
default=200,
|
|
122
|
+
help="maximum number of filtered output lines",
|
|
123
|
+
)
|
|
124
|
+
run.set_defaults(func=_run_command)
|
|
125
|
+
|
|
126
|
+
hook = sub.add_parser("hook", help="process hook JSON from stdin")
|
|
127
|
+
hook_sub = hook.add_subparsers(dest="agent", required=True)
|
|
128
|
+
for agent in ("claude", "cursor"):
|
|
129
|
+
agent_parser = hook_sub.add_parser(agent)
|
|
130
|
+
agent_parser.set_defaults(func=_run_hook)
|
|
131
|
+
|
|
132
|
+
return parser
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def main(argv: list[str] | None = None) -> int:
|
|
136
|
+
parser = build_parser()
|
|
137
|
+
args = parser.parse_args(argv)
|
|
138
|
+
func = getattr(args, "func", None)
|
|
139
|
+
if func is None:
|
|
140
|
+
parser.print_help()
|
|
141
|
+
return 2
|
|
142
|
+
return func(args)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
if __name__ == "__main__":
|
|
146
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass(frozen=True)
|
|
7
|
+
class RunOptions:
|
|
8
|
+
cwd: str | None = None
|
|
9
|
+
env: dict[str, str] = field(default_factory=dict)
|
|
10
|
+
timeout: float | None = None
|
|
11
|
+
plan: bool = True
|
|
12
|
+
excluded_commands: tuple[str, ...] = ()
|
|
13
|
+
max_output_lines: int = 200
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Packaged command registry data for PYTK-AI."""
|