flutter-dev 0.1.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.
- common_utils.py +261 -0
- core/__init__.py +7 -0
- core/constants.py +57 -0
- core/state.py +25 -0
- create_page.py +288 -0
- fdev.py +258 -0
- flutter_dev-0.1.0.dist-info/METADATA +411 -0
- flutter_dev-0.1.0.dist-info/RECORD +30 -0
- flutter_dev-0.1.0.dist-info/WHEEL +5 -0
- flutter_dev-0.1.0.dist-info/entry_points.txt +5 -0
- flutter_dev-0.1.0.dist-info/licenses/LICENSE +21 -0
- flutter_dev-0.1.0.dist-info/top_level.txt +9 -0
- gemini_api.py +395 -0
- git_diff_output_editor.py +34 -0
- install_legacy.py +467 -0
- managers/__init__.py +69 -0
- managers/ai.py +113 -0
- managers/app.py +541 -0
- managers/brew.py +477 -0
- managers/build.py +436 -0
- managers/datetime.py +49 -0
- managers/device.py +207 -0
- managers/doctor.py +286 -0
- managers/git.py +981 -0
- managers/git_account.py +542 -0
- managers/merge.py +165 -0
- managers/mirror.py +205 -0
- managers/project.py +138 -0
- managers/web_deploy.py +43 -0
- switch_ai.py +181 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Maxcode
|
|
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.
|
gemini_api.py
ADDED
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Multi AI Service API for generating git commit messages
|
|
5
|
+
Supports: Groq, Mistral, SambaNova, OpenRouter
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import requests
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import re
|
|
12
|
+
import sys
|
|
13
|
+
import time
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from dotenv import load_dotenv
|
|
16
|
+
|
|
17
|
+
# Import common utilities
|
|
18
|
+
from common_utils import RED, GREEN, YELLOW, BLUE, NC
|
|
19
|
+
|
|
20
|
+
def find_env_file():
|
|
21
|
+
"""Find the tool .env file without assuming a hardcoded install path."""
|
|
22
|
+
xdg_config_home = Path(os.getenv("XDG_CONFIG_HOME", Path.home() / ".config"))
|
|
23
|
+
candidates = [
|
|
24
|
+
os.getenv("FLUTTER_DEV_ENV"),
|
|
25
|
+
xdg_config_home / "flutter-dev" / ".env",
|
|
26
|
+
Path.home() / "scripts" / "flutter-tools" / ".env",
|
|
27
|
+
Path(__file__).resolve().parent / ".env",
|
|
28
|
+
Path.cwd() / ".env",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
for candidate in candidates:
|
|
32
|
+
if candidate:
|
|
33
|
+
env_file = Path(candidate).expanduser()
|
|
34
|
+
if env_file.exists():
|
|
35
|
+
return env_file
|
|
36
|
+
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# Load environment variables from .env file when available.
|
|
41
|
+
env_path = find_env_file()
|
|
42
|
+
if env_path:
|
|
43
|
+
load_dotenv(env_path)
|
|
44
|
+
else:
|
|
45
|
+
load_dotenv()
|
|
46
|
+
|
|
47
|
+
VALID_AI_SERVICES = ("groq", "mistral", "sambanova", "openrouter")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def get_default_ai_service():
|
|
51
|
+
"""Return the selected AI service without validating at import time."""
|
|
52
|
+
return os.getenv("DEFAULT_AI_SERVICE", "groq").lower()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def get_ai_configs():
|
|
56
|
+
"""Build AI service configurations from the current environment."""
|
|
57
|
+
return {
|
|
58
|
+
"groq": {
|
|
59
|
+
"api_key": os.getenv("GROQ_API_KEY", ""),
|
|
60
|
+
"api_url": os.getenv("GROQ_API_URL", ""),
|
|
61
|
+
"model": os.getenv("GROQ_MODEL", "")
|
|
62
|
+
},
|
|
63
|
+
"mistral": {
|
|
64
|
+
"api_key": os.getenv("MISTRAL_API_KEY", ""),
|
|
65
|
+
"api_url": os.getenv("MISTRAL_API_URL", ""),
|
|
66
|
+
"model": os.getenv("MISTRAL_MODEL", "")
|
|
67
|
+
},
|
|
68
|
+
"sambanova": {
|
|
69
|
+
"api_key": os.getenv("SAMBANOVA_API_KEY", ""),
|
|
70
|
+
"api_url": os.getenv("SAMBANOVA_API_URL", ""),
|
|
71
|
+
"model": os.getenv("SAMBANOVA_MODEL", "")
|
|
72
|
+
},
|
|
73
|
+
"openrouter": {
|
|
74
|
+
"api_key": os.getenv("OPENROUTER_API_KEY", ""),
|
|
75
|
+
"api_url": os.getenv("OPENROUTER_API_URL", ""),
|
|
76
|
+
"model": os.getenv("OPENROUTER_MODEL", "")
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def get_current_config():
|
|
82
|
+
"""Validate and return the selected AI service config when it is needed."""
|
|
83
|
+
service = get_default_ai_service()
|
|
84
|
+
configs = get_ai_configs()
|
|
85
|
+
|
|
86
|
+
if service not in configs:
|
|
87
|
+
print(f"{RED}✗ Error: Invalid AI service '{service}'{NC}")
|
|
88
|
+
print(f"{YELLOW}→ Available services: {', '.join(configs.keys())}{NC}")
|
|
89
|
+
return service, None
|
|
90
|
+
|
|
91
|
+
config = configs[service]
|
|
92
|
+
if not config["api_key"]:
|
|
93
|
+
print(f"{RED}✗ Error: API key not found for {service.upper()}{NC}")
|
|
94
|
+
print(f"{YELLOW}→ Please set {service.upper()}_API_KEY in ~/.config/flutter-dev/.env{NC}")
|
|
95
|
+
return service, None
|
|
96
|
+
|
|
97
|
+
if not config["api_url"] or not config["model"]:
|
|
98
|
+
print(f"{RED}✗ Error: API URL/model not configured for {service.upper()}{NC}")
|
|
99
|
+
print(f"{YELLOW}→ Please set {service.upper()}_API_URL and {service.upper()}_MODEL{NC}")
|
|
100
|
+
return service, None
|
|
101
|
+
|
|
102
|
+
return service, config
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
DEFAULT_AI_SERVICE = get_default_ai_service()
|
|
106
|
+
AI_CONFIGS = get_ai_configs()
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def strip_markdown_code_blocks(text):
|
|
110
|
+
"""
|
|
111
|
+
Remove markdown code blocks from AI response.
|
|
112
|
+
Handles: ```text, ```markdown, ``` etc.
|
|
113
|
+
"""
|
|
114
|
+
text = text.strip()
|
|
115
|
+
|
|
116
|
+
# Pattern to match code blocks with optional language identifier
|
|
117
|
+
# Matches: ```text\n...\n```, ```markdown\n...\n```, ```\n...\n```
|
|
118
|
+
pattern = r'^```(?:text|markdown|commit)?\s*\n?(.*?)\n?```$'
|
|
119
|
+
match = re.match(pattern, text, re.DOTALL | re.IGNORECASE)
|
|
120
|
+
|
|
121
|
+
if match:
|
|
122
|
+
return match.group(1).strip()
|
|
123
|
+
|
|
124
|
+
# Also handle case where code block markers are on separate lines
|
|
125
|
+
lines = text.split('\n')
|
|
126
|
+
if lines and lines[0].strip().startswith('```'):
|
|
127
|
+
# Remove first line if it's a code block start
|
|
128
|
+
lines = lines[1:]
|
|
129
|
+
if lines and lines[-1].strip() == '```':
|
|
130
|
+
# Remove last line if it's a code block end
|
|
131
|
+
lines = lines[:-1]
|
|
132
|
+
|
|
133
|
+
return '\n'.join(lines).strip()
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def generate_commit_message(git_diff_content):
|
|
137
|
+
"""
|
|
138
|
+
Generate commit message using selected AI service based on git diff
|
|
139
|
+
"""
|
|
140
|
+
service, config = get_current_config()
|
|
141
|
+
if not config:
|
|
142
|
+
return None
|
|
143
|
+
|
|
144
|
+
prompt = f"""Based on the following git diff, generate a commit message following the Angular Conventional Commit format:
|
|
145
|
+
|
|
146
|
+
<type>(<scope>): <short summary>
|
|
147
|
+
<blank line>
|
|
148
|
+
<body>
|
|
149
|
+
<blank line>
|
|
150
|
+
<footer>
|
|
151
|
+
|
|
152
|
+
Rules:
|
|
153
|
+
- Use one of these types: feat, fix, docs, style, refactor, test, chore
|
|
154
|
+
- (scope) is optional but should describe the module/component (e.g., auth, cart, ui)
|
|
155
|
+
- Summary should be short (max 50 chars) and in imperative form
|
|
156
|
+
- Body (optional) should explain what and why, not how
|
|
157
|
+
- Footer (optional) should include BREAKING CHANGE or issue references if applicable
|
|
158
|
+
- Use proper emoticons where appropriate
|
|
159
|
+
- Don't give any long boring texts, STRICTLY no explanations needed
|
|
160
|
+
|
|
161
|
+
Git diff content:
|
|
162
|
+
{git_diff_content}
|
|
163
|
+
|
|
164
|
+
Return only the commit message without any additional text or explanations."""
|
|
165
|
+
|
|
166
|
+
payload = {
|
|
167
|
+
"model": config["model"],
|
|
168
|
+
"messages": [
|
|
169
|
+
{
|
|
170
|
+
"role": "user",
|
|
171
|
+
"content": prompt
|
|
172
|
+
}
|
|
173
|
+
],
|
|
174
|
+
"max_tokens": 500,
|
|
175
|
+
"temperature": 0.7
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
headers = {
|
|
179
|
+
"Content-Type": "application/json",
|
|
180
|
+
"Authorization": f"Bearer {config['api_key']}",
|
|
181
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
max_retries = 3
|
|
185
|
+
retry_delay = 2
|
|
186
|
+
|
|
187
|
+
for attempt in range(max_retries):
|
|
188
|
+
try:
|
|
189
|
+
# Print context information
|
|
190
|
+
diff_size = len(git_diff_content)
|
|
191
|
+
print(f"{BLUE}→ Git diff size: {diff_size} characters{NC}")
|
|
192
|
+
print(f"{YELLOW}Generating commit message using {service.upper()} AI ({config['model']})... (Attempt {attempt + 1}/{max_retries}){NC}")
|
|
193
|
+
|
|
194
|
+
# Make request
|
|
195
|
+
response = requests.post(
|
|
196
|
+
config["api_url"],
|
|
197
|
+
json=payload,
|
|
198
|
+
headers=headers,
|
|
199
|
+
timeout=30
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
if response.status_code == 200:
|
|
203
|
+
result = response.json()
|
|
204
|
+
|
|
205
|
+
if 'choices' in result and len(result['choices']) > 0:
|
|
206
|
+
raw_message = result['choices'][0]['message']['content'].strip()
|
|
207
|
+
# Remove markdown code blocks if AI wrapped the response
|
|
208
|
+
commit_message = strip_markdown_code_blocks(raw_message)
|
|
209
|
+
print(f"{GREEN}✓ Commit message generated successfully using {service.upper()}{NC}")
|
|
210
|
+
return commit_message
|
|
211
|
+
else:
|
|
212
|
+
print(f"{RED}Error: No content generated from {service.upper()} API{NC}")
|
|
213
|
+
if attempt < max_retries - 1:
|
|
214
|
+
print(f"{YELLOW}Retrying in {retry_delay} seconds...{NC}")
|
|
215
|
+
time.sleep(retry_delay)
|
|
216
|
+
continue
|
|
217
|
+
return None
|
|
218
|
+
|
|
219
|
+
elif response.status_code == 429:
|
|
220
|
+
if attempt < max_retries - 1:
|
|
221
|
+
wait_time = retry_delay * (2 ** attempt)
|
|
222
|
+
print(f"{YELLOW}⚠ Rate limit reached (429). Waiting {wait_time} seconds...{NC}")
|
|
223
|
+
time.sleep(wait_time)
|
|
224
|
+
continue
|
|
225
|
+
else:
|
|
226
|
+
print(f"{RED}✗ Error 429: Rate limit exceeded.{NC}")
|
|
227
|
+
return None
|
|
228
|
+
|
|
229
|
+
elif response.status_code == 404:
|
|
230
|
+
print(f"{RED}✗ Error 404: Model not found - {config['model']}{NC}")
|
|
231
|
+
print(f"{RED}Error details: {response.text}{NC}")
|
|
232
|
+
return None
|
|
233
|
+
|
|
234
|
+
elif response.status_code == 400:
|
|
235
|
+
print(f"{RED}✗ Bad Request (400): {response.text}{NC}")
|
|
236
|
+
return None
|
|
237
|
+
|
|
238
|
+
elif response.status_code == 401:
|
|
239
|
+
print(f"{RED}✗ Error 401: API key is invalid or unauthorized{NC}")
|
|
240
|
+
print(f"{RED}Error details: {response.text}{NC}")
|
|
241
|
+
return None
|
|
242
|
+
|
|
243
|
+
elif response.status_code == 403:
|
|
244
|
+
print(f"{RED}✗ Error 403: API key doesn't have permission{NC}")
|
|
245
|
+
print(f"{RED}Error details: {response.text}{NC}")
|
|
246
|
+
return None
|
|
247
|
+
|
|
248
|
+
else:
|
|
249
|
+
print(f"{RED}✗ HTTP Error {response.status_code}: {response.reason}{NC}")
|
|
250
|
+
print(f"{RED}Details: {response.text}{NC}")
|
|
251
|
+
if attempt < max_retries - 1:
|
|
252
|
+
print(f"{YELLOW}Retrying in {retry_delay} seconds...{NC}")
|
|
253
|
+
time.sleep(retry_delay)
|
|
254
|
+
continue
|
|
255
|
+
return None
|
|
256
|
+
|
|
257
|
+
except requests.exceptions.Timeout:
|
|
258
|
+
print(f"{RED}Error: Request timed out{NC}")
|
|
259
|
+
if attempt < max_retries - 1:
|
|
260
|
+
print(f"{YELLOW}Retrying in {retry_delay} seconds...{NC}")
|
|
261
|
+
time.sleep(retry_delay)
|
|
262
|
+
continue
|
|
263
|
+
return None
|
|
264
|
+
|
|
265
|
+
except requests.exceptions.ConnectionError as e:
|
|
266
|
+
print(f"{RED}Error: Network connection failed - {e}{NC}")
|
|
267
|
+
if attempt < max_retries - 1:
|
|
268
|
+
print(f"{YELLOW}Retrying in {retry_delay} seconds...{NC}")
|
|
269
|
+
time.sleep(retry_delay)
|
|
270
|
+
continue
|
|
271
|
+
return None
|
|
272
|
+
|
|
273
|
+
except json.JSONDecodeError as e:
|
|
274
|
+
print(f"{RED}Error: Failed to parse API response - {e}{NC}")
|
|
275
|
+
if attempt < max_retries - 1:
|
|
276
|
+
print(f"{YELLOW}Retrying in {retry_delay} seconds...{NC}")
|
|
277
|
+
time.sleep(retry_delay)
|
|
278
|
+
continue
|
|
279
|
+
return None
|
|
280
|
+
|
|
281
|
+
except Exception as e:
|
|
282
|
+
print(f"{RED}Error: Unexpected error - {e}{NC}")
|
|
283
|
+
if attempt < max_retries - 1:
|
|
284
|
+
print(f"{YELLOW}Retrying in {retry_delay} seconds...{NC}")
|
|
285
|
+
time.sleep(retry_delay)
|
|
286
|
+
continue
|
|
287
|
+
return None
|
|
288
|
+
|
|
289
|
+
# All retries exhausted
|
|
290
|
+
print(f"{RED}✗ {service.upper()} API failed after {max_retries} attempts{NC}")
|
|
291
|
+
return None
|
|
292
|
+
|
|
293
|
+
def test_api_connection():
|
|
294
|
+
"""
|
|
295
|
+
Test selected AI service API connection
|
|
296
|
+
"""
|
|
297
|
+
service, config = get_current_config()
|
|
298
|
+
if not config:
|
|
299
|
+
return False
|
|
300
|
+
|
|
301
|
+
test_prompt = "Hello, respond with 'API connection successful'"
|
|
302
|
+
|
|
303
|
+
payload = {
|
|
304
|
+
"model": config["model"],
|
|
305
|
+
"messages": [
|
|
306
|
+
{
|
|
307
|
+
"role": "user",
|
|
308
|
+
"content": test_prompt
|
|
309
|
+
}
|
|
310
|
+
],
|
|
311
|
+
"max_tokens": 50,
|
|
312
|
+
"temperature": 0.7
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
headers = {
|
|
316
|
+
"Content-Type": "application/json",
|
|
317
|
+
"Authorization": f"Bearer {config['api_key']}",
|
|
318
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
try:
|
|
322
|
+
print(f"{BLUE}Testing {service.upper()} API connection with {config['model']}...{NC}")
|
|
323
|
+
|
|
324
|
+
# Make request
|
|
325
|
+
response = requests.post(
|
|
326
|
+
config["api_url"],
|
|
327
|
+
json=payload,
|
|
328
|
+
headers=headers,
|
|
329
|
+
timeout=10
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
if response.status_code == 200:
|
|
333
|
+
result = response.json()
|
|
334
|
+
print(f"{GREEN}✓ {service.upper()} API connection successful{NC}")
|
|
335
|
+
|
|
336
|
+
# Print response for debugging
|
|
337
|
+
if 'choices' in result and len(result['choices']) > 0:
|
|
338
|
+
text = result['choices'][0]['message']['content']
|
|
339
|
+
print(f"{BLUE}API Response: {text}{NC}")
|
|
340
|
+
|
|
341
|
+
return True
|
|
342
|
+
|
|
343
|
+
elif response.status_code == 429:
|
|
344
|
+
print(f"{RED}✗ HTTP Error 429: Rate limit reached{NC}")
|
|
345
|
+
print(f"{YELLOW}⚠ Wait a few minutes and try again.{NC}")
|
|
346
|
+
return False
|
|
347
|
+
|
|
348
|
+
elif response.status_code == 404:
|
|
349
|
+
print(f"{RED}✗ HTTP Error 404: Model not found - {config['model']}{NC}")
|
|
350
|
+
print(f"{RED}Error details: {response.text}{NC}")
|
|
351
|
+
return False
|
|
352
|
+
|
|
353
|
+
elif response.status_code == 400:
|
|
354
|
+
print(f"{RED}✗ HTTP Error 400: Bad Request{NC}")
|
|
355
|
+
print(f"{RED}Error details: {response.text}{NC}")
|
|
356
|
+
return False
|
|
357
|
+
|
|
358
|
+
elif response.status_code == 401:
|
|
359
|
+
print(f"{RED}✗ HTTP Error 401: API key is invalid or unauthorized{NC}")
|
|
360
|
+
print(f"{RED}Error details: {response.text}{NC}")
|
|
361
|
+
return False
|
|
362
|
+
|
|
363
|
+
elif response.status_code == 403:
|
|
364
|
+
print(f"{RED}✗ HTTP Error 403: API key doesn't have permission{NC}")
|
|
365
|
+
print(f"{RED}Error details: {response.text}{NC}")
|
|
366
|
+
return False
|
|
367
|
+
|
|
368
|
+
else:
|
|
369
|
+
print(f"{RED}✗ {service.upper()} API connection failed with status {response.status_code}{NC}")
|
|
370
|
+
print(f"{RED}Response: {response.text}{NC}")
|
|
371
|
+
return False
|
|
372
|
+
|
|
373
|
+
except requests.exceptions.Timeout:
|
|
374
|
+
print(f"{RED}✗ Request timed out{NC}")
|
|
375
|
+
return False
|
|
376
|
+
|
|
377
|
+
except requests.exceptions.ConnectionError as e:
|
|
378
|
+
print(f"{RED}✗ Network Error: {e}{NC}")
|
|
379
|
+
return False
|
|
380
|
+
|
|
381
|
+
except Exception as e:
|
|
382
|
+
print(f"{RED}✗ {service.upper()} API connection failed: {e}{NC}")
|
|
383
|
+
return False
|
|
384
|
+
|
|
385
|
+
def main():
|
|
386
|
+
# Test the API connection
|
|
387
|
+
DEFAULT_AI_SERVICE = get_default_ai_service()
|
|
388
|
+
print(f"{BLUE}{'='*50}{NC}")
|
|
389
|
+
print(f"{BLUE}{DEFAULT_AI_SERVICE.upper()} API Connection Test{NC}")
|
|
390
|
+
print(f"{BLUE}{'='*50}{NC}")
|
|
391
|
+
return 0 if test_api_connection() else 1
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
if __name__ == "__main__":
|
|
395
|
+
sys.exit(main())
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
# Import common utilities
|
|
7
|
+
from common_utils import open_file_with_default_app, BLUE, NC
|
|
8
|
+
|
|
9
|
+
def main():
|
|
10
|
+
# Use current working directory instead of hardcoded path
|
|
11
|
+
folder_path = os.getcwd()
|
|
12
|
+
|
|
13
|
+
print(f"{BLUE}Working in directory: {folder_path}{NC}")
|
|
14
|
+
|
|
15
|
+
# Change the working directory to the specified folder (already current directory)
|
|
16
|
+
os.chdir(folder_path)
|
|
17
|
+
|
|
18
|
+
# Execute the git diff command and write output to diff_output.txt
|
|
19
|
+
with open('diff_output.txt', 'w') as file:
|
|
20
|
+
subprocess.run(['git', 'diff'], stdout=file)
|
|
21
|
+
|
|
22
|
+
# Text to append
|
|
23
|
+
text_to_append = '\n================\n\nhere is my changes...give me git push msg and description of changes with using of proper emoticons...dont give any long boring texts, "STRICTLY i dont need any explanations"\n'
|
|
24
|
+
|
|
25
|
+
# Append the specified text to diff_output.txt
|
|
26
|
+
with open('diff_output.txt', 'a') as file:
|
|
27
|
+
file.write(text_to_append)
|
|
28
|
+
|
|
29
|
+
# Open the file with default editor
|
|
30
|
+
open_file_with_default_app('diff_output.txt')
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
if __name__ == "__main__":
|
|
34
|
+
main()
|