cliops 3.2.2__py3-none-any.whl → 4.0.1__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.
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cliops
3
- Version: 3.2.2
3
+ Version: 4.0.1
4
4
  Summary: Advanced CLI tool for structured, pattern-based prompt optimization and state management
5
- Home-page: https://github.com/cliops/cliops
5
+ Home-page: https://github.com/iammabd/cliops
6
6
  Author: cliops by mabd
7
7
  Author-email: iammabd@substack.com
8
- Project-URL: Bug Reports, https://github.com/cliops/cliops/issues
9
- Project-URL: Source, https://github.com/cliops/cliops
10
- Project-URL: Documentation, https://cliops.readthedocs.io
8
+ Project-URL: Bug Reports, https://github.com/iammabd/cliops/issues
9
+ Project-URL: Source, https://github.com/iammabd/cliops
10
+ Project-URL: Documentation, https://cliops.hashnode.space
11
11
  Keywords: cli prompt optimization ai llm prompt-engineering patterns state-management
12
12
  Classifier: Development Status :: 5 - Production/Stable
13
13
  Classifier: Intended Audience :: Developers
@@ -29,6 +29,9 @@ Requires-Python: >=3.8
29
29
  Description-Content-Type: text/markdown
30
30
  License-File: LICENSE
31
31
  Requires-Dist: rich>=13.0.0
32
+ Requires-Dist: jinja2>=3.0.0
33
+ Requires-Dist: pydantic>=2.0.0
34
+ Requires-Dist: typing-extensions>=4.0.0
32
35
  Provides-Extra: dev
33
36
  Requires-Dist: pytest>=7.0.0; extra == "dev"
34
37
  Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
@@ -51,7 +54,7 @@ Dynamic: requires-dist
51
54
  Dynamic: requires-python
52
55
  Dynamic: summary
53
56
 
54
- # CliOps - Command Line Interface for Prompt Optimization
57
+ # Cliops - Command Line Interface for Prompt Optimization
55
58
 
56
59
  A powerful CLI tool for structured, pattern-based prompt optimization and state management.
57
60
 
@@ -0,0 +1,18 @@
1
+ main.py,sha256=eLhDnSUOgWM0-wQ1UkfLduEw6NXMs8b5JuVxzmg0QIk,9828
2
+ presets.py,sha256=DKIfhwxtBZEC5fYHgXqhuBKou7KYQks_2M8cR3IeWao,3172
3
+ cliops-4.0.1.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/branding.py,sha256=B8afUp8mxh1yNzM3cTFLZ9uA_iwKy6wu3N1ZbK6ahss,989
7
+ core/cache.py,sha256=uDUPRr7Q5gLHl_sUzsybt2nXBl9TSsbo92K8stbs1Pk,1658
8
+ core/config.py,sha256=aoYCLt-EFM7WiR3_QN049_cfz9_SODuqnErte07DTVM,1388
9
+ core/optimizer.py,sha256=n72DSf4pc4dqW7g8KPH1GOw8W8qpdi6q45tI02miuok,7762
10
+ core/patterns.py,sha256=CW2uFm1z8pCeQCio2oT39vkEJ9z_ifF-oRJtoufcyEM,9310
11
+ core/setup.py,sha256=gP6IuoAbrUKpwHQIwlHHufZyd7F3ZUtg-B3TJgWmCAE,2865
12
+ core/state.py,sha256=pWAdv085KSc6V7Idy3DdVCL35QbICNcjO3chi09yvOU,2654
13
+ core/validation.py,sha256=2wTVQE9MsHeN_3WhV0EiixHmFd3EXzV5t67TS_auT9I,1896
14
+ cliops-4.0.1.dist-info/METADATA,sha256=12DKQv6mgdBz0FaJ3xEpEYkBRdIIzEtTC1WgWeU92OI,4182
15
+ cliops-4.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
+ cliops-4.0.1.dist-info/entry_points.txt,sha256=F8ZncVlnk83ELj0TGXUYS-qYvmaP-owQyU20I7jRefg,37
17
+ cliops-4.0.1.dist-info/top_level.txt,sha256=dV-NRp_bb_IkCFfEXDbA1mHA5SshVNbUsxaF809itG8,18
18
+ cliops-4.0.1.dist-info/RECORD,,
core/branding.py ADDED
@@ -0,0 +1,34 @@
1
+ from rich.console import Console
2
+ from rich.panel import Panel
3
+ from rich.text import Text
4
+
5
+ console = Console()
6
+
7
+ ASCII_LOGO = r"""
8
+ _____ _ _____ ____ _____ _____
9
+ / ____| | |_ _/ __ \| __ \ / ____|
10
+ | | | | | || | | | |__) | (___
11
+ | | | | | || | | | ___/ \___ \
12
+ | |____| |____ _| || |__| | | ____) |
13
+ \_____|______|_____\____/|_| |_____/
14
+ """
15
+
16
+ def show_banner():
17
+ """Display CLIOPS ASCII art banner"""
18
+ logo_text = Text(ASCII_LOGO, style="bold cyan")
19
+ tagline = Text("Command Line Interface for Prompt Optimization", style="dim italic")
20
+
21
+ console.print()
22
+ console.print(logo_text, justify="center")
23
+ console.print(tagline, justify="center")
24
+ console.print()
25
+
26
+ def show_input_frame(prompt_text: str) -> str:
27
+ """Display framed input prompt"""
28
+ panel = Panel(
29
+ f"[bold white]{prompt_text}[/bold white]",
30
+ border_style="white",
31
+ padding=(0, 1)
32
+ )
33
+ console.print(panel)
34
+ return input(">>> ")
core/cache.py ADDED
@@ -0,0 +1,51 @@
1
+ import json
2
+ import hashlib
3
+ from pathlib import Path
4
+ from typing import Any, Optional
5
+ from .config import Config
6
+
7
+ class Cache:
8
+ def __init__(self):
9
+ self.cache_dir = Config.get_app_data_dir() / "cache"
10
+ self.cache_dir.mkdir(parents=True, exist_ok=True)
11
+ self._memory_cache = {}
12
+
13
+ def _get_cache_key(self, key: str) -> str:
14
+ """Generate cache key hash"""
15
+ return hashlib.md5(key.encode()).hexdigest()
16
+
17
+ def get(self, key: str) -> Optional[Any]:
18
+ """Get cached value"""
19
+ # Check memory cache first
20
+ if key in self._memory_cache:
21
+ return self._memory_cache[key]
22
+
23
+ # Check disk cache
24
+ cache_file = self.cache_dir / f"{self._get_cache_key(key)}.json"
25
+ if cache_file.exists():
26
+ try:
27
+ with open(cache_file, 'r') as f:
28
+ data = json.load(f)
29
+ self._memory_cache[key] = data
30
+ return data
31
+ except (json.JSONDecodeError, IOError):
32
+ cache_file.unlink(missing_ok=True)
33
+
34
+ return None
35
+
36
+ def set(self, key: str, value: Any):
37
+ """Set cached value"""
38
+ self._memory_cache[key] = value
39
+
40
+ cache_file = self.cache_dir / f"{self._get_cache_key(key)}.json"
41
+ try:
42
+ with open(cache_file, 'w') as f:
43
+ json.dump(value, f)
44
+ except (IOError, TypeError):
45
+ pass # Fail silently for caching
46
+
47
+ def clear(self):
48
+ """Clear all cache"""
49
+ self._memory_cache.clear()
50
+ for cache_file in self.cache_dir.glob("*.json"):
51
+ cache_file.unlink(missing_ok=True)
core/optimizer.py CHANGED
@@ -1,10 +1,13 @@
1
1
  import re
2
+ from jinja2 import Template, TemplateError
2
3
  from rich.console import Console
3
4
  from rich.panel import Panel
4
5
  from rich.table import Table
5
6
  from rich.syntax import Syntax
6
7
  from rich.text import Text
7
8
  from rich import box
9
+ from .validation import PromptValidator
10
+ from .cache import Cache
8
11
 
9
12
  console = Console()
10
13
 
@@ -14,6 +17,8 @@ class PromptOptimizer:
14
17
  self.pattern_registry = pattern_registry
15
18
  self.cli_state = cli_state
16
19
  self.verbose = verbose
20
+ self.cache = Cache()
21
+ self.validator = PromptValidator()
17
22
 
18
23
  def _parse_prompt_into_sections(self, raw_prompt: str) -> dict:
19
24
  """Parses a raw prompt into a dictionary of sections based on '## SECTION:' headers and <TAG>...</TAG> blocks."""
@@ -89,12 +94,28 @@ class PromptOptimizer:
89
94
 
90
95
  def optimize_prompt(self, raw_prompt: str, pattern_name: str, overrides: dict, dry_run: bool = False) -> str:
91
96
  """Applies an optimization pattern to a raw prompt."""
92
- pattern = self.pattern_registry.get_pattern(pattern_name)
93
- if not pattern:
94
- raise ValueError(f"Pattern '{pattern_name}' not found.")
97
+ try:
98
+ # Validate and sanitize input
99
+ raw_prompt = self.validator.sanitize_input(raw_prompt)
100
+ pattern_name = self.validator.validate_pattern_name(pattern_name)
101
+
102
+ # Check cache first
103
+ cache_key = f"{pattern_name}:{hash(raw_prompt)}:{hash(str(overrides))}"
104
+ cached_result = self.cache.get(cache_key)
105
+ if cached_result and not dry_run:
106
+ return cached_result
107
+
108
+ pattern = self.pattern_registry.get_pattern(pattern_name)
109
+ if not pattern:
110
+ available = list(self.pattern_registry.patterns.keys())
111
+ raise ValueError(f"Pattern '{pattern_name}' not found. Available: {', '.join(available)}")
95
112
 
96
- # Extract components from raw_prompt
97
- extracted_fields = self._extract_components(raw_prompt, pattern)
113
+ # Extract components from raw_prompt
114
+ extracted_fields = self._extract_components(raw_prompt, pattern)
115
+
116
+ except Exception as e:
117
+ console.print(f"[bold red]Error:[/bold red] {str(e)}", style="red")
118
+ raise ValueError(f"Input validation failed: {str(e)}")
98
119
 
99
120
  # Prepare fields for template formatting
100
121
  template_fields = {}
@@ -144,7 +165,17 @@ class PromptOptimizer:
144
165
  return "Dry run complete. No prompt generated."
145
166
 
146
167
  try:
147
- optimized_prompt = pattern.template.format(**template_fields)
168
+ # Use Jinja2 template rendering
169
+ template = Template(pattern.template)
170
+ optimized_prompt = template.render(**template_fields)
171
+
172
+ # Cache the result
173
+ if not dry_run:
174
+ self.cache.set(cache_key, optimized_prompt)
175
+
148
176
  return optimized_prompt
149
- except KeyError as e:
150
- raise ValueError(f"Template for pattern '{pattern_name}' missing field {e}.")
177
+
178
+ except TemplateError as e:
179
+ raise ValueError(f"Template rendering failed for pattern '{pattern_name}': {str(e)}")
180
+ except Exception as e:
181
+ raise ValueError(f"Optimization failed: {str(e)}")
core/patterns.py CHANGED
@@ -1,11 +1,14 @@
1
1
  import json
2
2
  import re
3
3
  from pathlib import Path
4
+ from jinja2 import Template, TemplateError
4
5
  from rich.console import Console
5
6
  from rich.table import Table
6
7
  from rich.syntax import Syntax
7
8
  from rich import box
8
9
  from .config import Config
10
+ from .cache import Cache
11
+ from .validation import PromptValidator
9
12
 
10
13
  console = Console()
11
14
 
@@ -43,6 +46,7 @@ class PatternRegistry:
43
46
  def __init__(self, cli_state):
44
47
  self.patterns: dict[str, OptimizationPattern] = {}
45
48
  self.cli_state = cli_state
49
+ self.cache = Cache()
46
50
  self._load_default_patterns()
47
51
  self._load_user_patterns()
48
52
 
@@ -77,28 +81,80 @@ class PatternRegistry:
77
81
  default_patterns_data = [
78
82
  {"name": "context_aware_generation",
79
83
  "description": "Guides generation based on specific context, mindset, and current focus.",
80
- "template": "# DIRECTIVE: {directive}\n\n"
84
+ "template": "# DIRECTIVE: {{ directive }}\n\n"
81
85
  "## CONTEXT:\n"
82
- "<CONTEXT>{context}</CONTEXT>\n\n"
86
+ "<CONTEXT>{{ context }}</CONTEXT>\n\n"
83
87
  "## CURRENT FOCUS:\n"
84
- "{current_focus}\n\n"
88
+ "{{ current_focus }}\n\n"
85
89
  "## MINDSET:\n"
86
- "{mindset}\n\n"
90
+ "{{ mindset }}\n\n"
87
91
  "## CONSTRAINTS:\n"
88
- "{constraints}\n\n"
92
+ "{{ constraints }}\n\n"
89
93
  "## OUTPUT FORMAT:\n"
90
- "{output_format}\n\n"
91
- "## EXAMPLES:\n"
92
- "{examples}\n\n"
94
+ "{{ output_format }}\n\n"
93
95
  "## SUCCESS CRITERIA:\n"
94
- "{success_criteria}\n\n"
96
+ "{{ success_criteria }}\n\n"
95
97
  "## STATE:\n"
96
- "Project Architecture: {STATE.ARCHITECTURE}\n"
97
- "Common Patterns: {STATE.PATTERNS}\n"
98
- "Current Project Focus: {STATE.FOCUS}\n\n"
99
- "{code_here}",
98
+ "Project Architecture: {{ STATE.ARCHITECTURE }}\n"
99
+ "Common Patterns: {{ STATE.PATTERNS }}\n"
100
+ "Current Project Focus: {{ STATE.FOCUS }}\n\n"
101
+ "{{ code_here }}",
100
102
  "principles": ["Context-Aware Generation", "Adaptive Nuance", "State Anchoring"],
101
- "specific_extract_func": specific_extract_context_aware}
103
+ "specific_extract_func": specific_extract_context_aware},
104
+
105
+ {"name": "bug_fix_precision",
106
+ "description": "Structured approach for precise bug identification and resolution.",
107
+ "template": "# BUG FIX REQUEST\n\n"
108
+ "## PROBLEM DESCRIPTION:\n"
109
+ "{{ directive }}\n\n"
110
+ "## CURRENT CODE:\n"
111
+ "```\n{{ code_here }}\n```\n\n"
112
+ "## EXPECTED BEHAVIOR:\n"
113
+ "{{ success_criteria }}\n\n"
114
+ "## CONSTRAINTS:\n"
115
+ "{{ constraints }}\n\n"
116
+ "## ARCHITECTURE CONTEXT:\n"
117
+ "{{ STATE.ARCHITECTURE }}\n\n"
118
+ "## OUTPUT FORMAT:\n"
119
+ "{{ output_format }}",
120
+ "principles": ["Precision", "Root Cause Analysis", "Minimal Changes"]},
121
+
122
+ {"name": "code_review",
123
+ "description": "Comprehensive code review with security and performance focus.",
124
+ "template": "# CODE REVIEW REQUEST\n\n"
125
+ "## CODE TO REVIEW:\n"
126
+ "```\n{{ code_here }}\n```\n\n"
127
+ "## REVIEW FOCUS:\n"
128
+ "{{ directive }}\n\n"
129
+ "## ARCHITECTURE:\n"
130
+ "{{ STATE.ARCHITECTURE }}\n\n"
131
+ "## REVIEW CRITERIA:\n"
132
+ "- Security vulnerabilities\n"
133
+ "- Performance issues\n"
134
+ "- Code maintainability\n"
135
+ "- Best practices adherence\n\n"
136
+ "## CONSTRAINTS:\n"
137
+ "{{ constraints }}\n\n"
138
+ "## OUTPUT FORMAT:\n"
139
+ "{{ output_format }}",
140
+ "principles": ["Security First", "Performance", "Maintainability"]},
141
+
142
+ {"name": "api_design",
143
+ "description": "RESTful API design with OpenAPI specification focus.",
144
+ "template": "# API DESIGN REQUEST\n\n"
145
+ "## API REQUIREMENT:\n"
146
+ "{{ directive }}\n\n"
147
+ "## ARCHITECTURE:\n"
148
+ "{{ STATE.ARCHITECTURE }}\n\n"
149
+ "## ENDPOINTS NEEDED:\n"
150
+ "{{ scope }}\n\n"
151
+ "## CONSTRAINTS:\n"
152
+ "{{ constraints }}\n\n"
153
+ "## SUCCESS CRITERIA:\n"
154
+ "{{ success_criteria }}\n\n"
155
+ "## OUTPUT FORMAT:\n"
156
+ "{{ output_format }}",
157
+ "principles": ["RESTful Design", "OpenAPI Standard", "Scalability"]}
102
158
  ]
103
159
 
104
160
  for p_data in default_patterns_data:
@@ -124,7 +180,7 @@ class PatternRegistry:
124
180
  except Exception as e:
125
181
  console.print(f"[bold red]Error:[/bold red] loading user patterns from {patterns_file}: {e}", style="red")
126
182
 
127
- def get_pattern(self, name: str) -> OptimizationPattern | None:
183
+ def get_pattern(self, name: str):
128
184
  """Retrieves a pattern by name."""
129
185
  return self.patterns.get(name)
130
186
 
core/setup.py ADDED
@@ -0,0 +1,77 @@
1
+ from rich.console import Console
2
+ from rich.panel import Panel
3
+
4
+ from .validation import StateSchema
5
+ from .branding import show_input_frame
6
+ from pydantic import ValidationError
7
+
8
+ console = Console()
9
+
10
+ class StateSetup:
11
+ def __init__(self, cli_state):
12
+ self.cli_state = cli_state
13
+ self.required_fields = ["ARCHITECTURE", "FOCUS", "PATTERNS"]
14
+
15
+ def is_setup_complete(self) -> bool:
16
+ """Check if all required state fields are configured"""
17
+ for field in self.required_fields:
18
+ if not self.cli_state.get(field):
19
+ return False
20
+ return True
21
+
22
+ def run_interactive_setup(self):
23
+ """Run interactive state setup"""
24
+ console.print(Panel(
25
+ "[bold yellow]Initial Setup Required[/bold yellow]\n\n"
26
+ "Before using CLIOPS, please configure your project settings:",
27
+ border_style="yellow"
28
+ ))
29
+
30
+ setup_data = {}
31
+
32
+ # Architecture setup
33
+ architecture = show_input_frame("Enter your project architecture (e.g., 'React + Node.js', 'Django + PostgreSQL'):")
34
+ setup_data["ARCHITECTURE"] = architecture
35
+
36
+ # Focus setup
37
+ focus = show_input_frame("Enter your current project focus (e.g., 'API development', 'UI components'):")
38
+ setup_data["FOCUS"] = focus
39
+
40
+ # Patterns setup
41
+ console.print(Panel(
42
+ "Available patterns: context_aware_generation, bug_fix_precision, code_review, api_design",
43
+ border_style="dim"
44
+ ))
45
+ patterns = show_input_frame("Enter preferred patterns (comma-separated):")
46
+ setup_data["PATTERNS"] = patterns
47
+
48
+ # Default pattern
49
+ default_pattern = show_input_frame("Enter default pattern (optional, press Enter for 'context_aware_generation'):")
50
+ if default_pattern:
51
+ setup_data["DEFAULT_PATTERN"] = default_pattern
52
+ else:
53
+ setup_data["DEFAULT_PATTERN"] = "context_aware_generation"
54
+
55
+ # Validate and save
56
+ try:
57
+ validated_data = StateSchema(**setup_data)
58
+ for key, value in validated_data.model_dump().items():
59
+ if value: # Only set non-None values
60
+ self.cli_state.set(key, value)
61
+
62
+ console.print(Panel(
63
+ "[bold green]Setup Complete![/bold green]\n\n"
64
+ "You can now use CLIOPS to optimize and analyze prompts.",
65
+ border_style="green"
66
+ ))
67
+
68
+ except ValidationError as e:
69
+ console.print(f"[bold red]Setup Error:[/bold red] {e}", style="red")
70
+ raise ValueError("Setup validation failed")
71
+
72
+ def check_and_setup(self):
73
+ """Check setup status and run setup if needed"""
74
+ if not self.is_setup_complete():
75
+ self.run_interactive_setup()
76
+ return True
77
+ return False
core/state.py CHANGED
@@ -41,7 +41,7 @@ class CLIState:
41
41
  if not str(self.file_path).startswith('/tmp') and 'tmp' not in str(self.file_path):
42
42
  console.print(f"State '[bold green]{key.upper()}[/bold green]' set to '[cyan]{value}[/cyan]'.")
43
43
 
44
- def get(self, key: str) -> str | None:
44
+ def get(self, key: str):
45
45
  """Gets a value from the state."""
46
46
  return self.state.get(key.upper())
47
47
 
core/validation.py ADDED
@@ -0,0 +1,49 @@
1
+ from pydantic import BaseModel, Field, field_validator
2
+ from typing import Optional, Dict, Any, Union
3
+ import re
4
+
5
+ class StateSchema(BaseModel):
6
+ ARCHITECTURE: str = Field(..., min_length=1, description="Project architecture")
7
+ FOCUS: str = Field(..., min_length=1, description="Current project focus")
8
+ PATTERNS: str = Field(..., min_length=1, description="Preferred patterns")
9
+ DEFAULT_PATTERN: Optional[str] = Field(None, description="Default optimization pattern")
10
+
11
+ @field_validator('ARCHITECTURE')
12
+ @classmethod
13
+ def validate_architecture(cls, v):
14
+ if len(v.strip()) < 3:
15
+ raise ValueError('Architecture must be at least 3 characters')
16
+ return v.strip()
17
+
18
+ @field_validator('PATTERNS')
19
+ @classmethod
20
+ def validate_patterns(cls, v):
21
+ # Basic pattern name validation
22
+ pattern_names = [p.strip() for p in v.split(',')]
23
+ for name in pattern_names:
24
+ if not re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', name):
25
+ raise ValueError(f'Invalid pattern name: {name}')
26
+ return v
27
+
28
+ class PromptValidator:
29
+ @staticmethod
30
+ def sanitize_input(text: str) -> str:
31
+ """Sanitize user input"""
32
+ if not text or not isinstance(text, str):
33
+ raise ValueError("Input must be a non-empty string")
34
+
35
+ # Remove potentially dangerous characters
36
+ sanitized = re.sub(r'<[^>]*>', '', text.strip()) # Remove HTML tags
37
+ sanitized = re.sub(r'[{}]', '', sanitized) # Remove braces
38
+
39
+ if len(sanitized) < 3:
40
+ raise ValueError("Input too short (minimum 3 characters)")
41
+
42
+ return sanitized
43
+
44
+ @staticmethod
45
+ def validate_pattern_name(name: str) -> str:
46
+ """Validate pattern name"""
47
+ if not re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', name):
48
+ raise ValueError(f"Invalid pattern name: {name}")
49
+ return name
main.py CHANGED
@@ -11,6 +11,9 @@ from core.state import CLIState
11
11
  from core.patterns import PatternRegistry
12
12
  from core.analyzer import PromptAnalyzer
13
13
  from core.optimizer import PromptOptimizer
14
+ from core.branding import show_banner, show_input_frame
15
+ from core.setup import StateSetup
16
+ from core.validation import PromptValidator
14
17
  from presets import suggest_preset_from_prompt, apply_preset_interactive
15
18
 
16
19
  console = Console()
@@ -70,6 +73,9 @@ def _run_init_command(cli_state: CLIState, pattern_registry: PatternRegistry):
70
73
  console.print(Panel("[bold green]Initialization complete![/bold green]", expand=False, border_style="green"))
71
74
 
72
75
  def main():
76
+ # Show banner on startup
77
+ show_banner()
78
+
73
79
  # Create a dummy parser to peek at the arguments
74
80
  temp_parser = argparse.ArgumentParser(add_help=False)
75
81
  temp_parser.add_argument("first_arg", nargs='?', help=argparse.SUPPRESS)
@@ -106,6 +112,15 @@ def main():
106
112
 
107
113
  cli_state = CLIState(Config.get_state_file_path())
108
114
  pattern_registry = PatternRegistry(cli_state)
115
+
116
+ # Check and run setup if needed (except for init command or tests)
117
+ if args.command != "init" and not os.environ.get('CLIOPS_SKIP_SETUP'):
118
+ state_setup = StateSetup(cli_state)
119
+ try:
120
+ state_setup.check_and_setup()
121
+ except ValueError as e:
122
+ console.print(f"[bold red]Setup Error:[/bold red] {e}", style="red")
123
+ sys.exit(1)
109
124
 
110
125
  # Handle cases where prompt might be piped via stdin
111
126
  if hasattr(args, 'prompt') and args.prompt is None and not sys.stdin.isatty():
@@ -1,14 +0,0 @@
1
- main.py,sha256=YwzFPWB6QDtjK7r9gKA9tixEgD_7_b7gXoj6Q2ETCqk,9255
2
- presets.py,sha256=DKIfhwxtBZEC5fYHgXqhuBKou7KYQks_2M8cR3IeWao,3172
3
- cliops-3.2.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=MK-C0wpvdUfTu0a1OiKFTPqc9WptoMNYZc7pziwm3gQ,6435
8
- core/patterns.py,sha256=BWiwb5fjQbWHLsJ68p0QbtSddFqzhYx3AeLkSY-WKbU,6464
9
- core/state.py,sha256=BeosHqAwT3tcicaQs-gJabz2jrtwQp8t7J0ziGXv_QI,2668
10
- cliops-3.2.2.dist-info/METADATA,sha256=JnwFSYAm-g_CIVQsjM-ta5CQL1LukpS8kT04XLQrBZA,4076
11
- cliops-3.2.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
12
- cliops-3.2.2.dist-info/entry_points.txt,sha256=F8ZncVlnk83ELj0TGXUYS-qYvmaP-owQyU20I7jRefg,37
13
- cliops-3.2.2.dist-info/top_level.txt,sha256=dV-NRp_bb_IkCFfEXDbA1mHA5SshVNbUsxaF809itG8,18
14
- cliops-3.2.2.dist-info/RECORD,,
File without changes