cliops 1.0.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.
- cliops-1.0.0/LICENSE +21 -0
- cliops-1.0.0/MANIFEST.in +7 -0
- cliops-1.0.0/PKG-INFO +126 -0
- cliops-1.0.0/README.md +73 -0
- cliops-1.0.0/cliops.egg-info/PKG-INFO +126 -0
- cliops-1.0.0/cliops.egg-info/SOURCES.txt +23 -0
- cliops-1.0.0/cliops.egg-info/dependency_links.txt +1 -0
- cliops-1.0.0/cliops.egg-info/entry_points.txt +2 -0
- cliops-1.0.0/cliops.egg-info/not-zip-safe +1 -0
- cliops-1.0.0/cliops.egg-info/requires.txt +11 -0
- cliops-1.0.0/cliops.egg-info/top_level.txt +1 -0
- cliops-1.0.0/core/__init__.py +1 -0
- cliops-1.0.0/core/analyzer.py +78 -0
- cliops-1.0.0/core/config.py +35 -0
- cliops-1.0.0/core/optimizer.py +143 -0
- cliops-1.0.0/core/patterns.py +142 -0
- cliops-1.0.0/core/state.py +60 -0
- cliops-1.0.0/requirements.txt +1 -0
- cliops-1.0.0/setup.cfg +4 -0
- cliops-1.0.0/setup.py +66 -0
- cliops-1.0.0/tests/__init__.py +1 -0
- cliops-1.0.0/tests/test_integration.py +51 -0
- cliops-1.0.0/tests/test_optimizer.py +49 -0
- cliops-1.0.0/tests/test_patterns.py +51 -0
- cliops-1.0.0/tests/test_state.py +39 -0
cliops-1.0.0/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2024 CliOps Development Team
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
cliops-1.0.0/MANIFEST.in
ADDED
cliops-1.0.0/PKG-INFO
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: cliops
|
3
|
+
Version: 1.0.0
|
4
|
+
Summary: Advanced CLI tool for structured, pattern-based prompt optimization and state management
|
5
|
+
Home-page: https://github.com/cliops/cliops
|
6
|
+
Author: CliOps Development Team
|
7
|
+
Author-email: contact@cliops.dev
|
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
|
11
|
+
Keywords: cli prompt optimization ai llm prompt-engineering patterns state-management
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
13
|
+
Classifier: Intended Audience :: Developers
|
14
|
+
Classifier: Intended Audience :: System Administrators
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
16
|
+
Classifier: Operating System :: OS Independent
|
17
|
+
Classifier: Programming Language :: Python :: 3.8
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
23
|
+
Classifier: Topic :: Software Development :: Build Tools
|
24
|
+
Classifier: Topic :: System :: Systems Administration
|
25
|
+
Classifier: Topic :: Utilities
|
26
|
+
Classifier: Topic :: Text Processing :: Linguistic
|
27
|
+
Classifier: Environment :: Console
|
28
|
+
Requires-Python: >=3.8
|
29
|
+
Description-Content-Type: text/markdown
|
30
|
+
License-File: LICENSE
|
31
|
+
Requires-Dist: rich>=13.0.0
|
32
|
+
Provides-Extra: dev
|
33
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
34
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
35
|
+
Requires-Dist: black>=22.0.0; extra == "dev"
|
36
|
+
Requires-Dist: flake8>=5.0.0; extra == "dev"
|
37
|
+
Provides-Extra: test
|
38
|
+
Requires-Dist: pytest>=7.0.0; extra == "test"
|
39
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "test"
|
40
|
+
Dynamic: author
|
41
|
+
Dynamic: author-email
|
42
|
+
Dynamic: classifier
|
43
|
+
Dynamic: description
|
44
|
+
Dynamic: description-content-type
|
45
|
+
Dynamic: home-page
|
46
|
+
Dynamic: keywords
|
47
|
+
Dynamic: license-file
|
48
|
+
Dynamic: project-url
|
49
|
+
Dynamic: provides-extra
|
50
|
+
Dynamic: requires-dist
|
51
|
+
Dynamic: requires-python
|
52
|
+
Dynamic: summary
|
53
|
+
|
54
|
+
# CliOps - Command Line Interface for Prompt Optimization
|
55
|
+
|
56
|
+
A powerful CLI tool for structured, pattern-based prompt optimization and state management.
|
57
|
+
|
58
|
+
## Features
|
59
|
+
|
60
|
+
- **Pattern-based optimization**: Apply proven prompt engineering patterns
|
61
|
+
- **State management**: Persistent CLI state for project context
|
62
|
+
- **Rich terminal UI**: Beautiful output with syntax highlighting
|
63
|
+
- **Extensible**: Add custom patterns and presets
|
64
|
+
- **Cross-platform**: Works on Windows, macOS, and Linux
|
65
|
+
|
66
|
+
## Installation
|
67
|
+
|
68
|
+
```bash
|
69
|
+
pip install -e .
|
70
|
+
```
|
71
|
+
|
72
|
+
## Quick Start
|
73
|
+
|
74
|
+
```bash
|
75
|
+
# Initialize configuration
|
76
|
+
cliops init
|
77
|
+
|
78
|
+
# Set project context
|
79
|
+
cliops state set ARCHITECTURE "React + Node.js"
|
80
|
+
cliops state set FOCUS "API development"
|
81
|
+
|
82
|
+
# Optimize a prompt
|
83
|
+
cliops "Create a user authentication endpoint"
|
84
|
+
|
85
|
+
# Analyze a prompt
|
86
|
+
cliops analyze "Make this code better"
|
87
|
+
|
88
|
+
# List available patterns
|
89
|
+
cliops patterns
|
90
|
+
```
|
91
|
+
|
92
|
+
## Usage
|
93
|
+
|
94
|
+
### Basic Commands
|
95
|
+
|
96
|
+
- `cliops optimize <prompt>` - Optimize a prompt using patterns
|
97
|
+
- `cliops analyze <prompt>` - Analyze prompt for improvements
|
98
|
+
- `cliops patterns` - List available optimization patterns
|
99
|
+
- `cliops state set <key> <value>` - Set persistent state
|
100
|
+
- `cliops init` - Initialize configuration
|
101
|
+
|
102
|
+
### Examples
|
103
|
+
|
104
|
+
```bash
|
105
|
+
# Direct prompt optimization
|
106
|
+
cliops "Fix this bug in my React component"
|
107
|
+
|
108
|
+
# With specific pattern
|
109
|
+
cliops optimize "Refactor this function" --pattern surgical_refactor
|
110
|
+
|
111
|
+
# With overrides
|
112
|
+
cliops optimize "Create API" --context "Express.js backend" --constraints "RESTful design"
|
113
|
+
|
114
|
+
# Dry run to see what would be generated
|
115
|
+
cliops optimize "Test prompt" --dry-run
|
116
|
+
```
|
117
|
+
|
118
|
+
## Testing
|
119
|
+
|
120
|
+
```bash
|
121
|
+
python run_tests.py
|
122
|
+
```
|
123
|
+
|
124
|
+
## License
|
125
|
+
|
126
|
+
MIT License
|
cliops-1.0.0/README.md
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
# CliOps - Command Line Interface for Prompt Optimization
|
2
|
+
|
3
|
+
A powerful CLI tool for structured, pattern-based prompt optimization and state management.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
- **Pattern-based optimization**: Apply proven prompt engineering patterns
|
8
|
+
- **State management**: Persistent CLI state for project context
|
9
|
+
- **Rich terminal UI**: Beautiful output with syntax highlighting
|
10
|
+
- **Extensible**: Add custom patterns and presets
|
11
|
+
- **Cross-platform**: Works on Windows, macOS, and Linux
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
```bash
|
16
|
+
pip install -e .
|
17
|
+
```
|
18
|
+
|
19
|
+
## Quick Start
|
20
|
+
|
21
|
+
```bash
|
22
|
+
# Initialize configuration
|
23
|
+
cliops init
|
24
|
+
|
25
|
+
# Set project context
|
26
|
+
cliops state set ARCHITECTURE "React + Node.js"
|
27
|
+
cliops state set FOCUS "API development"
|
28
|
+
|
29
|
+
# Optimize a prompt
|
30
|
+
cliops "Create a user authentication endpoint"
|
31
|
+
|
32
|
+
# Analyze a prompt
|
33
|
+
cliops analyze "Make this code better"
|
34
|
+
|
35
|
+
# List available patterns
|
36
|
+
cliops patterns
|
37
|
+
```
|
38
|
+
|
39
|
+
## Usage
|
40
|
+
|
41
|
+
### Basic Commands
|
42
|
+
|
43
|
+
- `cliops optimize <prompt>` - Optimize a prompt using patterns
|
44
|
+
- `cliops analyze <prompt>` - Analyze prompt for improvements
|
45
|
+
- `cliops patterns` - List available optimization patterns
|
46
|
+
- `cliops state set <key> <value>` - Set persistent state
|
47
|
+
- `cliops init` - Initialize configuration
|
48
|
+
|
49
|
+
### Examples
|
50
|
+
|
51
|
+
```bash
|
52
|
+
# Direct prompt optimization
|
53
|
+
cliops "Fix this bug in my React component"
|
54
|
+
|
55
|
+
# With specific pattern
|
56
|
+
cliops optimize "Refactor this function" --pattern surgical_refactor
|
57
|
+
|
58
|
+
# With overrides
|
59
|
+
cliops optimize "Create API" --context "Express.js backend" --constraints "RESTful design"
|
60
|
+
|
61
|
+
# Dry run to see what would be generated
|
62
|
+
cliops optimize "Test prompt" --dry-run
|
63
|
+
```
|
64
|
+
|
65
|
+
## Testing
|
66
|
+
|
67
|
+
```bash
|
68
|
+
python run_tests.py
|
69
|
+
```
|
70
|
+
|
71
|
+
## License
|
72
|
+
|
73
|
+
MIT License
|
@@ -0,0 +1,126 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: cliops
|
3
|
+
Version: 1.0.0
|
4
|
+
Summary: Advanced CLI tool for structured, pattern-based prompt optimization and state management
|
5
|
+
Home-page: https://github.com/cliops/cliops
|
6
|
+
Author: CliOps Development Team
|
7
|
+
Author-email: contact@cliops.dev
|
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
|
11
|
+
Keywords: cli prompt optimization ai llm prompt-engineering patterns state-management
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
13
|
+
Classifier: Intended Audience :: Developers
|
14
|
+
Classifier: Intended Audience :: System Administrators
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
16
|
+
Classifier: Operating System :: OS Independent
|
17
|
+
Classifier: Programming Language :: Python :: 3.8
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
23
|
+
Classifier: Topic :: Software Development :: Build Tools
|
24
|
+
Classifier: Topic :: System :: Systems Administration
|
25
|
+
Classifier: Topic :: Utilities
|
26
|
+
Classifier: Topic :: Text Processing :: Linguistic
|
27
|
+
Classifier: Environment :: Console
|
28
|
+
Requires-Python: >=3.8
|
29
|
+
Description-Content-Type: text/markdown
|
30
|
+
License-File: LICENSE
|
31
|
+
Requires-Dist: rich>=13.0.0
|
32
|
+
Provides-Extra: dev
|
33
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
34
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
35
|
+
Requires-Dist: black>=22.0.0; extra == "dev"
|
36
|
+
Requires-Dist: flake8>=5.0.0; extra == "dev"
|
37
|
+
Provides-Extra: test
|
38
|
+
Requires-Dist: pytest>=7.0.0; extra == "test"
|
39
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "test"
|
40
|
+
Dynamic: author
|
41
|
+
Dynamic: author-email
|
42
|
+
Dynamic: classifier
|
43
|
+
Dynamic: description
|
44
|
+
Dynamic: description-content-type
|
45
|
+
Dynamic: home-page
|
46
|
+
Dynamic: keywords
|
47
|
+
Dynamic: license-file
|
48
|
+
Dynamic: project-url
|
49
|
+
Dynamic: provides-extra
|
50
|
+
Dynamic: requires-dist
|
51
|
+
Dynamic: requires-python
|
52
|
+
Dynamic: summary
|
53
|
+
|
54
|
+
# CliOps - Command Line Interface for Prompt Optimization
|
55
|
+
|
56
|
+
A powerful CLI tool for structured, pattern-based prompt optimization and state management.
|
57
|
+
|
58
|
+
## Features
|
59
|
+
|
60
|
+
- **Pattern-based optimization**: Apply proven prompt engineering patterns
|
61
|
+
- **State management**: Persistent CLI state for project context
|
62
|
+
- **Rich terminal UI**: Beautiful output with syntax highlighting
|
63
|
+
- **Extensible**: Add custom patterns and presets
|
64
|
+
- **Cross-platform**: Works on Windows, macOS, and Linux
|
65
|
+
|
66
|
+
## Installation
|
67
|
+
|
68
|
+
```bash
|
69
|
+
pip install -e .
|
70
|
+
```
|
71
|
+
|
72
|
+
## Quick Start
|
73
|
+
|
74
|
+
```bash
|
75
|
+
# Initialize configuration
|
76
|
+
cliops init
|
77
|
+
|
78
|
+
# Set project context
|
79
|
+
cliops state set ARCHITECTURE "React + Node.js"
|
80
|
+
cliops state set FOCUS "API development"
|
81
|
+
|
82
|
+
# Optimize a prompt
|
83
|
+
cliops "Create a user authentication endpoint"
|
84
|
+
|
85
|
+
# Analyze a prompt
|
86
|
+
cliops analyze "Make this code better"
|
87
|
+
|
88
|
+
# List available patterns
|
89
|
+
cliops patterns
|
90
|
+
```
|
91
|
+
|
92
|
+
## Usage
|
93
|
+
|
94
|
+
### Basic Commands
|
95
|
+
|
96
|
+
- `cliops optimize <prompt>` - Optimize a prompt using patterns
|
97
|
+
- `cliops analyze <prompt>` - Analyze prompt for improvements
|
98
|
+
- `cliops patterns` - List available optimization patterns
|
99
|
+
- `cliops state set <key> <value>` - Set persistent state
|
100
|
+
- `cliops init` - Initialize configuration
|
101
|
+
|
102
|
+
### Examples
|
103
|
+
|
104
|
+
```bash
|
105
|
+
# Direct prompt optimization
|
106
|
+
cliops "Fix this bug in my React component"
|
107
|
+
|
108
|
+
# With specific pattern
|
109
|
+
cliops optimize "Refactor this function" --pattern surgical_refactor
|
110
|
+
|
111
|
+
# With overrides
|
112
|
+
cliops optimize "Create API" --context "Express.js backend" --constraints "RESTful design"
|
113
|
+
|
114
|
+
# Dry run to see what would be generated
|
115
|
+
cliops optimize "Test prompt" --dry-run
|
116
|
+
```
|
117
|
+
|
118
|
+
## Testing
|
119
|
+
|
120
|
+
```bash
|
121
|
+
python run_tests.py
|
122
|
+
```
|
123
|
+
|
124
|
+
## License
|
125
|
+
|
126
|
+
MIT License
|
@@ -0,0 +1,23 @@
|
|
1
|
+
LICENSE
|
2
|
+
MANIFEST.in
|
3
|
+
README.md
|
4
|
+
requirements.txt
|
5
|
+
setup.py
|
6
|
+
cliops.egg-info/PKG-INFO
|
7
|
+
cliops.egg-info/SOURCES.txt
|
8
|
+
cliops.egg-info/dependency_links.txt
|
9
|
+
cliops.egg-info/entry_points.txt
|
10
|
+
cliops.egg-info/not-zip-safe
|
11
|
+
cliops.egg-info/requires.txt
|
12
|
+
cliops.egg-info/top_level.txt
|
13
|
+
core/__init__.py
|
14
|
+
core/analyzer.py
|
15
|
+
core/config.py
|
16
|
+
core/optimizer.py
|
17
|
+
core/patterns.py
|
18
|
+
core/state.py
|
19
|
+
tests/__init__.py
|
20
|
+
tests/test_integration.py
|
21
|
+
tests/test_optimizer.py
|
22
|
+
tests/test_patterns.py
|
23
|
+
tests/test_state.py
|
@@ -0,0 +1 @@
|
|
1
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
core
|
@@ -0,0 +1 @@
|
|
1
|
+
# Core module for cliops
|
@@ -0,0 +1,78 @@
|
|
1
|
+
import re
|
2
|
+
import os
|
3
|
+
from rich.console import Console
|
4
|
+
from rich.panel import Panel
|
5
|
+
|
6
|
+
console = Console()
|
7
|
+
|
8
|
+
class PromptAnalyzer:
|
9
|
+
"""Analyzes a raw prompt for common issues based on optimization principles."""
|
10
|
+
def __init__(self, pattern_registry):
|
11
|
+
self.pattern_registry = pattern_registry
|
12
|
+
self.principles = {
|
13
|
+
"Structure and Clarity": {
|
14
|
+
"keywords": ["directive", "scope", "constraints", "output format", "success criteria"],
|
15
|
+
"suggestion": "Ensure your prompt has a clear structure with explicit sections like 'DIRECTIVE', 'SCOPE', 'CONSTRAINTS', 'OUTPUT FORMAT', and 'SUCCESS CRITERIA'."
|
16
|
+
},
|
17
|
+
"Specificity and Detail": {
|
18
|
+
"keywords": ["specific", "detailed", "precise", "exact", "example"],
|
19
|
+
"suggestion": "Add more specific details about the task, desired output, edge cases, and expected behavior. Consider using few-shot examples."
|
20
|
+
},
|
21
|
+
"Context-Aware Generation": {
|
22
|
+
"keywords": ["context", "background", "system", "environment", "state"],
|
23
|
+
"suggestion": "Provide relevant context, system state, or background information the LLM needs to understand the scenario fully. Leverage `cliops state`."
|
24
|
+
},
|
25
|
+
"Output Format": {
|
26
|
+
"keywords": ["json", "xml", "yaml", "markdown", "code block", "format", "structure"],
|
27
|
+
"suggestion": "Clearly define the desired output format (e.g., JSON, YAML, Markdown code block, specific class structure)."
|
28
|
+
},
|
29
|
+
"Error Prevention": {
|
30
|
+
"keywords": ["handle errors", "robust", "mitigate", "assumptions", "potential issues", "safeguards"],
|
31
|
+
"suggestion": "Instruct the LLM on how to handle potential errors, edge cases, or invalid inputs. Define explicit assumptions."
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
def analyze_prompt(self, prompt_text: str):
|
36
|
+
"""Analyzes a raw prompt for adherence to prompt engineering principles."""
|
37
|
+
console.print(Panel("[bold green]Prompt Analysis[/bold green]", expand=False, border_style="green"))
|
38
|
+
issues = []
|
39
|
+
recommendations = []
|
40
|
+
|
41
|
+
# Check for general structure and key fields
|
42
|
+
if not re.search(r"DIRECTIVE:", prompt_text, re.IGNORECASE):
|
43
|
+
issues.append("Missing or unclear 'DIRECTIVE'.")
|
44
|
+
recommendations.append(self.principles["Structure and Clarity"]["suggestion"])
|
45
|
+
if not re.search(r"CONSTRAINTS:", prompt_text, re.IGNORECASE):
|
46
|
+
issues.append("Missing or unclear 'CONSTRAINTS'.")
|
47
|
+
recommendations.append(self.principles["Structure and Clarity"]["suggestion"])
|
48
|
+
if not re.search(r"OUTPUT FORMAT:", prompt_text, re.IGNORECASE):
|
49
|
+
issues.append("Missing or unclear 'OUTPUT FORMAT'.")
|
50
|
+
recommendations.append(self.principles["Output Format"]["suggestion"])
|
51
|
+
|
52
|
+
# Check for keywords related to other principles
|
53
|
+
for principle, data in self.principles.items():
|
54
|
+
if principle in ["Structure and Clarity", "Output Format"]:
|
55
|
+
continue
|
56
|
+
|
57
|
+
found_keyword = False
|
58
|
+
for keyword in data["keywords"]:
|
59
|
+
if re.search(r"\b" + re.escape(keyword) + r"\b", prompt_text, re.IGNORECASE):
|
60
|
+
found_keyword = True
|
61
|
+
break
|
62
|
+
if not found_keyword:
|
63
|
+
recommendations.append(f"Consider: {data['suggestion']}")
|
64
|
+
|
65
|
+
if issues:
|
66
|
+
console.print(Panel("[bold red]Identified Issues[/bold red]", expand=False, border_style="red"))
|
67
|
+
for issue in sorted(list(set(issues))):
|
68
|
+
console.print(f"[red]- {issue}[/red]")
|
69
|
+
else:
|
70
|
+
console.print("[bold green]No critical issues identified in prompt structure.[/bold green]", style="green")
|
71
|
+
|
72
|
+
if recommendations:
|
73
|
+
console.print(Panel("[bold yellow]Recommendations for Improvement[/bold yellow]", expand=False, border_style="yellow"))
|
74
|
+
for rec in sorted(list(set(recommendations))):
|
75
|
+
console.print(f"[yellow]- {rec}[/yellow]")
|
76
|
+
else:
|
77
|
+
console.print("[bold green]Prompt appears well-structured and comprehensive.[/bold green]", style="green")
|
78
|
+
console.print(Panel.fit("[dim]Analysis Complete[/dim]", border_style="dim"))
|
@@ -0,0 +1,35 @@
|
|
1
|
+
import os
|
2
|
+
from pathlib import Path
|
3
|
+
|
4
|
+
class Config:
|
5
|
+
"""Manages application configuration, including state file path and pattern definitions."""
|
6
|
+
APP_NAME = "cliops"
|
7
|
+
STATE_FILE_NAME = "cliops_state.json"
|
8
|
+
PATTERNS_FILE_NAME = "patterns.json"
|
9
|
+
|
10
|
+
@staticmethod
|
11
|
+
def get_app_data_dir():
|
12
|
+
"""Returns the appropriate application data directory for the OS."""
|
13
|
+
if os.name == 'nt': # Windows
|
14
|
+
return Path(os.environ.get('APPDATA', Path.home() / Config.APP_NAME)) / Config.APP_NAME
|
15
|
+
elif os.name == 'posix': # Linux, macOS, etc.
|
16
|
+
xdg_data_home = os.environ.get('XDG_DATA_HOME')
|
17
|
+
if xdg_data_home:
|
18
|
+
return Path(xdg_data_home) / Config.APP_NAME
|
19
|
+
return Path.home() / ".local" / "share" / Config.APP_NAME
|
20
|
+
else:
|
21
|
+
return Path.home() / f".{Config.APP_NAME}"
|
22
|
+
|
23
|
+
@staticmethod
|
24
|
+
def get_state_file_path():
|
25
|
+
"""Returns the full path to the CLI state file."""
|
26
|
+
data_dir = Config.get_app_data_dir()
|
27
|
+
data_dir.mkdir(parents=True, exist_ok=True)
|
28
|
+
return data_dir / Config.STATE_FILE_NAME
|
29
|
+
|
30
|
+
@staticmethod
|
31
|
+
def get_patterns_file_path():
|
32
|
+
"""Returns the full path to the user-defined patterns file."""
|
33
|
+
data_dir = Config.get_app_data_dir()
|
34
|
+
data_dir.mkdir(parents=True, exist_ok=True)
|
35
|
+
return data_dir / Config.PATTERNS_FILE_NAME
|
@@ -0,0 +1,143 @@
|
|
1
|
+
import re
|
2
|
+
from rich.console import Console
|
3
|
+
from rich.panel import Panel
|
4
|
+
from rich.table import Table
|
5
|
+
from rich.syntax import Syntax
|
6
|
+
from rich.text import Text
|
7
|
+
from rich import box
|
8
|
+
|
9
|
+
console = Console()
|
10
|
+
|
11
|
+
class PromptOptimizer:
|
12
|
+
"""Optimizes raw prompts using predefined patterns."""
|
13
|
+
def __init__(self, pattern_registry, cli_state, verbose: bool = False):
|
14
|
+
self.pattern_registry = pattern_registry
|
15
|
+
self.cli_state = cli_state
|
16
|
+
self.verbose = verbose
|
17
|
+
|
18
|
+
def _parse_prompt_into_sections(self, raw_prompt: str) -> dict:
|
19
|
+
"""Parses a raw prompt into a dictionary of sections based on '## SECTION:' headers and <TAG>...</TAG> blocks."""
|
20
|
+
sections = {}
|
21
|
+
main_body_content = raw_prompt
|
22
|
+
extracted_sections = {}
|
23
|
+
|
24
|
+
# Extract tagged blocks (e.g., <CODE>, <CONTEXT>)
|
25
|
+
tag_block_pattern = re.compile(r"<(\w+)>(.*?)</\1>", re.DOTALL | re.IGNORECASE)
|
26
|
+
|
27
|
+
matches_to_remove = []
|
28
|
+
for match in tag_block_pattern.finditer(main_body_content):
|
29
|
+
tag_name = match.group(1).upper()
|
30
|
+
content = match.group(2).strip()
|
31
|
+
extracted_sections[tag_name] = content
|
32
|
+
matches_to_remove.append(match.span())
|
33
|
+
|
34
|
+
# Remove extracted tag blocks from the main body content
|
35
|
+
for start, end in sorted(matches_to_remove, key=lambda x: x[0], reverse=True):
|
36
|
+
main_body_content = main_body_content[:start] + main_body_content[end:]
|
37
|
+
|
38
|
+
# Extract ## sections from the remaining text
|
39
|
+
parts = re.split(r'(?m)^(##\s*[\w\s\/]+?:)', main_body_content)
|
40
|
+
|
41
|
+
current_key = "MAIN_BODY"
|
42
|
+
if parts and parts[0].strip():
|
43
|
+
extracted_sections[current_key] = parts[0].strip()
|
44
|
+
|
45
|
+
for i in range(1, len(parts), 2):
|
46
|
+
header = parts[i].strip()
|
47
|
+
key = header.replace('##', '').replace(':', '').strip().replace(' ', '_').upper()
|
48
|
+
content = parts[i+1].strip() if i+1 < len(parts) else ""
|
49
|
+
extracted_sections[key] = content
|
50
|
+
|
51
|
+
# Handle 'code_here' specifically if it's the main body or implied
|
52
|
+
if "CODE" in extracted_sections:
|
53
|
+
extracted_sections["CODE_HERE"] = extracted_sections.pop("CODE")
|
54
|
+
|
55
|
+
return extracted_sections
|
56
|
+
|
57
|
+
def _extract_components(self, raw_prompt: str, pattern) -> dict:
|
58
|
+
"""Extracts common and pattern-specific components from a raw prompt."""
|
59
|
+
parsed_sections = self._parse_prompt_into_sections(raw_prompt)
|
60
|
+
extracted_fields = {}
|
61
|
+
|
62
|
+
# Map common section names to expected template field names
|
63
|
+
common_mappings = {
|
64
|
+
'DIRECTIVE': 'directive',
|
65
|
+
'SCOPE': 'scope',
|
66
|
+
'CONSTRAINTS': 'constraints',
|
67
|
+
'OUTPUT_FORMAT': 'output_format',
|
68
|
+
'SUCCESS_CRITERIA': 'success_criteria',
|
69
|
+
'CODE_HERE': 'code_here',
|
70
|
+
'CODE': 'code_here',
|
71
|
+
'CONTEXT': 'context',
|
72
|
+
'CURRENT_FOCUS': 'current_focus',
|
73
|
+
'MINDSET': 'mindset',
|
74
|
+
'MAIN_BODY': 'code_here'
|
75
|
+
}
|
76
|
+
|
77
|
+
for section_key, template_field in common_mappings.items():
|
78
|
+
if section_key in parsed_sections and parsed_sections[section_key] != "":
|
79
|
+
extracted_fields[template_field] = parsed_sections[section_key]
|
80
|
+
|
81
|
+
# Apply pattern-specific extraction logic
|
82
|
+
if pattern.specific_extract_func:
|
83
|
+
specific_extracted = pattern.specific_extract_func(raw_prompt)
|
84
|
+
for k,v in specific_extracted.items():
|
85
|
+
if v is not None and v != '':
|
86
|
+
extracted_fields[k.lower()] = v
|
87
|
+
|
88
|
+
return {k: v for k, v in extracted_fields.items() if v is not None}
|
89
|
+
|
90
|
+
def optimize_prompt(self, raw_prompt: str, pattern_name: str, overrides: dict, dry_run: bool = False) -> str:
|
91
|
+
"""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.")
|
95
|
+
|
96
|
+
# Extract components from raw_prompt
|
97
|
+
extracted_fields = self._extract_components(raw_prompt, pattern)
|
98
|
+
|
99
|
+
# Prepare fields for template formatting
|
100
|
+
template_fields = {}
|
101
|
+
for key in extracted_fields:
|
102
|
+
template_fields[key] = extracted_fields[key]
|
103
|
+
|
104
|
+
# Inject CLI state
|
105
|
+
cli_state_values = {key: self.cli_state.get(key) for key in self.cli_state.state.keys()}
|
106
|
+
StateObject = type('StateObject', (object,), cli_state_values)
|
107
|
+
template_fields['STATE'] = StateObject()
|
108
|
+
|
109
|
+
# Apply explicit CLI argument overrides
|
110
|
+
for key, value in overrides.items():
|
111
|
+
if key in template_fields:
|
112
|
+
template_fields[key] = value
|
113
|
+
elif key.upper() in cli_state_values:
|
114
|
+
setattr(template_fields['STATE'], key.upper(), value)
|
115
|
+
|
116
|
+
# Set defaults if fields are still missing
|
117
|
+
default_fields = {
|
118
|
+
'directive': 'Please complete the task.',
|
119
|
+
'scope': 'The entire codebase.',
|
120
|
+
'constraints': 'No specific constraints.',
|
121
|
+
'output_format': 'Clean code/text.',
|
122
|
+
'success_criteria': 'The task is completed as per the directive.',
|
123
|
+
'code_here': 'No code provided.',
|
124
|
+
'context': 'General context.',
|
125
|
+
'current_focus': 'Overall system.',
|
126
|
+
'mindset': 'Standard development mindset.',
|
127
|
+
'examples': 'No examples provided.'
|
128
|
+
}
|
129
|
+
|
130
|
+
for field, default_value in default_fields.items():
|
131
|
+
if field not in template_fields or template_fields[field] is None or template_fields[field] == '':
|
132
|
+
template_fields[field] = default_value
|
133
|
+
|
134
|
+
if dry_run:
|
135
|
+
console.print(Panel("[bold blue]Dry Run: Prompt Optimization Details[/bold blue]", expand=False, border_style="blue"))
|
136
|
+
console.print("Dry run complete. No prompt generated.")
|
137
|
+
return "Dry run complete. No prompt generated."
|
138
|
+
|
139
|
+
try:
|
140
|
+
optimized_prompt = pattern.template.format(**template_fields)
|
141
|
+
return optimized_prompt
|
142
|
+
except KeyError as e:
|
143
|
+
raise ValueError(f"Template for pattern '{pattern_name}' missing field {e}.")
|
@@ -0,0 +1,142 @@
|
|
1
|
+
import json
|
2
|
+
import re
|
3
|
+
from pathlib import Path
|
4
|
+
from rich.console import Console
|
5
|
+
from rich.table import Table
|
6
|
+
from rich.syntax import Syntax
|
7
|
+
from rich import box
|
8
|
+
from .config import Config
|
9
|
+
|
10
|
+
console = Console()
|
11
|
+
|
12
|
+
class OptimizationPattern:
|
13
|
+
"""Represents a prompt optimization pattern with its template and extraction logic."""
|
14
|
+
def __init__(self, name: str, description: str, template: str, principles: list[str], specific_extract_func=None):
|
15
|
+
self.name = name
|
16
|
+
self.description = description
|
17
|
+
self.template = template
|
18
|
+
self.principles = principles
|
19
|
+
self.specific_extract_func = specific_extract_func
|
20
|
+
|
21
|
+
@classmethod
|
22
|
+
def from_dict(cls, data: dict):
|
23
|
+
"""Creates an OptimizationPattern instance from a dictionary."""
|
24
|
+
return cls(
|
25
|
+
name=data['name'],
|
26
|
+
description=data['description'],
|
27
|
+
template=data['template'],
|
28
|
+
principles=data.get('principles', []),
|
29
|
+
specific_extract_func=None
|
30
|
+
)
|
31
|
+
|
32
|
+
def to_dict(self):
|
33
|
+
"""Converts the pattern to a dictionary for serialization."""
|
34
|
+
return {
|
35
|
+
'name': self.name,
|
36
|
+
'description': self.description,
|
37
|
+
'template': self.template,
|
38
|
+
'principles': self.principles
|
39
|
+
}
|
40
|
+
|
41
|
+
class PatternRegistry:
|
42
|
+
"""Manages available optimization patterns."""
|
43
|
+
def __init__(self, cli_state):
|
44
|
+
self.patterns: dict[str, OptimizationPattern] = {}
|
45
|
+
self.cli_state = cli_state
|
46
|
+
self._load_default_patterns()
|
47
|
+
self._load_user_patterns()
|
48
|
+
|
49
|
+
def _load_default_patterns(self):
|
50
|
+
"""Loads hardcoded default patterns."""
|
51
|
+
# Generic extraction helpers
|
52
|
+
def extract_colon_value(text, field_name):
|
53
|
+
match = re.search(rf"^{re.escape(field_name)}:\s*(.*?)(?:\n##|\n<|\Z)", text, re.MULTILINE | re.DOTALL | re.IGNORECASE)
|
54
|
+
if match:
|
55
|
+
content = match.group(1).strip()
|
56
|
+
next_header_match = re.search(r"##\s*\w+", content, re.DOTALL)
|
57
|
+
if next_header_match:
|
58
|
+
content = content[:next_header_match.start()].strip()
|
59
|
+
next_tag_match = re.search(r"<\w+>", content, re.DOTALL)
|
60
|
+
if next_tag_match:
|
61
|
+
content = content[:next_tag_match.start()].strip()
|
62
|
+
return content
|
63
|
+
return None
|
64
|
+
|
65
|
+
def extract_between_tags(text, start_tag, end_tag):
|
66
|
+
match = re.search(rf"{re.escape(start_tag)}(.*?){re.escape(end_tag)}", text, re.DOTALL)
|
67
|
+
return match.group(1).strip() if match else None
|
68
|
+
|
69
|
+
# Pattern-specific extraction functions
|
70
|
+
def specific_extract_context_aware(prompt_text):
|
71
|
+
extracted = {}
|
72
|
+
extracted['CONTEXT'] = extract_between_tags(prompt_text, "<CONTEXT>", "</CONTEXT>")
|
73
|
+
extracted['CURRENT_FOCUS'] = extract_colon_value(prompt_text, "Current Focus")
|
74
|
+
extracted['MINDSET'] = extract_colon_value(prompt_text, "Mindset")
|
75
|
+
return {k: v for k, v in extracted.items() if v is not None}
|
76
|
+
|
77
|
+
default_patterns_data = [
|
78
|
+
{"name": "context_aware_generation",
|
79
|
+
"description": "Guides generation based on specific context, mindset, and current focus.",
|
80
|
+
"template": "# DIRECTIVE: {directive}\n\n"
|
81
|
+
"## CONTEXT:\n"
|
82
|
+
"<CONTEXT>{context}</CONTEXT>\n\n"
|
83
|
+
"## CURRENT FOCUS:\n"
|
84
|
+
"{current_focus}\n\n"
|
85
|
+
"## MINDSET:\n"
|
86
|
+
"{mindset}\n\n"
|
87
|
+
"## CONSTRAINTS:\n"
|
88
|
+
"{constraints}\n\n"
|
89
|
+
"## OUTPUT FORMAT:\n"
|
90
|
+
"{output_format}\n\n"
|
91
|
+
"## EXAMPLES:\n"
|
92
|
+
"{examples}\n\n"
|
93
|
+
"## SUCCESS CRITERIA:\n"
|
94
|
+
"{success_criteria}\n\n"
|
95
|
+
"## 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}",
|
100
|
+
"principles": ["Context-Aware Generation", "Adaptive Nuance", "State Anchoring"],
|
101
|
+
"specific_extract_func": specific_extract_context_aware}
|
102
|
+
]
|
103
|
+
|
104
|
+
for p_data in default_patterns_data:
|
105
|
+
pattern = OptimizationPattern.from_dict(p_data)
|
106
|
+
if pattern.name == "context_aware_generation":
|
107
|
+
pattern.specific_extract_func = specific_extract_context_aware
|
108
|
+
self.patterns[pattern.name] = pattern
|
109
|
+
|
110
|
+
def _load_user_patterns(self):
|
111
|
+
"""Loads user-defined patterns from patterns.json."""
|
112
|
+
patterns_file = Config.get_patterns_file_path()
|
113
|
+
if patterns_file.exists():
|
114
|
+
try:
|
115
|
+
with open(patterns_file, 'r') as f:
|
116
|
+
user_patterns_data = json.load(f)
|
117
|
+
for p_data in user_patterns_data:
|
118
|
+
pattern_name = p_data.get('name')
|
119
|
+
if pattern_name:
|
120
|
+
pattern = OptimizationPattern.from_dict(p_data)
|
121
|
+
self.patterns[pattern_name] = pattern
|
122
|
+
except json.JSONDecodeError:
|
123
|
+
console.print(f"[bold yellow]Warning:[/bold yellow] Could not decode JSON from user patterns file {patterns_file}. Ignoring user patterns.", style="yellow")
|
124
|
+
except Exception as e:
|
125
|
+
console.print(f"[bold red]Error:[/bold red] loading user patterns from {patterns_file}: {e}", style="red")
|
126
|
+
|
127
|
+
def get_pattern(self, name: str) -> OptimizationPattern | None:
|
128
|
+
"""Retrieves a pattern by name."""
|
129
|
+
return self.patterns.get(name)
|
130
|
+
|
131
|
+
def list_patterns(self):
|
132
|
+
"""Lists all available patterns."""
|
133
|
+
table = Table(title="Available Optimization Patterns", box=box.ROUNDED, style="magenta")
|
134
|
+
table.add_column("Pattern Name", style="bold cyan", justify="left")
|
135
|
+
table.add_column("Description", style="green", justify="left")
|
136
|
+
table.add_column("Principles", style="blue", justify="left")
|
137
|
+
|
138
|
+
for name, pattern in self.patterns.items():
|
139
|
+
principles_text = ", ".join(pattern.principles) if pattern.principles else "N/A"
|
140
|
+
table.add_row(name, pattern.description, principles_text)
|
141
|
+
|
142
|
+
console.print(table)
|
@@ -0,0 +1,60 @@
|
|
1
|
+
import json
|
2
|
+
from pathlib import Path
|
3
|
+
from rich.console import Console
|
4
|
+
from rich.table import Table
|
5
|
+
from rich import box
|
6
|
+
|
7
|
+
console = Console()
|
8
|
+
|
9
|
+
class CLIState:
|
10
|
+
"""Manages persistent key-value state for CLI operations."""
|
11
|
+
def __init__(self, file_path: Path):
|
12
|
+
self.file_path = file_path
|
13
|
+
self.state = self._load_state()
|
14
|
+
|
15
|
+
def _load_state(self) -> dict:
|
16
|
+
"""Loads state from the JSON file."""
|
17
|
+
if self.file_path.exists():
|
18
|
+
try:
|
19
|
+
with open(self.file_path, 'r') as f:
|
20
|
+
return json.load(f)
|
21
|
+
except json.JSONDecodeError:
|
22
|
+
console.print(f"[bold yellow]Warning:[/bold yellow] Could not decode JSON from {self.file_path}. Starting with empty state.", style="yellow")
|
23
|
+
return {}
|
24
|
+
return {}
|
25
|
+
|
26
|
+
def _save_state(self):
|
27
|
+
"""Saves the current state to the JSON file."""
|
28
|
+
with open(self.file_path, 'w') as f:
|
29
|
+
json.dump(self.state, f, indent=4)
|
30
|
+
|
31
|
+
def set(self, key: str, value: str):
|
32
|
+
"""Sets a key-value pair in the state."""
|
33
|
+
self.state[key.upper()] = value
|
34
|
+
self._save_state()
|
35
|
+
console.print(f"State '[bold green]{key.upper()}[/bold green]' set to '[cyan]{value}[/cyan]'.")
|
36
|
+
|
37
|
+
def get(self, key: str) -> str | None:
|
38
|
+
"""Gets a value from the state."""
|
39
|
+
return self.state.get(key.upper())
|
40
|
+
|
41
|
+
def show(self):
|
42
|
+
"""Displays the current state."""
|
43
|
+
if not self.state:
|
44
|
+
console.print("No CLI state currently set.", style="italic dim")
|
45
|
+
return
|
46
|
+
|
47
|
+
table = Table(title="Current CLI State", box=box.ROUNDED, style="blue")
|
48
|
+
table.add_column("Key", style="bold cyan")
|
49
|
+
table.add_column("Value", style="green")
|
50
|
+
|
51
|
+
for key, value in self.state.items():
|
52
|
+
table.add_row(key, value)
|
53
|
+
|
54
|
+
console.print(table)
|
55
|
+
|
56
|
+
def clear(self):
|
57
|
+
"""Clears all entries from the state."""
|
58
|
+
self.state = {}
|
59
|
+
self._save_state()
|
60
|
+
console.print("CLI state cleared.", style="red")
|
@@ -0,0 +1 @@
|
|
1
|
+
rich>=13.0.0
|
cliops-1.0.0/setup.cfg
ADDED
cliops-1.0.0/setup.py
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
from setuptools import setup, find_packages
|
2
|
+
from pathlib import Path
|
3
|
+
|
4
|
+
# Read README for long description
|
5
|
+
this_directory = Path(__file__).parent
|
6
|
+
long_description = (this_directory / "README.md").read_text(encoding='utf-8')
|
7
|
+
|
8
|
+
# Read requirements
|
9
|
+
requirements = (this_directory / "requirements.txt").read_text().strip().split('\n')
|
10
|
+
|
11
|
+
setup(
|
12
|
+
name='cliops',
|
13
|
+
version='1.0.0',
|
14
|
+
author='CliOps Development Team',
|
15
|
+
author_email='contact@cliops.dev',
|
16
|
+
description='Advanced CLI tool for structured, pattern-based prompt optimization and state management',
|
17
|
+
long_description=long_description,
|
18
|
+
long_description_content_type='text/markdown',
|
19
|
+
url='https://github.com/cliops/cliops',
|
20
|
+
project_urls={
|
21
|
+
'Bug Reports': 'https://github.com/cliops/cliops/issues',
|
22
|
+
'Source': 'https://github.com/cliops/cliops',
|
23
|
+
'Documentation': 'https://cliops.readthedocs.io',
|
24
|
+
},
|
25
|
+
packages=find_packages(exclude=['tests*']),
|
26
|
+
include_package_data=True,
|
27
|
+
install_requires=requirements,
|
28
|
+
extras_require={
|
29
|
+
'dev': [
|
30
|
+
'pytest>=7.0.0',
|
31
|
+
'pytest-cov>=4.0.0',
|
32
|
+
'black>=22.0.0',
|
33
|
+
'flake8>=5.0.0',
|
34
|
+
],
|
35
|
+
'test': [
|
36
|
+
'pytest>=7.0.0',
|
37
|
+
'pytest-cov>=4.0.0',
|
38
|
+
],
|
39
|
+
},
|
40
|
+
entry_points={
|
41
|
+
'console_scripts': [
|
42
|
+
'cliops=main:main',
|
43
|
+
],
|
44
|
+
},
|
45
|
+
classifiers=[
|
46
|
+
'Development Status :: 5 - Production/Stable',
|
47
|
+
'Intended Audience :: Developers',
|
48
|
+
'Intended Audience :: System Administrators',
|
49
|
+
'License :: OSI Approved :: MIT License',
|
50
|
+
'Operating System :: OS Independent',
|
51
|
+
'Programming Language :: Python :: 3.8',
|
52
|
+
'Programming Language :: Python :: 3.9',
|
53
|
+
'Programming Language :: Python :: 3.10',
|
54
|
+
'Programming Language :: Python :: 3.11',
|
55
|
+
'Programming Language :: Python :: 3.12',
|
56
|
+
'Topic :: Software Development :: Libraries :: Python Modules',
|
57
|
+
'Topic :: Software Development :: Build Tools',
|
58
|
+
'Topic :: System :: Systems Administration',
|
59
|
+
'Topic :: Utilities',
|
60
|
+
'Topic :: Text Processing :: Linguistic',
|
61
|
+
'Environment :: Console',
|
62
|
+
],
|
63
|
+
keywords='cli prompt optimization ai llm prompt-engineering patterns state-management',
|
64
|
+
python_requires='>=3.8',
|
65
|
+
zip_safe=False,
|
66
|
+
)
|
@@ -0,0 +1 @@
|
|
1
|
+
# Test module for cliops
|
@@ -0,0 +1,51 @@
|
|
1
|
+
import unittest
|
2
|
+
import subprocess
|
3
|
+
import tempfile
|
4
|
+
import os
|
5
|
+
from pathlib import Path
|
6
|
+
|
7
|
+
class TestCLIIntegration(unittest.TestCase):
|
8
|
+
def setUp(self):
|
9
|
+
self.temp_dir = tempfile.mkdtemp()
|
10
|
+
self.original_home = os.environ.get('HOME')
|
11
|
+
os.environ['HOME'] = self.temp_dir
|
12
|
+
|
13
|
+
def tearDown(self):
|
14
|
+
if self.original_home:
|
15
|
+
os.environ['HOME'] = self.original_home
|
16
|
+
else:
|
17
|
+
os.environ.pop('HOME', None)
|
18
|
+
|
19
|
+
def run_cliops(self, args):
|
20
|
+
"""Helper to run cliops command and return result"""
|
21
|
+
cmd = ['python', 'main.py'] + args
|
22
|
+
result = subprocess.run(cmd, capture_output=True, text=True, cwd=Path(__file__).parent.parent)
|
23
|
+
return result
|
24
|
+
|
25
|
+
def test_init_command(self):
|
26
|
+
result = self.run_cliops(['init'])
|
27
|
+
self.assertEqual(result.returncode, 0)
|
28
|
+
self.assertIn("Initialization complete", result.stdout)
|
29
|
+
|
30
|
+
def test_state_set_and_show(self):
|
31
|
+
# Set state
|
32
|
+
result = self.run_cliops(['state', 'set', 'TEST_KEY', 'test_value'])
|
33
|
+
self.assertEqual(result.returncode, 0)
|
34
|
+
|
35
|
+
# Show state
|
36
|
+
result = self.run_cliops(['state', 'show'])
|
37
|
+
self.assertEqual(result.returncode, 0)
|
38
|
+
self.assertIn('TEST_KEY', result.stdout)
|
39
|
+
|
40
|
+
def test_patterns_list(self):
|
41
|
+
result = self.run_cliops(['patterns'])
|
42
|
+
self.assertEqual(result.returncode, 0)
|
43
|
+
self.assertIn('context_aware_generation', result.stdout)
|
44
|
+
|
45
|
+
def test_optimize_basic(self):
|
46
|
+
result = self.run_cliops(['optimize', 'Create a function', '--dry-run'])
|
47
|
+
self.assertEqual(result.returncode, 0)
|
48
|
+
self.assertIn('Dry run complete', result.stdout)
|
49
|
+
|
50
|
+
if __name__ == '__main__':
|
51
|
+
unittest.main()
|
@@ -0,0 +1,49 @@
|
|
1
|
+
import unittest
|
2
|
+
from unittest.mock import Mock
|
3
|
+
from core.optimizer import PromptOptimizer
|
4
|
+
from core.patterns import OptimizationPattern
|
5
|
+
|
6
|
+
class TestPromptOptimizer(unittest.TestCase):
|
7
|
+
def setUp(self):
|
8
|
+
self.mock_cli_state = Mock()
|
9
|
+
self.mock_cli_state.state = {"ARCHITECTURE": "Test Architecture"}
|
10
|
+
self.mock_cli_state.get.return_value = "Test Architecture"
|
11
|
+
|
12
|
+
self.mock_pattern_registry = Mock()
|
13
|
+
self.test_pattern = OptimizationPattern(
|
14
|
+
name="test_pattern",
|
15
|
+
description="Test pattern",
|
16
|
+
template="# DIRECTIVE: {directive}\n## CONTEXT: {context}\n{code_here}",
|
17
|
+
principles=["Test"]
|
18
|
+
)
|
19
|
+
self.mock_pattern_registry.get_pattern.return_value = self.test_pattern
|
20
|
+
|
21
|
+
self.optimizer = PromptOptimizer(self.mock_pattern_registry, self.mock_cli_state)
|
22
|
+
|
23
|
+
def test_parse_prompt_into_sections(self):
|
24
|
+
prompt = "## DIRECTIVE:\nTest directive\n## CONTEXT:\nTest context\n<CODE>test code</CODE>"
|
25
|
+
sections = self.optimizer._parse_prompt_into_sections(prompt)
|
26
|
+
|
27
|
+
self.assertIn("DIRECTIVE", sections)
|
28
|
+
self.assertIn("CONTEXT", sections)
|
29
|
+
self.assertIn("CODE_HERE", sections)
|
30
|
+
self.assertEqual(sections["DIRECTIVE"], "Test directive")
|
31
|
+
self.assertEqual(sections["CONTEXT"], "Test context")
|
32
|
+
self.assertEqual(sections["CODE_HERE"], "test code")
|
33
|
+
|
34
|
+
def test_optimize_prompt_basic(self):
|
35
|
+
raw_prompt = "## DIRECTIVE:\nCreate a function\n## CONTEXT:\nPython project"
|
36
|
+
result = self.optimizer.optimize_prompt(raw_prompt, "test_pattern", {})
|
37
|
+
|
38
|
+
self.assertIn("Create a function", result)
|
39
|
+
self.assertIn("Python project", result)
|
40
|
+
|
41
|
+
def test_optimize_prompt_with_overrides(self):
|
42
|
+
raw_prompt = "## DIRECTIVE:\nCreate a function"
|
43
|
+
overrides = {"context": "Override context"}
|
44
|
+
result = self.optimizer.optimize_prompt(raw_prompt, "test_pattern", overrides)
|
45
|
+
|
46
|
+
self.assertIn("Override context", result)
|
47
|
+
|
48
|
+
if __name__ == '__main__':
|
49
|
+
unittest.main()
|
@@ -0,0 +1,51 @@
|
|
1
|
+
import unittest
|
2
|
+
from unittest.mock import Mock
|
3
|
+
from core.patterns import OptimizationPattern, PatternRegistry
|
4
|
+
|
5
|
+
class TestOptimizationPattern(unittest.TestCase):
|
6
|
+
def test_from_dict(self):
|
7
|
+
data = {
|
8
|
+
"name": "test_pattern",
|
9
|
+
"description": "Test pattern",
|
10
|
+
"template": "Test template: {field}",
|
11
|
+
"principles": ["Test Principle"]
|
12
|
+
}
|
13
|
+
pattern = OptimizationPattern.from_dict(data)
|
14
|
+
self.assertEqual(pattern.name, "test_pattern")
|
15
|
+
self.assertEqual(pattern.description, "Test pattern")
|
16
|
+
self.assertEqual(pattern.template, "Test template: {field}")
|
17
|
+
self.assertEqual(pattern.principles, ["Test Principle"])
|
18
|
+
|
19
|
+
def test_to_dict(self):
|
20
|
+
pattern = OptimizationPattern(
|
21
|
+
name="test_pattern",
|
22
|
+
description="Test pattern",
|
23
|
+
template="Test template: {field}",
|
24
|
+
principles=["Test Principle"]
|
25
|
+
)
|
26
|
+
result = pattern.to_dict()
|
27
|
+
expected = {
|
28
|
+
"name": "test_pattern",
|
29
|
+
"description": "Test pattern",
|
30
|
+
"template": "Test template: {field}",
|
31
|
+
"principles": ["Test Principle"]
|
32
|
+
}
|
33
|
+
self.assertEqual(result, expected)
|
34
|
+
|
35
|
+
class TestPatternRegistry(unittest.TestCase):
|
36
|
+
def setUp(self):
|
37
|
+
self.mock_cli_state = Mock()
|
38
|
+
self.mock_cli_state.state = {}
|
39
|
+
self.registry = PatternRegistry(self.mock_cli_state)
|
40
|
+
|
41
|
+
def test_get_pattern(self):
|
42
|
+
pattern = self.registry.get_pattern("context_aware_generation")
|
43
|
+
self.assertIsNotNone(pattern)
|
44
|
+
self.assertEqual(pattern.name, "context_aware_generation")
|
45
|
+
|
46
|
+
def test_get_nonexistent_pattern(self):
|
47
|
+
pattern = self.registry.get_pattern("nonexistent_pattern")
|
48
|
+
self.assertIsNone(pattern)
|
49
|
+
|
50
|
+
if __name__ == '__main__':
|
51
|
+
unittest.main()
|
@@ -0,0 +1,39 @@
|
|
1
|
+
import unittest
|
2
|
+
import tempfile
|
3
|
+
import json
|
4
|
+
from pathlib import Path
|
5
|
+
from core.state import CLIState
|
6
|
+
|
7
|
+
class TestCLIState(unittest.TestCase):
|
8
|
+
def setUp(self):
|
9
|
+
self.temp_file = tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.json')
|
10
|
+
self.temp_file.close()
|
11
|
+
self.state_file = Path(self.temp_file.name)
|
12
|
+
self.cli_state = CLIState(self.state_file)
|
13
|
+
|
14
|
+
def tearDown(self):
|
15
|
+
if self.state_file.exists():
|
16
|
+
self.state_file.unlink()
|
17
|
+
|
18
|
+
def test_set_and_get(self):
|
19
|
+
self.cli_state.set("TEST_KEY", "test_value")
|
20
|
+
self.assertEqual(self.cli_state.get("TEST_KEY"), "test_value")
|
21
|
+
self.assertEqual(self.cli_state.get("test_key"), "test_value") # Case insensitive
|
22
|
+
|
23
|
+
def test_persistence(self):
|
24
|
+
self.cli_state.set("PERSIST_KEY", "persist_value")
|
25
|
+
|
26
|
+
# Create new instance to test persistence
|
27
|
+
new_cli_state = CLIState(self.state_file)
|
28
|
+
self.assertEqual(new_cli_state.get("PERSIST_KEY"), "persist_value")
|
29
|
+
|
30
|
+
def test_clear(self):
|
31
|
+
self.cli_state.set("CLEAR_KEY", "clear_value")
|
32
|
+
self.cli_state.clear()
|
33
|
+
self.assertIsNone(self.cli_state.get("CLEAR_KEY"))
|
34
|
+
|
35
|
+
def test_nonexistent_key(self):
|
36
|
+
self.assertIsNone(self.cli_state.get("NONEXISTENT"))
|
37
|
+
|
38
|
+
if __name__ == '__main__':
|
39
|
+
unittest.main()
|