cliops 1.0.0__py3-none-any.whl → 1.0.2__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.
- {cliops-1.0.0.dist-info → cliops-1.0.2.dist-info}/METADATA +1 -1
- cliops-1.0.2.dist-info/RECORD +14 -0
- cliops-1.0.2.dist-info/top_level.txt +3 -0
- main.py +177 -0
- presets.py +93 -0
- cliops-1.0.0.dist-info/RECORD +0 -12
- cliops-1.0.0.dist-info/top_level.txt +0 -1
- {cliops-1.0.0.dist-info → cliops-1.0.2.dist-info}/WHEEL +0 -0
- {cliops-1.0.0.dist-info → cliops-1.0.2.dist-info}/entry_points.txt +0 -0
- {cliops-1.0.0.dist-info → cliops-1.0.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,14 @@
|
|
1
|
+
main.py,sha256=YwzFPWB6QDtjK7r9gKA9tixEgD_7_b7gXoj6Q2ETCqk,9255
|
2
|
+
presets.py,sha256=DKIfhwxtBZEC5fYHgXqhuBKou7KYQks_2M8cR3IeWao,3172
|
3
|
+
cliops-1.0.2.dist-info/licenses/LICENSE,sha256=2J5KKebeJ2AdMaxuYA1fX0ZuDs9MmWp3cpjZX-JqrZs,1079
|
4
|
+
core/__init__.py,sha256=aWl7MZaubJNqrafNCM5VRYg4SZ7sdifrxTWrJC8d-_s,24
|
5
|
+
core/analyzer.py,sha256=mwMmrlV-Pk31mMfKs0NPqBqY3cpNwCq_DWwGaurjPzI,4328
|
6
|
+
core/config.py,sha256=aoYCLt-EFM7WiR3_QN049_cfz9_SODuqnErte07DTVM,1388
|
7
|
+
core/optimizer.py,sha256=6hGdelQjWb71WAdXWr89EvYNinzb2gETQgaB3APuoV8,6196
|
8
|
+
core/patterns.py,sha256=BWiwb5fjQbWHLsJ68p0QbtSddFqzhYx3AeLkSY-WKbU,6464
|
9
|
+
core/state.py,sha256=dHdA9U0LNgGzyS_bSlcotjfpnwZGtMHfEz5JEbUqwf8,2055
|
10
|
+
cliops-1.0.2.dist-info/METADATA,sha256=2bxAK7TdiiqRIVCvBpIxgr2uGoQXPC5H14SFsiYmsRA,3818
|
11
|
+
cliops-1.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
12
|
+
cliops-1.0.2.dist-info/entry_points.txt,sha256=F8ZncVlnk83ELj0TGXUYS-qYvmaP-owQyU20I7jRefg,37
|
13
|
+
cliops-1.0.2.dist-info/top_level.txt,sha256=dV-NRp_bb_IkCFfEXDbA1mHA5SshVNbUsxaF809itG8,18
|
14
|
+
cliops-1.0.2.dist-info/RECORD,,
|
main.py
ADDED
@@ -0,0 +1,177 @@
|
|
1
|
+
import argparse
|
2
|
+
import sys
|
3
|
+
import os
|
4
|
+
from rich.console import Console
|
5
|
+
from rich.panel import Panel
|
6
|
+
from rich.syntax import Syntax
|
7
|
+
from rich.text import Text
|
8
|
+
|
9
|
+
from core.config import Config
|
10
|
+
from core.state import CLIState
|
11
|
+
from core.patterns import PatternRegistry
|
12
|
+
from core.analyzer import PromptAnalyzer
|
13
|
+
from core.optimizer import PromptOptimizer
|
14
|
+
from presets import suggest_preset_from_prompt, apply_preset_interactive
|
15
|
+
|
16
|
+
console = Console()
|
17
|
+
|
18
|
+
def _define_subparsers(subparsers):
|
19
|
+
# Optimize command
|
20
|
+
optimize_parser = subparsers.add_parser("optimize", aliases=['opt'], help="Optimize a raw prompt using a specified pattern.")
|
21
|
+
optimize_parser.add_argument("prompt", type=str, nargs='?', help="The raw prompt string to optimize. If omitted, tries to read from stdin.")
|
22
|
+
optimize_parser.add_argument("--pattern", type=str, required=False, help="The name of the optimization pattern to apply.")
|
23
|
+
optimize_parser.add_argument("--dry-run", action="store_true", help="Show parsed fields and final template before generation.")
|
24
|
+
|
25
|
+
# Common override arguments
|
26
|
+
optimize_parser.add_argument("--directive", type=str, help="Override the 'DIRECTIVE' field.")
|
27
|
+
optimize_parser.add_argument("--scope", type=str, help="Override the 'SCOPE' field.")
|
28
|
+
optimize_parser.add_argument("--constraints", type=str, help="Override the 'CONSTRAINTS' field.")
|
29
|
+
optimize_parser.add_argument("--output-format", type=str, help="Override the 'OUTPUT FORMAT' field.")
|
30
|
+
optimize_parser.add_argument("--success-criteria", type=str, help="Override the 'SUCCESS CRITERIA' field.")
|
31
|
+
optimize_parser.add_argument("--code", type=str, help="Override the 'CODE' block content.")
|
32
|
+
optimize_parser.add_argument("--context", type=str, help="Override the 'CONTEXT' for context-aware patterns.")
|
33
|
+
optimize_parser.add_argument("--current-focus", type=str, help="Override 'CURRENT FOCUS' for context-aware patterns.")
|
34
|
+
optimize_parser.add_argument("--mindset", type=str, help="Override 'MINDSET' for context-aware patterns.")
|
35
|
+
|
36
|
+
# Analyze command
|
37
|
+
analyze_parser = subparsers.add_parser("analyze", aliases=['an'], help="Analyze a raw prompt for optimization recommendations.")
|
38
|
+
analyze_parser.add_argument("prompt", type=str, nargs='?', help="The raw prompt string to analyze.")
|
39
|
+
|
40
|
+
# Patterns command
|
41
|
+
patterns_parser = subparsers.add_parser("patterns", aliases=['ls'], help="List available optimization patterns.")
|
42
|
+
patterns_parser.add_argument("pattern_name", nargs="?", type=str, help="Optional: Name of a specific pattern to get details for.")
|
43
|
+
|
44
|
+
# State command
|
45
|
+
state_parser = subparsers.add_parser("state", help="Manage persistent CLI state.")
|
46
|
+
state_subparsers = state_parser.add_subparsers(dest="state_command", help="State commands")
|
47
|
+
|
48
|
+
state_set_parser = state_subparsers.add_parser("set", help="Set a key-value pair in the CLI state.")
|
49
|
+
state_set_parser.add_argument("key", type=str, help="The key for the state variable.")
|
50
|
+
state_set_parser.add_argument("value", type=str, help="The value to set.")
|
51
|
+
|
52
|
+
state_show_parser = state_subparsers.add_parser("show", help="Show the current CLI state.")
|
53
|
+
state_clear_parser = state_subparsers.add_parser("clear", help="Clear all entries from the CLI state.")
|
54
|
+
|
55
|
+
# Init command
|
56
|
+
init_parser = subparsers.add_parser("init", help="Initialize CliOps configuration files.")
|
57
|
+
|
58
|
+
def _run_init_command(cli_state: CLIState, pattern_registry: PatternRegistry):
|
59
|
+
"""Handles the 'cliops init' command to set up config files."""
|
60
|
+
console.print(Panel("[bold green]Initializing CliOps Configuration[/bold green]", expand=False, border_style="green"))
|
61
|
+
|
62
|
+
state_file = Config.get_state_file_path()
|
63
|
+
if not state_file.exists() or cli_state.state == {}:
|
64
|
+
state_file.parent.mkdir(parents=True, exist_ok=True)
|
65
|
+
cli_state.clear()
|
66
|
+
console.print(f"Created/reset CLI state file: [bold cyan]{state_file}[/bold cyan]", style="green")
|
67
|
+
else:
|
68
|
+
console.print(f"CLI state file already exists at [bold cyan]{state_file}[/bold cyan].", style="dim")
|
69
|
+
|
70
|
+
console.print(Panel("[bold green]Initialization complete![/bold green]", expand=False, border_style="green"))
|
71
|
+
|
72
|
+
def main():
|
73
|
+
# Create a dummy parser to peek at the arguments
|
74
|
+
temp_parser = argparse.ArgumentParser(add_help=False)
|
75
|
+
temp_parser.add_argument("first_arg", nargs='?', help=argparse.SUPPRESS)
|
76
|
+
temp_parser.add_argument("--verbose", action="store_true", help=argparse.SUPPRESS)
|
77
|
+
|
78
|
+
temp_args, remaining_argv = temp_parser.parse_known_args(sys.argv[1:])
|
79
|
+
verbose_mode = temp_args.verbose
|
80
|
+
|
81
|
+
known_commands = ["optimize", "opt", "analyze", "an", "patterns", "ls", "state", "init"]
|
82
|
+
|
83
|
+
# Check if the first argument looks like a prompt
|
84
|
+
is_direct_prompt = (temp_args.first_arg and
|
85
|
+
temp_args.first_arg not in known_commands and
|
86
|
+
not temp_args.first_arg.startswith('-'))
|
87
|
+
|
88
|
+
if is_direct_prompt:
|
89
|
+
new_argv = ["optimize", temp_args.first_arg] + remaining_argv
|
90
|
+
sys.argv = [sys.argv[0]] + new_argv
|
91
|
+
|
92
|
+
# Set up the real parser
|
93
|
+
parser = argparse.ArgumentParser(
|
94
|
+
description="CliOps: Command Line Interface for Prompt Optimization and State Management (v3.2)",
|
95
|
+
formatter_class=argparse.RawTextHelpFormatter
|
96
|
+
)
|
97
|
+
parser.add_argument("--verbose", action="store_true", help="Enable verbose output for debugging.")
|
98
|
+
|
99
|
+
subparsers = parser.add_subparsers(dest="command", help="Available commands")
|
100
|
+
_define_subparsers(subparsers)
|
101
|
+
|
102
|
+
args = parser.parse_args()
|
103
|
+
|
104
|
+
if hasattr(args, 'verbose') and args.verbose:
|
105
|
+
verbose_mode = True
|
106
|
+
|
107
|
+
cli_state = CLIState(Config.get_state_file_path())
|
108
|
+
pattern_registry = PatternRegistry(cli_state)
|
109
|
+
|
110
|
+
# Handle cases where prompt might be piped via stdin
|
111
|
+
if hasattr(args, 'prompt') and args.prompt is None and not sys.stdin.isatty():
|
112
|
+
args.prompt = sys.stdin.read().strip()
|
113
|
+
if not args.prompt:
|
114
|
+
console.print("[bold red]Error:[/bold red] No prompt provided via argument or stdin.", style="red")
|
115
|
+
sys.exit(1)
|
116
|
+
elif hasattr(args, 'prompt') and args.prompt is None and sys.stdin.isatty() and args.command in ["optimize", "analyze"]:
|
117
|
+
console.print(f"[bold red]Error:[/bold red] Missing prompt argument for '{args.command}' command.", style="red")
|
118
|
+
parser.print_help(sys.stderr)
|
119
|
+
sys.exit(1)
|
120
|
+
|
121
|
+
# Set default pattern if not specified
|
122
|
+
if args.command in ["optimize", "opt"] and (not hasattr(args, 'pattern') or args.pattern is None):
|
123
|
+
default_pattern = cli_state.get("DEFAULT_PATTERN")
|
124
|
+
if default_pattern and pattern_registry.get_pattern(default_pattern):
|
125
|
+
args.pattern = default_pattern
|
126
|
+
else:
|
127
|
+
args.pattern = "context_aware_generation"
|
128
|
+
|
129
|
+
if args.command in ["optimize", "opt"]:
|
130
|
+
overrides = {k: v for k, v in vars(args).items() if v is not None and k not in ['command', 'prompt', 'pattern', 'dry_run', 'verbose']}
|
131
|
+
|
132
|
+
try:
|
133
|
+
optimizer = PromptOptimizer(pattern_registry, cli_state, verbose=verbose_mode)
|
134
|
+
optimized_prompt = optimizer.optimize_prompt(args.prompt, args.pattern, overrides, args.dry_run)
|
135
|
+
|
136
|
+
if not args.dry_run:
|
137
|
+
console.print(Panel("[bold green]Optimized Prompt[/bold green]", expand=False, border_style="green"))
|
138
|
+
console.print(Syntax(optimized_prompt, "markdown", theme="monokai", line_numbers=True, word_wrap=True))
|
139
|
+
console.print(Panel.fit("[dim]Prompt Optimization Complete[/dim]", border_style="dim"))
|
140
|
+
|
141
|
+
except ValueError as e:
|
142
|
+
console.print(f"[bold red]Error:[/bold red] {e}", style="red")
|
143
|
+
sys.exit(1)
|
144
|
+
|
145
|
+
elif args.command in ["analyze", "an"]:
|
146
|
+
analyzer = PromptAnalyzer(pattern_registry)
|
147
|
+
analyzer.analyze_prompt(args.prompt)
|
148
|
+
elif args.command in ["patterns", "ls"]:
|
149
|
+
if args.pattern_name:
|
150
|
+
pattern = pattern_registry.get_pattern(args.pattern_name)
|
151
|
+
if pattern:
|
152
|
+
console.print(Panel(f"[bold blue]Pattern Details: {pattern.name}[/bold blue]", expand=False, border_style="blue"))
|
153
|
+
console.print(f"[bold]Description:[/bold] {pattern.description}")
|
154
|
+
console.print(f"[bold]Principles:[/bold] {', '.join(pattern.principles)}")
|
155
|
+
console.print(Panel(
|
156
|
+
Syntax(pattern.template, "jinja2", theme="monokai", line_numbers=True, word_wrap=True),
|
157
|
+
title="[bold]Template[/bold]", border_style="dim"
|
158
|
+
))
|
159
|
+
else:
|
160
|
+
console.print(f"[bold red]Error:[/bold red] Pattern '{args.pattern_name}' not found.", style="red")
|
161
|
+
pattern_registry.list_patterns()
|
162
|
+
else:
|
163
|
+
pattern_registry.list_patterns()
|
164
|
+
elif args.command == "state":
|
165
|
+
if args.state_command == "set":
|
166
|
+
cli_state.set(args.key, args.value)
|
167
|
+
elif args.state_command == "show":
|
168
|
+
cli_state.show()
|
169
|
+
elif args.state_command == "clear":
|
170
|
+
cli_state.clear()
|
171
|
+
elif args.command == "init":
|
172
|
+
_run_init_command(cli_state, pattern_registry)
|
173
|
+
else:
|
174
|
+
parser.print_help()
|
175
|
+
|
176
|
+
if __name__ == "__main__":
|
177
|
+
main()
|
presets.py
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
# presets.py
|
2
|
+
import json
|
3
|
+
import re
|
4
|
+
from pathlib import Path
|
5
|
+
|
6
|
+
BUILTIN_PRESETS = {
|
7
|
+
"react-tailwind": {
|
8
|
+
"ARCHITECTURE": "React + Tailwind CSS",
|
9
|
+
"FOCUS": "UI layout",
|
10
|
+
"PATTERNS": "context_aware_generation, state_anchoring",
|
11
|
+
"DEFAULT_PATTERN": "context_aware_generation"
|
12
|
+
},
|
13
|
+
"django-api": {
|
14
|
+
"ARCHITECTURE": "Django REST Framework",
|
15
|
+
"FOCUS": "API endpoints and serializers",
|
16
|
+
"PATTERNS": "bug_fix_precision",
|
17
|
+
"DEFAULT_PATTERN": "bug_fix_precision"
|
18
|
+
},
|
19
|
+
"bash-script": {
|
20
|
+
"ARCHITECTURE": "Shell scripting",
|
21
|
+
"FOCUS": "Automation and CLI behavior",
|
22
|
+
"PATTERNS": "context_aware_generation",
|
23
|
+
"DEFAULT_PATTERN": "context_aware_generation"
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
CUSTOM_PRESET_FILE = Path.home() / ".cliops_presets.json"
|
28
|
+
|
29
|
+
def suggest_preset_from_prompt(prompt: str) -> str | None:
|
30
|
+
prompt = prompt.lower()
|
31
|
+
if "tailwind" in prompt or "jsx" in prompt:
|
32
|
+
return "react-tailwind"
|
33
|
+
if "endpoint" in prompt or "serializer" in prompt or "django" in prompt:
|
34
|
+
return "django-api"
|
35
|
+
if re.search(r'#!/bin/bash|\\.sh|chmod|\\bcron\\b', prompt):
|
36
|
+
return "bash-script"
|
37
|
+
return None
|
38
|
+
|
39
|
+
def save_custom_preset(name, preset_dict):
|
40
|
+
if CUSTOM_PRESET_FILE.exists():
|
41
|
+
with open(CUSTOM_PRESET_FILE, 'r') as f:
|
42
|
+
all_presets = json.load(f)
|
43
|
+
else:
|
44
|
+
all_presets = {}
|
45
|
+
all_presets[name] = preset_dict
|
46
|
+
with open(CUSTOM_PRESET_FILE, 'w') as f:
|
47
|
+
json.dump(all_presets, f, indent=2)
|
48
|
+
print(f"✅ Custom preset '{name}' saved.")
|
49
|
+
|
50
|
+
def apply_preset_interactive(suggested_name, state):
|
51
|
+
print(f"\n🤖 Suggested preset based on your prompt: {suggested_name}")
|
52
|
+
use = input("Would you like to use this preset? (y/n): ").strip().lower()
|
53
|
+
if use != 'y':
|
54
|
+
print("❌ Preset declined.")
|
55
|
+
return
|
56
|
+
|
57
|
+
fields = {}
|
58
|
+
for key in BUILTIN_PRESETS.get(suggested_name, {}):
|
59
|
+
default = BUILTIN_PRESETS[suggested_name][key]
|
60
|
+
user_val = input(f"{key} [{default}]: ").strip()
|
61
|
+
fields[key] = user_val or default
|
62
|
+
state.set(key, fields[key])
|
63
|
+
|
64
|
+
save = input("Save this as a custom preset? (y/n): ").strip().lower()
|
65
|
+
if save == 'y':
|
66
|
+
name = input("Enter a name for your preset: ").strip()
|
67
|
+
if name:
|
68
|
+
save_custom_preset(name, fields)
|
69
|
+
|
70
|
+
def list_all_presets():
|
71
|
+
print("\n📦 Available Presets:")
|
72
|
+
for name in BUILTIN_PRESETS:
|
73
|
+
print(f" - {name} (built-in)")
|
74
|
+
if CUSTOM_PRESET_FILE.exists():
|
75
|
+
with open(CUSTOM_PRESET_FILE, 'r') as f:
|
76
|
+
custom = json.load(f)
|
77
|
+
for name in custom:
|
78
|
+
print(f" - {name} (custom)")
|
79
|
+
|
80
|
+
|
81
|
+
def apply_named_preset(name, state):
|
82
|
+
preset = BUILTIN_PRESETS.get(name)
|
83
|
+
if not preset and CUSTOM_PRESET_FILE.exists():
|
84
|
+
with open(CUSTOM_PRESET_FILE, 'r') as f:
|
85
|
+
preset = json.load(f).get(name)
|
86
|
+
|
87
|
+
if not preset:
|
88
|
+
print(f"❌ Preset '{name}' not found.")
|
89
|
+
return
|
90
|
+
|
91
|
+
for key, value in preset.items():
|
92
|
+
state.set(key, value)
|
93
|
+
print(f"✅ Preset '{name}' applied.")
|
cliops-1.0.0.dist-info/RECORD
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
cliops-1.0.0.dist-info/licenses/LICENSE,sha256=2J5KKebeJ2AdMaxuYA1fX0ZuDs9MmWp3cpjZX-JqrZs,1079
|
2
|
-
core/__init__.py,sha256=aWl7MZaubJNqrafNCM5VRYg4SZ7sdifrxTWrJC8d-_s,24
|
3
|
-
core/analyzer.py,sha256=mwMmrlV-Pk31mMfKs0NPqBqY3cpNwCq_DWwGaurjPzI,4328
|
4
|
-
core/config.py,sha256=aoYCLt-EFM7WiR3_QN049_cfz9_SODuqnErte07DTVM,1388
|
5
|
-
core/optimizer.py,sha256=6hGdelQjWb71WAdXWr89EvYNinzb2gETQgaB3APuoV8,6196
|
6
|
-
core/patterns.py,sha256=BWiwb5fjQbWHLsJ68p0QbtSddFqzhYx3AeLkSY-WKbU,6464
|
7
|
-
core/state.py,sha256=dHdA9U0LNgGzyS_bSlcotjfpnwZGtMHfEz5JEbUqwf8,2055
|
8
|
-
cliops-1.0.0.dist-info/METADATA,sha256=GgUh6K4F4_xXeJXKV2Jq8f1DtFQvpvLwuO-pp7oqlSI,3818
|
9
|
-
cliops-1.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
10
|
-
cliops-1.0.0.dist-info/entry_points.txt,sha256=F8ZncVlnk83ELj0TGXUYS-qYvmaP-owQyU20I7jRefg,37
|
11
|
-
cliops-1.0.0.dist-info/top_level.txt,sha256=0ebp8k35dOTHVwbY3UcokRsjC7cWLN_iaJ70tIcmODU,5
|
12
|
-
cliops-1.0.0.dist-info/RECORD,,
|
@@ -1 +0,0 @@
|
|
1
|
-
core
|
File without changes
|
File without changes
|
File without changes
|