ry-tool 1.0.1__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.
ry_tool-1.0.1/PKG-INFO ADDED
@@ -0,0 +1,112 @@
1
+ Metadata-Version: 2.3
2
+ Name: ry-tool
3
+ Version: 1.0.1
4
+ Summary: Pure YAML command orchestrator - CI/CD for humans
5
+ Author: Fredrik Angelsen
6
+ Author-email: Fredrik Angelsen <fredrikangelsen@gmail.com>
7
+ Requires-Dist: pyyaml>=6.0
8
+ Requires-Python: >=3.12
9
+ Description-Content-Type: text/markdown
10
+
11
+ # ry-next
12
+
13
+ A clean, modular command augmentation framework that enhances existing CLI tools without breaking their native behavior.
14
+
15
+ ## Features
16
+
17
+ - **Command Augmentation**: Wrap and enhance existing CLI tools
18
+ - **Clean Architecture**: Modular design with single-responsibility components
19
+ - **Type-Safe Processing**: Recursive template processing with type dispatch
20
+ - **Token-Based Safety**: Time-limited tokens for dangerous operations
21
+ - **Library System**: Reusable command definitions with metadata
22
+ - **No Shell Escaping**: Direct subprocess execution for safety
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ pip install -e .
28
+ ```
29
+
30
+ This installs the `ry-next` command globally.
31
+
32
+ ## Quick Start
33
+
34
+ ```bash
35
+ # List available libraries
36
+ ry-next --list
37
+
38
+ # Get help for a library
39
+ ry-next git --ry-help
40
+
41
+ # Execute augmented command
42
+ ry-next git commit -m "feat: new feature"
43
+
44
+ # Show execution plan (dry run)
45
+ ry-next --ry-run git commit -m "test"
46
+ ```
47
+
48
+ ## Production Libraries
49
+
50
+ - **git** - Enhanced git workflow with review tokens and commit validation
51
+ - **uv** - Python package management with automated version workflows
52
+ - **changelog** - Simple changelog management following Keep a Changelog
53
+ - **ry-lib** - Library development and management tools
54
+
55
+ ## Documentation
56
+
57
+ - [Full Documentation](docs/README_RYNEXT.md)
58
+ - [Library Development](docs/libraries/ry-lib/README.md)
59
+ - [Examples](examples/README.md)
60
+
61
+ ## Project Structure
62
+
63
+ ```
64
+ ry-next/
65
+ ├── src/ry_next/ # Core implementation
66
+ ├── docs/
67
+ │ ├── libraries/ # Production libraries
68
+ │ └── README_RYNEXT.md # Full documentation
69
+ ├── examples/ # Example libraries
70
+ └── _archive/ # Old ry-tool code (deprecated)
71
+ ```
72
+
73
+ ## Key Concepts
74
+
75
+ ### Library Format (v2.0)
76
+
77
+ ```yaml
78
+ version: "2.0"
79
+ name: git
80
+ type: augmentation
81
+ target: /usr/bin/git
82
+
83
+ commands:
84
+ commit:
85
+ flags:
86
+ m/message: string
87
+ augment:
88
+ before:
89
+ - python: |
90
+ # Validation logic
91
+ relay: native
92
+ ```
93
+
94
+ ### Token-Based Safety
95
+
96
+ Critical operations require preview and token verification:
97
+
98
+ ```bash
99
+ # Preview changes
100
+ git diff --staged # → Generates REVIEW_TOKEN
101
+
102
+ # Execute with token
103
+ REVIEW_TOKEN=xxx git commit -m "message"
104
+ ```
105
+
106
+ ## Development
107
+
108
+ See [docs/README_RYNEXT.md](docs/README_RYNEXT.md) for complete documentation.
109
+
110
+ ## License
111
+
112
+ MIT
@@ -0,0 +1,102 @@
1
+ # ry-next
2
+
3
+ A clean, modular command augmentation framework that enhances existing CLI tools without breaking their native behavior.
4
+
5
+ ## Features
6
+
7
+ - **Command Augmentation**: Wrap and enhance existing CLI tools
8
+ - **Clean Architecture**: Modular design with single-responsibility components
9
+ - **Type-Safe Processing**: Recursive template processing with type dispatch
10
+ - **Token-Based Safety**: Time-limited tokens for dangerous operations
11
+ - **Library System**: Reusable command definitions with metadata
12
+ - **No Shell Escaping**: Direct subprocess execution for safety
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ pip install -e .
18
+ ```
19
+
20
+ This installs the `ry-next` command globally.
21
+
22
+ ## Quick Start
23
+
24
+ ```bash
25
+ # List available libraries
26
+ ry-next --list
27
+
28
+ # Get help for a library
29
+ ry-next git --ry-help
30
+
31
+ # Execute augmented command
32
+ ry-next git commit -m "feat: new feature"
33
+
34
+ # Show execution plan (dry run)
35
+ ry-next --ry-run git commit -m "test"
36
+ ```
37
+
38
+ ## Production Libraries
39
+
40
+ - **git** - Enhanced git workflow with review tokens and commit validation
41
+ - **uv** - Python package management with automated version workflows
42
+ - **changelog** - Simple changelog management following Keep a Changelog
43
+ - **ry-lib** - Library development and management tools
44
+
45
+ ## Documentation
46
+
47
+ - [Full Documentation](docs/README_RYNEXT.md)
48
+ - [Library Development](docs/libraries/ry-lib/README.md)
49
+ - [Examples](examples/README.md)
50
+
51
+ ## Project Structure
52
+
53
+ ```
54
+ ry-next/
55
+ ├── src/ry_next/ # Core implementation
56
+ ├── docs/
57
+ │ ├── libraries/ # Production libraries
58
+ │ └── README_RYNEXT.md # Full documentation
59
+ ├── examples/ # Example libraries
60
+ └── _archive/ # Old ry-tool code (deprecated)
61
+ ```
62
+
63
+ ## Key Concepts
64
+
65
+ ### Library Format (v2.0)
66
+
67
+ ```yaml
68
+ version: "2.0"
69
+ name: git
70
+ type: augmentation
71
+ target: /usr/bin/git
72
+
73
+ commands:
74
+ commit:
75
+ flags:
76
+ m/message: string
77
+ augment:
78
+ before:
79
+ - python: |
80
+ # Validation logic
81
+ relay: native
82
+ ```
83
+
84
+ ### Token-Based Safety
85
+
86
+ Critical operations require preview and token verification:
87
+
88
+ ```bash
89
+ # Preview changes
90
+ git diff --staged # → Generates REVIEW_TOKEN
91
+
92
+ # Execute with token
93
+ REVIEW_TOKEN=xxx git commit -m "message"
94
+ ```
95
+
96
+ ## Development
97
+
98
+ See [docs/README_RYNEXT.md](docs/README_RYNEXT.md) for complete documentation.
99
+
100
+ ## License
101
+
102
+ MIT
@@ -0,0 +1,19 @@
1
+ [project]
2
+ name = "ry-tool"
3
+ version = "1.0.1"
4
+ description = "Pure YAML command orchestrator - CI/CD for humans"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Fredrik Angelsen", email = "fredrikangelsen@gmail.com" }
8
+ ]
9
+ requires-python = ">=3.12"
10
+ dependencies = [
11
+ "pyyaml>=6.0",
12
+ ]
13
+
14
+ [project.scripts]
15
+ ry-next = "ry_tool.app:run"
16
+
17
+ [build-system]
18
+ requires = ["uv_build>=0.8.13,<0.9.0"]
19
+ build-backend = "uv_build"
@@ -0,0 +1,27 @@
1
+ """
2
+ ry-next: Next generation command augmentation framework.
3
+
4
+ Clean architecture with semantic command understanding.
5
+ """
6
+
7
+ __version__ = "2.0.0-alpha"
8
+
9
+ from .parser import CommandParser, ParsedCommand
10
+ from .executor import Executor, ExecutionResult
11
+ from .context import ExecutionContext
12
+ from .template import TemplateProcessor
13
+ from .loader import LibraryLoader, LibraryConfig
14
+ from .matcher import CommandMatcher, MatchResult
15
+
16
+ __all__ = [
17
+ 'CommandParser',
18
+ 'ParsedCommand',
19
+ 'Executor',
20
+ 'ExecutionResult',
21
+ 'ExecutionContext',
22
+ 'TemplateProcessor',
23
+ 'LibraryLoader',
24
+ 'LibraryConfig',
25
+ 'CommandMatcher',
26
+ 'MatchResult',
27
+ ]
@@ -0,0 +1,9 @@
1
+ """
2
+ Entry point for ry-next when run as a module.
3
+
4
+ Usage: python -m ry_next
5
+ """
6
+ from .app import run
7
+
8
+ if __name__ == "__main__":
9
+ run()
@@ -0,0 +1,244 @@
1
+ """
2
+ Lightweight CLI framework for ry.
3
+ A mini framework tailored for ry's command parsing needs.
4
+ """
5
+
6
+ import sys
7
+ from typing import Dict, List, Callable, Optional
8
+ from dataclasses import dataclass
9
+
10
+
11
+ @dataclass
12
+ class Command:
13
+ """Represents a CLI command."""
14
+
15
+ name: str
16
+ handler: Callable
17
+ help: str
18
+ requires_arg: bool = False
19
+ arg_name: str = "arg"
20
+ arg_help: str = ""
21
+
22
+
23
+ class CLI:
24
+ """Lightweight CLI framework for ry."""
25
+
26
+ def __init__(self, name: str = "ry", description: str = ""):
27
+ self.name = name
28
+ self.description = description
29
+ self.commands: Dict[str, Command] = {}
30
+ self.default_handler: Optional[Callable] = None
31
+ self.global_flags: Dict[str, bool] = {} # Track global flags
32
+
33
+ def command(
34
+ self,
35
+ name: str,
36
+ help: str = "",
37
+ requires_arg: bool = False,
38
+ arg_name: str = "arg",
39
+ arg_help: str = "",
40
+ ):
41
+ """Decorator to register a command."""
42
+
43
+ def decorator(func: Callable):
44
+ self.commands[name] = Command(
45
+ name=name,
46
+ handler=func,
47
+ help=help,
48
+ requires_arg=requires_arg,
49
+ arg_name=arg_name,
50
+ arg_help=arg_help,
51
+ )
52
+ return func
53
+
54
+ return decorator
55
+
56
+ def default(self, func: Callable):
57
+ """Decorator to register the default handler for non-command arguments."""
58
+ self.default_handler = func
59
+ return func
60
+
61
+ def run(self, argv: Optional[List[str]] = None):
62
+ """Parse arguments and run the appropriate command."""
63
+ if argv is None:
64
+ argv = sys.argv
65
+
66
+ # No arguments - show help
67
+ if len(argv) < 2:
68
+ self.show_help()
69
+ sys.exit(0)
70
+
71
+ # Parse global flags first
72
+ self.global_flags = {}
73
+ filtered_argv = [argv[0]]
74
+
75
+ for arg in argv[1:]:
76
+ if arg == '--ry-run':
77
+ self.global_flags['ry_run'] = True
78
+ else:
79
+ filtered_argv.append(arg)
80
+
81
+ # If only global flags were provided, show help
82
+ if len(filtered_argv) < 2:
83
+ self.show_help()
84
+ sys.exit(0)
85
+
86
+ first_arg = filtered_argv[1]
87
+ remaining_args = filtered_argv[2:] if len(filtered_argv) > 2 else []
88
+
89
+ # Check for help
90
+ if first_arg in ["-h", "--help"]:
91
+ self.show_help()
92
+ sys.exit(0)
93
+
94
+ # Check if it's a registered command
95
+ if first_arg in self.commands:
96
+ cmd = self.commands[first_arg]
97
+
98
+ # Check if command requires an argument
99
+ if cmd.requires_arg and not remaining_args:
100
+ print(f"Error: {first_arg} requires an argument", file=sys.stderr)
101
+ print(
102
+ f"Usage: {self.name} {first_arg} <{cmd.arg_name}>", file=sys.stderr
103
+ )
104
+ sys.exit(1)
105
+
106
+ # Call the handler
107
+ try:
108
+ if cmd.requires_arg:
109
+ result = cmd.handler(remaining_args[0], *remaining_args[1:])
110
+ else:
111
+ result = cmd.handler(*remaining_args)
112
+
113
+ # Handle result
114
+ if isinstance(result, bool):
115
+ sys.exit(0 if result else 1)
116
+ elif isinstance(result, int):
117
+ sys.exit(result)
118
+ else:
119
+ sys.exit(0)
120
+ except KeyboardInterrupt:
121
+ sys.exit(130)
122
+ except Exception as e:
123
+ print(f"Error: {e}", file=sys.stderr)
124
+ sys.exit(1)
125
+
126
+ # Not a command - try default handler
127
+ elif self.default_handler:
128
+ try:
129
+ result = self.default_handler(first_arg, *remaining_args)
130
+ if isinstance(result, bool):
131
+ sys.exit(0 if result else 1)
132
+ elif isinstance(result, int):
133
+ sys.exit(result)
134
+ else:
135
+ sys.exit(0)
136
+ except KeyboardInterrupt:
137
+ sys.exit(130)
138
+ except Exception as e:
139
+ print(f"Error: {e}", file=sys.stderr)
140
+ sys.exit(1)
141
+
142
+ else:
143
+ print(f"Unknown command: {first_arg}", file=sys.stderr)
144
+ print(f"Try: {self.name} --help", file=sys.stderr)
145
+ sys.exit(1)
146
+
147
+ def show_help(self):
148
+ """Display auto-generated help message."""
149
+ lines = []
150
+
151
+ # Header
152
+ lines.append(f"{self.name} - {self.description}")
153
+ lines.append("")
154
+
155
+ # Usage
156
+ lines.append("Usage:")
157
+ if self.default_handler:
158
+ lines.append(
159
+ f" {self.name} <library> [args...] Execute library command"
160
+ )
161
+ lines.append(
162
+ f" {self.name} <file.yaml> [args...] Execute from YAML file"
163
+ )
164
+ lines.append(
165
+ f" {self.name} --ry-run <library> [args...] Show execution plan"
166
+ )
167
+ lines.append("")
168
+
169
+ # Group commands by type
170
+ user_commands = {}
171
+ dev_commands = {}
172
+
173
+ for name, cmd in sorted(self.commands.items()):
174
+ if name.startswith("--dev-"):
175
+ dev_commands[name] = cmd
176
+ else:
177
+ user_commands[name] = cmd
178
+
179
+ # User commands
180
+ if user_commands:
181
+ lines.append("Package Management:")
182
+ for name, cmd in user_commands.items():
183
+ # Format command line
184
+ if cmd.requires_arg:
185
+ usage = f"{self.name} {name} <{cmd.arg_name}>"
186
+ else:
187
+ usage = f"{self.name} {name}"
188
+ # Align help text
189
+ lines.append(f" {usage:<40} {cmd.help}")
190
+ lines.append("")
191
+
192
+ # Developer commands
193
+ if dev_commands:
194
+ lines.append("Developer Commands:")
195
+ for name, cmd in dev_commands.items():
196
+ if cmd.requires_arg:
197
+ usage = f"{self.name} {name} <{cmd.arg_name}>"
198
+ else:
199
+ usage = f"{self.name} {name}"
200
+ lines.append(f" {usage:<40} {cmd.help}")
201
+ lines.append("")
202
+
203
+ # Examples
204
+ lines.append("Examples:")
205
+ lines.append(f" {self.name} hello.yaml world --name Alice")
206
+ lines.append(f" {self.name} git commit -m 'feat: add feature'")
207
+ lines.append(f" {self.name} --ry-run deploy.yaml production")
208
+ lines.append(f" {self.name} --list")
209
+
210
+ print("\n".join(lines))
211
+
212
+
213
+ # Utility decorators for common CLI patterns
214
+ def requires_git_repo(func):
215
+ """Decorator to ensure command runs in a git repository."""
216
+ from functools import wraps
217
+
218
+ @wraps(func)
219
+ def wrapper(*args, **kwargs):
220
+ import subprocess
221
+ try:
222
+ subprocess.run(['git', 'rev-parse', '--git-dir'],
223
+ capture_output=True, check=True)
224
+ return func(*args, **kwargs)
225
+ except subprocess.CalledProcessError:
226
+ print("Error: Not in a git repository", file=sys.stderr)
227
+ return 1
228
+ return wrapper
229
+
230
+
231
+ def requires_file(file_path: str):
232
+ """Decorator to ensure required file exists."""
233
+ def decorator(func):
234
+ from functools import wraps
235
+
236
+ @wraps(func)
237
+ def wrapper(*args, **kwargs):
238
+ from pathlib import Path
239
+ if not Path(file_path).exists():
240
+ print(f"Error: Required file not found: {file_path}", file=sys.stderr)
241
+ return 1
242
+ return func(*args, **kwargs)
243
+ return wrapper
244
+ return decorator