fancygit 1.0.0__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.
- fancygit-1.0.0.dist-info/METADATA +169 -0
- fancygit-1.0.0.dist-info/RECORD +29 -0
- fancygit-1.0.0.dist-info/WHEEL +5 -0
- fancygit-1.0.0.dist-info/entry_points.txt +2 -0
- fancygit-1.0.0.dist-info/top_level.txt +2 -0
- src/__init__.py +1 -0
- src/colors.py +260 -0
- src/git_error.py +55 -0
- src/git_error_parser.py +43 -0
- src/git_insights.py +304 -0
- src/git_runner.py +20 -0
- src/loading_animation.py +167 -0
- src/merge_conflict.py +27 -0
- src/mermaid_export.py +430 -0
- src/ollama_client.py +142 -0
- src/output_colorizer.py +358 -0
- src/repo_state.py +29 -0
- src/utils.py +0 -0
- tests/README.md +186 -0
- tests/__init__.py +0 -0
- tests/conftest.py +61 -0
- tests/test_conflict_parser_integration.py +65 -0
- tests/test_fancygit_advanced.py +504 -0
- tests/test_fancygit_commands.py +507 -0
- tests/test_fancygit_integration.py +158 -0
- tests/test_fancygit_workflows.py +441 -0
- tests/test_git_error.py +74 -0
- tests/test_git_error_parser.py +129 -0
- tests/test_git_runner.py +118 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fancygit
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A smart CLI tool that provides intelligent recommendations and helps solve merge conflicts
|
|
5
|
+
Home-page: https://github.com/Youssif-Ashmawy/Fancy_Git
|
|
6
|
+
Author: Youssif Ashmawy
|
|
7
|
+
Author-email: ashmawyyoussif@gmail.com
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
14
|
+
Requires-Python: >=3.6
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
Requires-Dist: requests>=2.25.0
|
|
17
|
+
Dynamic: author
|
|
18
|
+
Dynamic: author-email
|
|
19
|
+
Dynamic: classifier
|
|
20
|
+
Dynamic: description
|
|
21
|
+
Dynamic: description-content-type
|
|
22
|
+
Dynamic: home-page
|
|
23
|
+
Dynamic: requires-dist
|
|
24
|
+
Dynamic: requires-python
|
|
25
|
+
Dynamic: summary
|
|
26
|
+
|
|
27
|
+
# Fancy Git
|
|
28
|
+
|
|
29
|
+

|
|
30
|
+
|
|
31
|
+
A smart CLI tool that provides intelligent recommendations and helps solve merge conflicts by analyzing git command outputs.
|
|
32
|
+
|
|
33
|
+
## Email Notifications Test
|
|
34
|
+
*Testing email notification system - this change should trigger an email to the commit author.*
|
|
35
|
+
|
|
36
|
+
## Overview
|
|
37
|
+
|
|
38
|
+
FancyGit is a CLI wrapper around git commands that:
|
|
39
|
+
- Runs git commands and captures their output
|
|
40
|
+
- Detects warnings and errors in real-time
|
|
41
|
+
- Provides user confirmation for clean operations
|
|
42
|
+
- Prepares for AI-powered recommendations (future phase)
|
|
43
|
+
|
|
44
|
+
## Current Implementation (Phase 1)
|
|
45
|
+
|
|
46
|
+
### ✅ Features Implemented
|
|
47
|
+
- **CLI Interface**: `fancygit` command works system-wide
|
|
48
|
+
- **Git Commands**: Full support for `add`, `commit`, `push`, and `pull` with argument passing
|
|
49
|
+
- **Warning/Error Detection**: Regex-based pattern matching for common git issues
|
|
50
|
+
- **User Confirmation**: Prompts for confirmation when no issues detected
|
|
51
|
+
- **Cross-platform**: Works on macOS, Linux, and Windows
|
|
52
|
+
|
|
53
|
+
### 🔍 Detection Patterns
|
|
54
|
+
|
|
55
|
+
**Error Patterns:**
|
|
56
|
+
- `error:`, `fatal:`, `failed`, `rejected`
|
|
57
|
+
- `conflict`, `merge conflict`, `unable to`
|
|
58
|
+
|
|
59
|
+
**Warning Patterns:**
|
|
60
|
+
- `warning:`, `WARNING:`, `behind`, `ahead`, `diverged`
|
|
61
|
+
|
|
62
|
+
## Installation
|
|
63
|
+
|
|
64
|
+
### Quick Setup
|
|
65
|
+
|
|
66
|
+
Run the automated cross-platform launcher script:
|
|
67
|
+
```bash
|
|
68
|
+
python3 launcher.py
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
This script will:
|
|
72
|
+
- Automatically detect your operating system (Linux, macOS, or Windows)
|
|
73
|
+
- Run the appropriate installation script for your system
|
|
74
|
+
- Check Python 3.6+ and Git installation
|
|
75
|
+
- Make fancygit.py executable
|
|
76
|
+
- Create system-wide symlink (Unix-like systems) or add to PATH (Windows)
|
|
77
|
+
- Test the installation
|
|
78
|
+
|
|
79
|
+
## Usage
|
|
80
|
+
|
|
81
|
+
### Basic Commands
|
|
82
|
+
```bash
|
|
83
|
+
# Add files
|
|
84
|
+
fancygit add .
|
|
85
|
+
fancygit add filename.txt
|
|
86
|
+
|
|
87
|
+
# Commit changes
|
|
88
|
+
fancygit commit -m "Your commit message"
|
|
89
|
+
fancygit commit --amend
|
|
90
|
+
|
|
91
|
+
# Push to remote
|
|
92
|
+
fancygit push origin main
|
|
93
|
+
|
|
94
|
+
# Pull from remote
|
|
95
|
+
fancygit pull origin main
|
|
96
|
+
|
|
97
|
+
# With additional arguments
|
|
98
|
+
fancygit push origin main --force-with-lease
|
|
99
|
+
fancygit pull origin main --rebase
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### How It Works
|
|
103
|
+
|
|
104
|
+
1. **Command Execution**: Runs the actual git command
|
|
105
|
+
2. **Output Analysis**: Scans stdout/stderr for warning/error patterns
|
|
106
|
+
3. **User Feedback**:
|
|
107
|
+
- If issues detected: Shows detailed error/warning messages
|
|
108
|
+
- If no issues: Prompts for confirmation with "enter 'y' to run the command"
|
|
109
|
+
4. **Action**: Executes or cancels based on user input
|
|
110
|
+
|
|
111
|
+
### Example Output
|
|
112
|
+
|
|
113
|
+
**No Issues:**
|
|
114
|
+
```
|
|
115
|
+
Running: git push origin main
|
|
116
|
+
✅ No warnings or errors detected
|
|
117
|
+
No warning messages - enter 'y' to run the command: y
|
|
118
|
+
Command executed successfully!
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Issues Detected:**
|
|
122
|
+
```
|
|
123
|
+
Running: git pull origin main
|
|
124
|
+
❌ Errors detected:
|
|
125
|
+
error: couldn't find remote ref refs/heads/main
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Architecture
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
FancyGit CLI → Git Commands → Collect Output → Pattern Matching → User Interface (CLI)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Future Roadmap
|
|
135
|
+
|
|
136
|
+
### Phase 2: AI Pipeline
|
|
137
|
+
- Implement AI model for parsing warnings/errors
|
|
138
|
+
- Generate intelligent recommendations
|
|
139
|
+
- Add confidence scoring (backend only)
|
|
140
|
+
|
|
141
|
+
### Phase 3: Smart Resolution
|
|
142
|
+
- Automatic conflict resolution suggestions
|
|
143
|
+
- Context-aware recommendations
|
|
144
|
+
- Integration with popular Git workflows
|
|
145
|
+
|
|
146
|
+
## File Structure
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
Fancy_Git/
|
|
150
|
+
├── fancygit.py # Main CLI application
|
|
151
|
+
├── launcher.py # Cross-platform launcher script
|
|
152
|
+
├── README.md # This file
|
|
153
|
+
├── images/ # Images and documentation
|
|
154
|
+
│ └── overview.png # Project overview image
|
|
155
|
+
└── .git/ # Git repository
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
*Note: Platform-specific installation scripts (`start.sh`, `start.bat`) are included but automatically managed by `launcher.py`.*
|
|
159
|
+
THIS SHOULD BE UPDATED ASAP
|
|
160
|
+
## Requirements
|
|
161
|
+
|
|
162
|
+
- Python 3.6+
|
|
163
|
+
- Git installed and configured
|
|
164
|
+
- System permissions for symlink creation
|
|
165
|
+
- Note: No external dependecies for now so no environment setup required
|
|
166
|
+
|
|
167
|
+
## License
|
|
168
|
+
|
|
169
|
+
MIT License (For now as it is still a private project)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
src/__init__.py,sha256=9kebFj2X3VcOAcKAMsbUy_BY0odMs6JDpR6XZd-UBMo,53
|
|
2
|
+
src/colors.py,sha256=v5GogeZzJJdchim5kbenlQno350kld_4HkTojLvGbog,7810
|
|
3
|
+
src/git_error.py,sha256=tb-RbWWQZYqEcqP2PxI3snjwlfuEp96baZiONFulDm0,1852
|
|
4
|
+
src/git_error_parser.py,sha256=aWhVpb0eXx0tF8y8npWovvuKMYQTv-vgHnNh7o5rzh4,1499
|
|
5
|
+
src/git_insights.py,sha256=2R4Qb6iZ3HRdMNFUV03FWxZtbaYMCMIQ_lTXePhbC7w,13196
|
|
6
|
+
src/git_runner.py,sha256=ogQntBlV55uPH1dHlyQtEJI3OOL9WnvdzyxK-7qUaVE,544
|
|
7
|
+
src/loading_animation.py,sha256=Ei5oXCmNxLCbLPsLuST8ipxQN0vjzYBaHWfkbCASttw,5365
|
|
8
|
+
src/merge_conflict.py,sha256=OZ5_WkbEIycAtg0fazEB_LulhibfT8A4vpUwuD_NN1s,559
|
|
9
|
+
src/mermaid_export.py,sha256=HdFWpDSsiKIYehZ24mEQa62mMEiPOecD6LdDHZoc7w0,17157
|
|
10
|
+
src/ollama_client.py,sha256=2aB0dwrJNX1LagKpMJAgNyX7gL9bhdCbeDjXCklFU6I,5229
|
|
11
|
+
src/output_colorizer.py,sha256=yBDF0-ddoGH5A-44Ht9qNKFdamAfenJeTgyxMwOFuS0,14161
|
|
12
|
+
src/repo_state.py,sha256=QaDKEBYk3J6L6aZVeJdJlKz6aci3K3VK5eEHF43TuzU,665
|
|
13
|
+
src/utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
tests/README.md,sha256=x_5bZLmVpTvobP8YQc_qVDwKlRk6CjbVqD-cN0WNfpI,4377
|
|
15
|
+
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
+
tests/conftest.py,sha256=z80AuePwW-ZczGfa9YQpuAKKG2iaJ8qzx3DU-qXTVV0,2095
|
|
17
|
+
tests/test_conflict_parser_integration.py,sha256=vWCHNBcxlHDxjk5c7kk8dlktcbUcF0sGUF47Iwvru_w,2721
|
|
18
|
+
tests/test_fancygit_advanced.py,sha256=SWEZJBrEbqFg-hzvEkqV150DFIdpkSigovH9cM-D1Dc,22920
|
|
19
|
+
tests/test_fancygit_commands.py,sha256=FNNAd4kLyd_tXlNXQBG0cCt5yOH3_v3Xy8B0M4M9LKM,22414
|
|
20
|
+
tests/test_fancygit_integration.py,sha256=EQxjw-wlUoWBRhtCVvSERM-ikx-P73M7-R_bJqtP8oM,7506
|
|
21
|
+
tests/test_fancygit_workflows.py,sha256=CPGpmRIgFlwf95fAx-I_sb2ZpelOZPIfhqTTj4use8Q,21309
|
|
22
|
+
tests/test_git_error.py,sha256=lJcFlVwa1beP74fnTbn7pD8XKFLGgAL59XMYW3CF8W0,2618
|
|
23
|
+
tests/test_git_error_parser.py,sha256=UuRwVN5awD5F9G7fg9dJLowRFMt-tYfXwOyedukO1-U,6008
|
|
24
|
+
tests/test_git_runner.py,sha256=pVIaz2P2qbpIrcYBW-jCYA6HUD6uq6Ye7RDviDvoyyU,4365
|
|
25
|
+
fancygit-1.0.0.dist-info/METADATA,sha256=wHhxrZOmQ0VFpzQHDuQmrYpolhUF_9KkuqS8BdvLlmg,4802
|
|
26
|
+
fancygit-1.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
27
|
+
fancygit-1.0.0.dist-info/entry_points.txt,sha256=kUhjnub9IlB7qbE3w9KKKhKb8-QMdgLfAsTrD4Jhe_w,43
|
|
28
|
+
fancygit-1.0.0.dist-info/top_level.txt,sha256=KW3xgkz9NLMTcmmzgKvW8RFpCFkRIQ085qzq2diFf68,10
|
|
29
|
+
fancygit-1.0.0.dist-info/RECORD,,
|
src/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# This file makes the src directory a Python package
|
src/colors.py
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
FancyGit Color System
|
|
4
|
+
Provides vibrant colors and styling for the CLI interface
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
class Colors:
|
|
8
|
+
"""ANSI color codes for vibrant terminal output"""
|
|
9
|
+
|
|
10
|
+
# Reset
|
|
11
|
+
RESET = '\033[0m'
|
|
12
|
+
|
|
13
|
+
# Check if terminal supports colors
|
|
14
|
+
SUPPORTS_COLOR = True
|
|
15
|
+
|
|
16
|
+
@classmethod
|
|
17
|
+
def check_color_support(cls):
|
|
18
|
+
"""Check if terminal supports colors"""
|
|
19
|
+
import os
|
|
20
|
+
# Check if NO_COLOR environment variable is set
|
|
21
|
+
if os.environ.get('NO_COLOR'):
|
|
22
|
+
cls.SUPPORTS_COLOR = False
|
|
23
|
+
return False
|
|
24
|
+
|
|
25
|
+
# Check if TERM environment variable suggests no color support
|
|
26
|
+
term = os.environ.get('TERM', '').lower()
|
|
27
|
+
no_color_terms = ['dumb', 'unknown', 'unknown']
|
|
28
|
+
if any(no_color_term in term for no_color_term in no_color_terms):
|
|
29
|
+
cls.SUPPORTS_COLOR = False
|
|
30
|
+
return False
|
|
31
|
+
|
|
32
|
+
# Check if we're not in a TTY
|
|
33
|
+
import sys
|
|
34
|
+
if not sys.stdout.isatty():
|
|
35
|
+
cls.SUPPORTS_COLOR = False
|
|
36
|
+
return False
|
|
37
|
+
|
|
38
|
+
cls.SUPPORTS_COLOR = True
|
|
39
|
+
return True
|
|
40
|
+
|
|
41
|
+
# Regular colors
|
|
42
|
+
BLACK = '\033[30m'
|
|
43
|
+
RED = '\033[31m'
|
|
44
|
+
GREEN = '\033[32m'
|
|
45
|
+
YELLOW = '\033[33m'
|
|
46
|
+
BLUE = '\033[34m'
|
|
47
|
+
MAGENTA = '\033[35m'
|
|
48
|
+
CYAN = '\033[36m'
|
|
49
|
+
WHITE = '\033[37m'
|
|
50
|
+
|
|
51
|
+
# Bright colors
|
|
52
|
+
BRIGHT_BLACK = '\033[90m'
|
|
53
|
+
BRIGHT_RED = '\033[91m'
|
|
54
|
+
BRIGHT_GREEN = '\033[92m'
|
|
55
|
+
BRIGHT_YELLOW = '\033[93m'
|
|
56
|
+
BRIGHT_BLUE = '\033[94m'
|
|
57
|
+
BRIGHT_MAGENTA = '\033[95m'
|
|
58
|
+
BRIGHT_CYAN = '\033[96m'
|
|
59
|
+
BRIGHT_WHITE = '\033[97m'
|
|
60
|
+
|
|
61
|
+
# Background colors
|
|
62
|
+
BG_BLACK = '\033[40m'
|
|
63
|
+
BG_RED = '\033[41m'
|
|
64
|
+
BG_GREEN = '\033[42m'
|
|
65
|
+
BG_YELLOW = '\033[43m'
|
|
66
|
+
BG_BLUE = '\033[44m'
|
|
67
|
+
BG_MAGENTA = '\033[45m'
|
|
68
|
+
BG_CYAN = '\033[46m'
|
|
69
|
+
BG_WHITE = '\033[47m'
|
|
70
|
+
|
|
71
|
+
# Bright background colors
|
|
72
|
+
BG_BRIGHT_BLACK = '\033[100m'
|
|
73
|
+
BG_BRIGHT_RED = '\033[101m'
|
|
74
|
+
BG_BRIGHT_GREEN = '\033[102m'
|
|
75
|
+
BG_BRIGHT_YELLOW = '\033[103m'
|
|
76
|
+
BG_BRIGHT_BLUE = '\033[104m'
|
|
77
|
+
BG_BRIGHT_MAGENTA = '\033[105m'
|
|
78
|
+
BG_BRIGHT_CYAN = '\033[106m'
|
|
79
|
+
BG_BRIGHT_WHITE = '\033[107m'
|
|
80
|
+
|
|
81
|
+
# Text styles
|
|
82
|
+
BOLD = '\033[1m'
|
|
83
|
+
DIM = '\033[2m'
|
|
84
|
+
ITALIC = '\033[3m'
|
|
85
|
+
UNDERLINE = '\033[4m'
|
|
86
|
+
BLINK = '\033[5m'
|
|
87
|
+
REVERSE = '\033[7m'
|
|
88
|
+
STRIKETHROUGH = '\033[9m'
|
|
89
|
+
|
|
90
|
+
# FancyGit theme colors
|
|
91
|
+
PRIMARY = BRIGHT_BLUE
|
|
92
|
+
SECONDARY = BRIGHT_CYAN
|
|
93
|
+
ACCENT = BRIGHT_YELLOW
|
|
94
|
+
SUCCESS = BRIGHT_GREEN
|
|
95
|
+
WARNING = BRIGHT_YELLOW
|
|
96
|
+
ERROR = BRIGHT_RED
|
|
97
|
+
INFO = BRIGHT_MAGENTA
|
|
98
|
+
MUTED = BRIGHT_BLACK
|
|
99
|
+
|
|
100
|
+
# Git-specific colors
|
|
101
|
+
GIT_ADD = SUCCESS
|
|
102
|
+
GIT_MODIFY = WARNING
|
|
103
|
+
GIT_DELETE = ERROR
|
|
104
|
+
GIT_RENAME = INFO
|
|
105
|
+
GIT_COPY = SECONDARY
|
|
106
|
+
GIT_CONFLICT = BG_BRIGHT_RED + WHITE + BOLD
|
|
107
|
+
|
|
108
|
+
# Status indicators
|
|
109
|
+
STATUS_SUCCESS = '✅'
|
|
110
|
+
STATUS_ERROR = '❌'
|
|
111
|
+
STATUS_WARNING = '⚠️'
|
|
112
|
+
STATUS_INFO = 'ℹ️'
|
|
113
|
+
STATUS_LOADING = '🔄'
|
|
114
|
+
STATUS_AI = '🤖'
|
|
115
|
+
STATUS_BRAIN = '🧠'
|
|
116
|
+
|
|
117
|
+
@classmethod
|
|
118
|
+
def colorize(cls, text: str, color: str, style: str = '') -> str:
|
|
119
|
+
"""Apply color and style to text"""
|
|
120
|
+
if not cls.SUPPORTS_COLOR:
|
|
121
|
+
return text
|
|
122
|
+
return f"{style}{color}{text}{cls.RESET}"
|
|
123
|
+
|
|
124
|
+
@classmethod
|
|
125
|
+
def command(cls, text: str) -> str:
|
|
126
|
+
"""Color for command names"""
|
|
127
|
+
return cls.colorize(text, cls.PRIMARY, cls.BOLD)
|
|
128
|
+
|
|
129
|
+
@classmethod
|
|
130
|
+
def success(cls, text: str) -> str:
|
|
131
|
+
"""Color for success messages"""
|
|
132
|
+
return cls.colorize(text, cls.SUCCESS)
|
|
133
|
+
|
|
134
|
+
@classmethod
|
|
135
|
+
def error(cls, text: str) -> str:
|
|
136
|
+
"""Color for error messages"""
|
|
137
|
+
return cls.colorize(text, cls.ERROR, cls.BOLD)
|
|
138
|
+
|
|
139
|
+
@classmethod
|
|
140
|
+
def warning(cls, text: str) -> str:
|
|
141
|
+
"""Color for warning messages"""
|
|
142
|
+
return cls.colorize(text, cls.WARNING)
|
|
143
|
+
|
|
144
|
+
@classmethod
|
|
145
|
+
def info(cls, text: str) -> str:
|
|
146
|
+
"""Color for info messages"""
|
|
147
|
+
return cls.colorize(text, cls.INFO)
|
|
148
|
+
|
|
149
|
+
@classmethod
|
|
150
|
+
def ai_response(cls, text: str) -> str:
|
|
151
|
+
"""Color for AI responses"""
|
|
152
|
+
return cls.colorize(text, cls.SECONDARY, cls.ITALIC)
|
|
153
|
+
|
|
154
|
+
@classmethod
|
|
155
|
+
def highlight(cls, text: str) -> str:
|
|
156
|
+
"""Color for highlighted text"""
|
|
157
|
+
return cls.colorize(text, cls.ACCENT, cls.BOLD)
|
|
158
|
+
|
|
159
|
+
@classmethod
|
|
160
|
+
def muted(cls, text: str) -> str:
|
|
161
|
+
"""Color for muted text"""
|
|
162
|
+
return cls.colorize(text, cls.MUTED)
|
|
163
|
+
|
|
164
|
+
@classmethod
|
|
165
|
+
def git_status(cls, status: str) -> str:
|
|
166
|
+
"""Color for git status indicators"""
|
|
167
|
+
status_colors = {
|
|
168
|
+
'A': cls.GIT_ADD, # Added
|
|
169
|
+
'M': cls.GIT_MODIFY, # Modified
|
|
170
|
+
'D': cls.GIT_DELETE, # Deleted
|
|
171
|
+
'R': cls.GIT_RENAME, # Renamed
|
|
172
|
+
'C': cls.GIT_COPY, # Copied
|
|
173
|
+
'??': cls.MUTED, # Untracked
|
|
174
|
+
'UU': cls.GIT_CONFLICT # Conflict
|
|
175
|
+
}
|
|
176
|
+
color = status_colors.get(status, cls.WHITE)
|
|
177
|
+
return cls.colorize(status, color, cls.BOLD)
|
|
178
|
+
|
|
179
|
+
@classmethod
|
|
180
|
+
def branch_name(cls, branch: str, is_current: bool = False) -> str:
|
|
181
|
+
"""Color for branch names"""
|
|
182
|
+
if is_current:
|
|
183
|
+
return cls.colorize(branch, cls.BRIGHT_GREEN, cls.BOLD)
|
|
184
|
+
return cls.colorize(branch, cls.PRIMARY)
|
|
185
|
+
|
|
186
|
+
@classmethod
|
|
187
|
+
def file_path(cls, path: str) -> str:
|
|
188
|
+
"""Color for file paths"""
|
|
189
|
+
return cls.colorize(path, cls.BRIGHT_CYAN)
|
|
190
|
+
|
|
191
|
+
@classmethod
|
|
192
|
+
def header(cls, text: str) -> str:
|
|
193
|
+
"""Color for headers"""
|
|
194
|
+
return cls.colorize(text, cls.PRIMARY, cls.BOLD)
|
|
195
|
+
|
|
196
|
+
@classmethod
|
|
197
|
+
def subheader(cls, text: str) -> str:
|
|
198
|
+
"""Color for subheaders"""
|
|
199
|
+
return cls.colorize(text, cls.SECONDARY)
|
|
200
|
+
|
|
201
|
+
@classmethod
|
|
202
|
+
def divider(cls, char: str = '=', length: int = 60) -> str:
|
|
203
|
+
"""Create a colored divider"""
|
|
204
|
+
return cls.colorize(char * length, cls.MUTED)
|
|
205
|
+
|
|
206
|
+
@classmethod
|
|
207
|
+
def progress_bar(cls, current: int, total: int, width: int = 20) -> str:
|
|
208
|
+
"""Create a colored progress bar"""
|
|
209
|
+
filled = int(width * current / total)
|
|
210
|
+
bar = '█' * filled + '░' * (width - filled)
|
|
211
|
+
return cls.colorize(f'[{bar}] {current}/{total}', cls.SUCCESS)
|
|
212
|
+
|
|
213
|
+
@classmethod
|
|
214
|
+
def rainbow_text(cls, text: str) -> str:
|
|
215
|
+
"""Create rainbow-colored text"""
|
|
216
|
+
colors = [cls.RED, cls.BRIGHT_YELLOW, cls.GREEN, cls.CYAN, cls.BLUE, cls.MAGENTA]
|
|
217
|
+
result = []
|
|
218
|
+
for i, char in enumerate(text):
|
|
219
|
+
color = colors[i % len(colors)]
|
|
220
|
+
result.append(f"{color}{char}")
|
|
221
|
+
return ''.join(result) + cls.RESET
|
|
222
|
+
|
|
223
|
+
@classmethod
|
|
224
|
+
def gradient_text(cls, text: str, start_color: str, end_color: str) -> str:
|
|
225
|
+
"""Create gradient-colored text (simplified version)"""
|
|
226
|
+
# This is a simplified gradient - in a full implementation,
|
|
227
|
+
# you'd interpolate between RGB values
|
|
228
|
+
mid_point = len(text) // 2
|
|
229
|
+
first_half = text[:mid_point]
|
|
230
|
+
second_half = text[mid_point:]
|
|
231
|
+
|
|
232
|
+
return f"{start_color}{first_half}{end_color}{second_half}{cls.RESET}"
|
|
233
|
+
|
|
234
|
+
# Convenience functions for common color operations
|
|
235
|
+
def color_command(text: str) -> str:
|
|
236
|
+
return Colors.command(text)
|
|
237
|
+
|
|
238
|
+
def color_success(text: str) -> str:
|
|
239
|
+
return Colors.success(text)
|
|
240
|
+
|
|
241
|
+
def color_error(text: str) -> str:
|
|
242
|
+
return Colors.error(text)
|
|
243
|
+
|
|
244
|
+
def color_warning(text: str) -> str:
|
|
245
|
+
return Colors.warning(text)
|
|
246
|
+
|
|
247
|
+
def color_info(text: str) -> str:
|
|
248
|
+
return Colors.info(text)
|
|
249
|
+
|
|
250
|
+
def color_ai(text: str) -> str:
|
|
251
|
+
return Colors.ai_response(text)
|
|
252
|
+
|
|
253
|
+
def color_header(text: str) -> str:
|
|
254
|
+
return Colors.header(text)
|
|
255
|
+
|
|
256
|
+
def color_file(text: str) -> str:
|
|
257
|
+
return Colors.file_path(text)
|
|
258
|
+
|
|
259
|
+
def color_branch(text: str, current: bool = False) -> str:
|
|
260
|
+
return Colors.branch_name(text, current)
|
src/git_error.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# this class will be an immutable dataclass
|
|
2
|
+
# it will represent like the format of the data so we can then
|
|
3
|
+
# pass it to the ai models so they can be fed a consistent and organized
|
|
4
|
+
# json format that is both readable and can be easily extracted and
|
|
5
|
+
# worked on
|
|
6
|
+
|
|
7
|
+
# dataclasses are suitable here because we will primarily use it to store the
|
|
8
|
+
# data and represent it
|
|
9
|
+
# moreover to make it look cleaner without having to write an init method
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# the format will be like the following
|
|
13
|
+
# {
|
|
14
|
+
# "type": "MERGE_CONFLICT",
|
|
15
|
+
# "source" : stdout || stderr
|
|
16
|
+
# "message": "error: Merge conflict in README.md",
|
|
17
|
+
# "severity": "error" || "warning",
|
|
18
|
+
# "file": "README.md",
|
|
19
|
+
# "line": null
|
|
20
|
+
# }
|
|
21
|
+
from dataclasses import dataclass
|
|
22
|
+
from src.colors import Colors, color_error, color_warning, color_info, color_file
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen = True) # we freeze it to make it immutable
|
|
25
|
+
class GitError:
|
|
26
|
+
source: str
|
|
27
|
+
message: str
|
|
28
|
+
severity: str = "unknown"
|
|
29
|
+
|
|
30
|
+
type: str | None = None
|
|
31
|
+
file: str | None = None
|
|
32
|
+
line: int | None = None
|
|
33
|
+
|
|
34
|
+
def __str__(self) -> str:
|
|
35
|
+
severity_color = {
|
|
36
|
+
'error': color_error,
|
|
37
|
+
'warning': color_warning,
|
|
38
|
+
'info': color_info,
|
|
39
|
+
'unknown': lambda x: x
|
|
40
|
+
}.get(self.severity, lambda x: x)
|
|
41
|
+
|
|
42
|
+
type_str = str(self.type) if self.type is not None else 'None'
|
|
43
|
+
file_str = str(self.file) if self.file is not None else 'N/A'
|
|
44
|
+
line_str = str(self.line) if self.line is not None else 'N/A'
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
f"{Colors.divider('=', 60)}\n"
|
|
48
|
+
f"type: {type_str}\n"
|
|
49
|
+
f"source: {str(self.source)}\n"
|
|
50
|
+
f"message: {str(self.message)}\n"
|
|
51
|
+
f"severity: {str(self.severity)}\n"
|
|
52
|
+
f"file: {file_str}\n"
|
|
53
|
+
f"line: {line_str}\n"
|
|
54
|
+
f"{Colors.divider('=', 60)}"
|
|
55
|
+
)
|
src/git_error_parser.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from src.git_error import GitError
|
|
3
|
+
class GitErrorParser:
|
|
4
|
+
def __init__(self) -> None:
|
|
5
|
+
pass
|
|
6
|
+
|
|
7
|
+
def detect_warnings_errors(self, stdout, stderr):
|
|
8
|
+
"""Detect warnings and errors in git output"""
|
|
9
|
+
messages = []
|
|
10
|
+
|
|
11
|
+
def scan_patterns(patterns, text, type, source):
|
|
12
|
+
for pattern in patterns:
|
|
13
|
+
for match in re.findall(f'{pattern}.*', text, re.IGNORECASE | re.MULTILINE):
|
|
14
|
+
messages.append(GitError(severity = type, source=source, message=match.strip()))
|
|
15
|
+
|
|
16
|
+
# Common git error patterns (more specific to avoid false positives)
|
|
17
|
+
error_patterns = [
|
|
18
|
+
r'fatal:',
|
|
19
|
+
r'error:',
|
|
20
|
+
r'\bfailed\b',
|
|
21
|
+
r'\brejected\b',
|
|
22
|
+
r'merge conflict',
|
|
23
|
+
r'\bconflict\b',
|
|
24
|
+
r'unable to',
|
|
25
|
+
r'\.git.*error:',
|
|
26
|
+
r'^(?!.*error:).*pathspec.*did not match'
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
# Common git warning patterns (more specific to avoid false positives)
|
|
30
|
+
warning_patterns = [
|
|
31
|
+
r'warning:',
|
|
32
|
+
r'WARNING:',
|
|
33
|
+
r'.*behind.*by\s+\d+.*',
|
|
34
|
+
r'.*ahead.*by\s+\d+.*',
|
|
35
|
+
r'\bdiverged\b'
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
scan_patterns(error_patterns, stdout, "error", "stdout")
|
|
39
|
+
scan_patterns(warning_patterns, stdout, "warning", "stdout")
|
|
40
|
+
scan_patterns(error_patterns, stderr, "error", "stderr")
|
|
41
|
+
scan_patterns(warning_patterns, stderr, "warning", "stderr")
|
|
42
|
+
|
|
43
|
+
return messages
|