redgit 1.3.0__tar.gz → 1.3.2__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.
- {redgit-1.3.0/redgit.egg-info → redgit-1.3.2}/PKG-INFO +24 -5
- {redgit-1.3.0 → redgit-1.3.2}/README.md +21 -4
- {redgit-1.3.0 → redgit-1.3.2}/pyproject.toml +5 -1
- redgit-1.3.2/redgit/__init__.py +1 -0
- {redgit-1.3.0 → redgit-1.3.2}/redgit/cli.py +15 -7
- redgit-1.3.2/redgit/commands/backup.py +168 -0
- {redgit-1.3.0 → redgit-1.3.2}/redgit/commands/ci.py +2 -2
- {redgit-1.3.0 → redgit-1.3.2}/redgit/commands/config.py +6 -6
- {redgit-1.3.0 → redgit-1.3.2}/redgit/commands/daily.py +4 -4
- {redgit-1.3.0 → redgit-1.3.2}/redgit/commands/init.py +2 -2
- {redgit-1.3.0 → redgit-1.3.2}/redgit/commands/integration.py +40 -10
- {redgit-1.3.0 → redgit-1.3.2}/redgit/commands/notify.py +1 -1
- {redgit-1.3.0 → redgit-1.3.2}/redgit/commands/plugin.py +2 -2
- redgit-1.3.2/redgit/commands/poker.py +1111 -0
- {redgit-1.3.0 → redgit-1.3.2}/redgit/commands/propose.py +1032 -452
- {redgit-1.3.0 → redgit-1.3.2}/redgit/commands/push.py +465 -28
- {redgit-1.3.0 → redgit-1.3.2}/redgit/commands/quality.py +21 -13
- {redgit-1.3.0 → redgit-1.3.2}/redgit/commands/scout.py +385 -2
- {redgit-1.3.0 → redgit-1.3.2}/redgit/commands/tap.py +7 -7
- redgit-1.3.2/redgit/commands/tunnel.py +138 -0
- redgit-1.3.2/redgit/commands/webhook.py +280 -0
- redgit-1.3.2/redgit/core/common/__init__.py +52 -0
- redgit-1.3.2/redgit/core/common/backup.py +330 -0
- {redgit-1.3.0/redgit/core → redgit-1.3.2/redgit/core/common}/constants.py +11 -0
- {redgit-1.3.0/redgit/core → redgit-1.3.2/redgit/core/common}/gitops.py +121 -17
- {redgit-1.3.0/redgit/core → redgit-1.3.2/redgit/core/common}/llm.py +125 -4
- {redgit-1.3.0/redgit/core → redgit-1.3.2/redgit/core/common}/prompt.py +67 -7
- redgit-1.3.2/redgit/core/daily/__init__.py +9 -0
- redgit-1.3.2/redgit/core/poker/__init__.py +24 -0
- redgit-1.3.2/redgit/core/poker/ai_voter.py +176 -0
- redgit-1.3.2/redgit/core/poker/client.py +456 -0
- redgit-1.3.2/redgit/core/poker/server.py +597 -0
- redgit-1.3.2/redgit/core/poker/session.py +508 -0
- redgit-1.3.2/redgit/core/poker/ui.py +875 -0
- redgit-1.3.2/redgit/core/propose/__init__.py +61 -0
- redgit-1.3.2/redgit/core/propose/analysis.py +500 -0
- redgit-1.3.2/redgit/core/propose/commit.py +169 -0
- redgit-1.3.2/redgit/core/propose/display.py +507 -0
- redgit-1.3.2/redgit/core/quality/__init__.py +29 -0
- {redgit-1.3.0/redgit/core → redgit-1.3.2/redgit/core/quality}/semgrep.py +31 -6
- {redgit-1.3.0 → redgit-1.3.2}/redgit/core/scout/__init__.py +289 -9
- redgit-1.3.2/redgit/core/tap/__init__.py +25 -0
- redgit-1.3.0/redgit/core/tap.py → redgit-1.3.2/redgit/core/tap/manager.py +1 -1
- redgit-1.3.2/redgit/core/webhook/__init__.py +16 -0
- redgit-1.3.2/redgit/core/webhook/actions.py +312 -0
- redgit-1.3.2/redgit/core/webhook/server.py +439 -0
- {redgit-1.3.0 → redgit-1.3.2}/redgit/integrations/base.py +404 -5
- {redgit-1.3.0 → redgit-1.3.2}/redgit/integrations/registry.py +50 -4
- {redgit-1.3.0 → redgit-1.3.2}/redgit/plugins/registry.py +1 -1
- redgit-1.3.2/redgit/prompts/commit/multi_task.md +77 -0
- redgit-1.3.2/redgit/utils/dependency.py +165 -0
- {redgit-1.3.0 → redgit-1.3.2}/redgit/utils/formatting.py +1 -1
- redgit-1.3.2/redgit/utils/notifications.py +639 -0
- {redgit-1.3.0 → redgit-1.3.2/redgit.egg-info}/PKG-INFO +24 -5
- {redgit-1.3.0 → redgit-1.3.2}/redgit.egg-info/SOURCES.txt +33 -9
- {redgit-1.3.0 → redgit-1.3.2}/redgit.egg-info/requires.txt +3 -0
- redgit-1.3.0/redgit/__init__.py +0 -1
- redgit-1.3.0/redgit/utils/notifications.py +0 -264
- {redgit-1.3.0 → redgit-1.3.2}/LICENSE +0 -0
- {redgit-1.3.0 → redgit-1.3.2}/redgit/commands/__init__.py +0 -0
- {redgit-1.3.0/redgit/core → redgit-1.3.2/redgit/core/common}/config.py +0 -0
- {redgit-1.3.0/redgit/core → redgit-1.3.2/redgit/core/common}/llm_providers.json +0 -0
- /redgit-1.3.0/redgit/core/daily_state.py → /redgit-1.3.2/redgit/core/daily/state.py +0 -0
- {redgit-1.3.0 → redgit-1.3.2}/redgit/core/scout/team.py +0 -0
- {redgit-1.3.0 → redgit-1.3.2}/redgit/integrations/__init__.py +0 -0
- {redgit-1.3.0 → redgit-1.3.2}/redgit/plugins/__init__.py +0 -0
- {redgit-1.3.0 → redgit-1.3.2}/redgit/plugins/base.py +0 -0
- {redgit-1.3.0 → redgit-1.3.2}/redgit/prompts/__init__.py +0 -0
- {redgit-1.3.0 → redgit-1.3.2}/redgit/prompts/commit/default.md +0 -0
- {redgit-1.3.0 → redgit-1.3.2}/redgit/prompts/commit/minimal.md +0 -0
- {redgit-1.3.0 → redgit-1.3.2}/redgit/prompts/commit/task_filtered.md +0 -0
- {redgit-1.3.0 → redgit-1.3.2}/redgit/prompts/daily/default.md +0 -0
- {redgit-1.3.0 → redgit-1.3.2}/redgit/prompts/quality/default.md +0 -0
- {redgit-1.3.0 → redgit-1.3.2}/redgit/splash.py +0 -0
- {redgit-1.3.0 → redgit-1.3.2}/redgit/utils/__init__.py +0 -0
- {redgit-1.3.0 → redgit-1.3.2}/redgit/utils/console.py +0 -0
- {redgit-1.3.0 → redgit-1.3.2}/redgit/utils/editor.py +0 -0
- {redgit-1.3.0 → redgit-1.3.2}/redgit/utils/logging.py +0 -0
- {redgit-1.3.0 → redgit-1.3.2}/redgit/utils/security.py +0 -0
- {redgit-1.3.0 → redgit-1.3.2}/redgit.egg-info/dependency_links.txt +0 -0
- {redgit-1.3.0 → redgit-1.3.2}/redgit.egg-info/entry_points.txt +0 -0
- {redgit-1.3.0 → redgit-1.3.2}/redgit.egg-info/top_level.txt +0 -0
- {redgit-1.3.0 → redgit-1.3.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: redgit
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.2
|
|
4
4
|
Summary: AI-powered Git workflow assistant with task management integration
|
|
5
5
|
Author-email: Your Name <your.email@example.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -35,10 +35,12 @@ Provides-Extra: dev
|
|
|
35
35
|
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
36
36
|
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
|
|
37
37
|
Requires-Dist: pytest-mock>=3.12.0; extra == "dev"
|
|
38
|
+
Provides-Extra: poker
|
|
39
|
+
Requires-Dist: websockets>=12.0; extra == "poker"
|
|
38
40
|
Dynamic: license-file
|
|
39
41
|
|
|
40
42
|
<p align="center">
|
|
41
|
-
<img src="https://raw.githubusercontent.com/ertiz82/redgit/main/assets/logo.svg?v=1.3.
|
|
43
|
+
<img src="https://raw.githubusercontent.com/ertiz82/redgit/main/assets/logo.svg?v=1.3.2" alt="RedGit Logo" width="400"/>
|
|
42
44
|
</p>
|
|
43
45
|
|
|
44
46
|
<p align="center">
|
|
@@ -107,6 +109,8 @@ rg push # Push and update Jira/Linear
|
|
|
107
109
|
| **Code Quality** | Built-in quality checks with ruff/flake8 + AI analysis |
|
|
108
110
|
| **Semgrep Integration** | Multi-language static analysis (35+ languages) for security & best practices |
|
|
109
111
|
| **CI/CD Integration** | Trigger and monitor pipelines from the command line |
|
|
112
|
+
| **Planning Poker** | Real-time sprint estimation with WebSocket, task distribution, sprint creation |
|
|
113
|
+
| **Tunnel Support** | Expose local ports for webhooks and remote access (ngrok, cloudflare, etc.) |
|
|
110
114
|
| **Plugin System** | Framework-specific prompts (Laravel, Django, etc.) |
|
|
111
115
|
|
|
112
116
|
---
|
|
@@ -207,6 +211,7 @@ RedGit supports 30+ integrations across different categories:
|
|
|
207
211
|
| **CI/CD** | GitHub Actions, GitLab CI, Jenkins, CircleCI |
|
|
208
212
|
| **Notifications** | Slack, Discord, Telegram, MS Teams |
|
|
209
213
|
| **Code Quality** | SonarQube, Snyk, Codecov, Codacy |
|
|
214
|
+
| **Tunnel** | ngrok, Cloudflare Tunnel, localtunnel, bore, serveo |
|
|
210
215
|
|
|
211
216
|
Install integrations from [RedGit Tap](https://github.com/ertiz82/redgit-tap):
|
|
212
217
|
|
|
@@ -220,17 +225,31 @@ rg install sonarqube
|
|
|
220
225
|
|
|
221
226
|
## Documentation
|
|
222
227
|
|
|
228
|
+
### Core Documentation
|
|
229
|
+
|
|
223
230
|
| Section | Description |
|
|
224
231
|
|---------|-------------|
|
|
225
232
|
| **[Getting Started](docs/getting-started.md)** | Installation and first steps |
|
|
226
233
|
| **[Commands Reference](docs/commands.md)** | All CLI commands |
|
|
227
234
|
| **[Configuration](docs/configuration.md)** | Config file options |
|
|
228
|
-
| **[Integrations](docs/integrations/index.md)** | Task management, code hosting, CI/CD |
|
|
229
|
-
| **[Plugins](docs/plugins/index.md)** | Framework plugins and release management |
|
|
230
235
|
| **[Workflows](docs/workflows.md)** | Local merge vs merge request strategies |
|
|
231
|
-
| **[Custom Integrations](docs/integrations/custom.md)** | Build your own integrations |
|
|
232
236
|
| **[Troubleshooting](docs/troubleshooting.md)** | Common issues and solutions |
|
|
233
237
|
|
|
238
|
+
### Features
|
|
239
|
+
|
|
240
|
+
| Feature | Description |
|
|
241
|
+
|---------|-------------|
|
|
242
|
+
| **[Planning Poker](docs/planning-poker.md)** | Real-time sprint estimation with WebSocket |
|
|
243
|
+
| **[Tunnel](docs/tunnel.md)** | Expose local ports for remote access |
|
|
244
|
+
|
|
245
|
+
### Integrations & Plugins
|
|
246
|
+
|
|
247
|
+
| Section | Description |
|
|
248
|
+
|---------|-------------|
|
|
249
|
+
| **[Integrations](docs/integrations/index.md)** | Task management, code hosting, CI/CD, notifications |
|
|
250
|
+
| **[Plugins](docs/plugins/index.md)** | Framework plugins and release management |
|
|
251
|
+
| **[Custom Integrations](docs/integrations/custom.md)** | Build your own integrations |
|
|
252
|
+
|
|
234
253
|
---
|
|
235
254
|
|
|
236
255
|
## LLM Support
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="https://raw.githubusercontent.com/ertiz82/redgit/main/assets/logo.svg?v=1.3.
|
|
2
|
+
<img src="https://raw.githubusercontent.com/ertiz82/redgit/main/assets/logo.svg?v=1.3.2" alt="RedGit Logo" width="400"/>
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
@@ -68,6 +68,8 @@ rg push # Push and update Jira/Linear
|
|
|
68
68
|
| **Code Quality** | Built-in quality checks with ruff/flake8 + AI analysis |
|
|
69
69
|
| **Semgrep Integration** | Multi-language static analysis (35+ languages) for security & best practices |
|
|
70
70
|
| **CI/CD Integration** | Trigger and monitor pipelines from the command line |
|
|
71
|
+
| **Planning Poker** | Real-time sprint estimation with WebSocket, task distribution, sprint creation |
|
|
72
|
+
| **Tunnel Support** | Expose local ports for webhooks and remote access (ngrok, cloudflare, etc.) |
|
|
71
73
|
| **Plugin System** | Framework-specific prompts (Laravel, Django, etc.) |
|
|
72
74
|
|
|
73
75
|
---
|
|
@@ -168,6 +170,7 @@ RedGit supports 30+ integrations across different categories:
|
|
|
168
170
|
| **CI/CD** | GitHub Actions, GitLab CI, Jenkins, CircleCI |
|
|
169
171
|
| **Notifications** | Slack, Discord, Telegram, MS Teams |
|
|
170
172
|
| **Code Quality** | SonarQube, Snyk, Codecov, Codacy |
|
|
173
|
+
| **Tunnel** | ngrok, Cloudflare Tunnel, localtunnel, bore, serveo |
|
|
171
174
|
|
|
172
175
|
Install integrations from [RedGit Tap](https://github.com/ertiz82/redgit-tap):
|
|
173
176
|
|
|
@@ -181,17 +184,31 @@ rg install sonarqube
|
|
|
181
184
|
|
|
182
185
|
## Documentation
|
|
183
186
|
|
|
187
|
+
### Core Documentation
|
|
188
|
+
|
|
184
189
|
| Section | Description |
|
|
185
190
|
|---------|-------------|
|
|
186
191
|
| **[Getting Started](docs/getting-started.md)** | Installation and first steps |
|
|
187
192
|
| **[Commands Reference](docs/commands.md)** | All CLI commands |
|
|
188
193
|
| **[Configuration](docs/configuration.md)** | Config file options |
|
|
189
|
-
| **[Integrations](docs/integrations/index.md)** | Task management, code hosting, CI/CD |
|
|
190
|
-
| **[Plugins](docs/plugins/index.md)** | Framework plugins and release management |
|
|
191
194
|
| **[Workflows](docs/workflows.md)** | Local merge vs merge request strategies |
|
|
192
|
-
| **[Custom Integrations](docs/integrations/custom.md)** | Build your own integrations |
|
|
193
195
|
| **[Troubleshooting](docs/troubleshooting.md)** | Common issues and solutions |
|
|
194
196
|
|
|
197
|
+
### Features
|
|
198
|
+
|
|
199
|
+
| Feature | Description |
|
|
200
|
+
|---------|-------------|
|
|
201
|
+
| **[Planning Poker](docs/planning-poker.md)** | Real-time sprint estimation with WebSocket |
|
|
202
|
+
| **[Tunnel](docs/tunnel.md)** | Expose local ports for remote access |
|
|
203
|
+
|
|
204
|
+
### Integrations & Plugins
|
|
205
|
+
|
|
206
|
+
| Section | Description |
|
|
207
|
+
|---------|-------------|
|
|
208
|
+
| **[Integrations](docs/integrations/index.md)** | Task management, code hosting, CI/CD, notifications |
|
|
209
|
+
| **[Plugins](docs/plugins/index.md)** | Framework plugins and release management |
|
|
210
|
+
| **[Custom Integrations](docs/integrations/custom.md)** | Build your own integrations |
|
|
211
|
+
|
|
195
212
|
---
|
|
196
213
|
|
|
197
214
|
## LLM Support
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "redgit"
|
|
7
|
-
version = "1.3.
|
|
7
|
+
version = "1.3.2"
|
|
8
8
|
description = "AI-powered Git workflow assistant with task management integration"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -43,6 +43,9 @@ dev = [
|
|
|
43
43
|
"pytest-cov>=4.1.0",
|
|
44
44
|
"pytest-mock>=3.12.0",
|
|
45
45
|
]
|
|
46
|
+
poker = [
|
|
47
|
+
"websockets>=12.0",
|
|
48
|
+
]
|
|
46
49
|
|
|
47
50
|
[project.urls]
|
|
48
51
|
Homepage = "https://github.com/ertiz82/redgit"
|
|
@@ -61,6 +64,7 @@ include = ["redgit*"]
|
|
|
61
64
|
[tool.setuptools.package-data]
|
|
62
65
|
redgit = [
|
|
63
66
|
"core/*.json",
|
|
67
|
+
"core/common/*.json",
|
|
64
68
|
"integrations/*.json",
|
|
65
69
|
"prompts/**/*.md"
|
|
66
70
|
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.3.2"
|
|
@@ -18,6 +18,10 @@ from redgit.commands.ci import ci_app
|
|
|
18
18
|
from redgit.commands.config import config_app
|
|
19
19
|
from redgit.commands.quality import quality_app
|
|
20
20
|
from redgit.commands.scout import scout_app
|
|
21
|
+
from redgit.commands.webhook import webhook_app
|
|
22
|
+
from redgit.commands.tunnel import tunnel_app
|
|
23
|
+
from redgit.commands.poker import poker_app
|
|
24
|
+
from redgit.commands.backup import app as backup_app
|
|
21
25
|
|
|
22
26
|
|
|
23
27
|
def version_callback(value: bool):
|
|
@@ -61,12 +65,16 @@ app.add_typer(ci_app, name="ci")
|
|
|
61
65
|
app.add_typer(config_app, name="config")
|
|
62
66
|
app.add_typer(quality_app, name="quality")
|
|
63
67
|
app.add_typer(scout_app, name="scout")
|
|
68
|
+
app.add_typer(webhook_app, name="webhook")
|
|
69
|
+
app.add_typer(tunnel_app, name="tunnel")
|
|
70
|
+
app.add_typer(poker_app, name="poker")
|
|
71
|
+
app.add_typer(backup_app, name="backup")
|
|
64
72
|
|
|
65
73
|
|
|
66
74
|
def _load_plugin_commands():
|
|
67
75
|
"""Dynamically load commands from enabled plugins."""
|
|
68
76
|
try:
|
|
69
|
-
from redgit.core.config import ConfigManager
|
|
77
|
+
from redgit.core.common.config import ConfigManager
|
|
70
78
|
from redgit.plugins.registry import get_enabled_plugin_commands, get_all_plugin_shortcuts
|
|
71
79
|
|
|
72
80
|
config = ConfigManager().load()
|
|
@@ -93,13 +101,13 @@ def _load_plugin_commands():
|
|
|
93
101
|
|
|
94
102
|
|
|
95
103
|
def _load_integration_commands():
|
|
96
|
-
"""Dynamically load commands from
|
|
104
|
+
"""Dynamically load commands from installed integrations."""
|
|
97
105
|
try:
|
|
98
|
-
from redgit.
|
|
99
|
-
from redgit.integrations.registry import get_active_integration_commands
|
|
106
|
+
from redgit.integrations.registry import get_all_integration_commands
|
|
100
107
|
|
|
101
|
-
|
|
102
|
-
|
|
108
|
+
# Load commands for ALL installed integrations (not just active ones)
|
|
109
|
+
# This allows `rg jira`, `rg gitlab` etc. to work regardless of activation
|
|
110
|
+
commands = get_all_integration_commands()
|
|
103
111
|
|
|
104
112
|
for name, cmd_app in commands.items():
|
|
105
113
|
app.add_typer(cmd_app, name=name)
|
|
@@ -111,7 +119,7 @@ def _load_integration_commands():
|
|
|
111
119
|
def _setup_logging():
|
|
112
120
|
"""Set up logging based on config."""
|
|
113
121
|
try:
|
|
114
|
-
from redgit.core.config import ConfigManager
|
|
122
|
+
from redgit.core.common.config import ConfigManager
|
|
115
123
|
|
|
116
124
|
config_manager = ConfigManager()
|
|
117
125
|
logging_config = config_manager.get_logging_config()
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Backup command for RedGit.
|
|
3
|
+
|
|
4
|
+
Provides CLI interface for managing working tree backups.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.table import Table
|
|
10
|
+
|
|
11
|
+
from ..core.common.backup import BackupManager
|
|
12
|
+
from ..core.common.gitops import GitOps
|
|
13
|
+
|
|
14
|
+
app = typer.Typer(help="Manage working tree backups")
|
|
15
|
+
console = Console()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@app.command("list")
|
|
19
|
+
def list_cmd():
|
|
20
|
+
"""List all backups."""
|
|
21
|
+
try:
|
|
22
|
+
gitops = GitOps()
|
|
23
|
+
except Exception:
|
|
24
|
+
gitops = None
|
|
25
|
+
|
|
26
|
+
backup_manager = BackupManager(gitops)
|
|
27
|
+
backups = backup_manager.list_backups()
|
|
28
|
+
|
|
29
|
+
if not backups:
|
|
30
|
+
console.print("[yellow]No backups found.[/yellow]")
|
|
31
|
+
return
|
|
32
|
+
|
|
33
|
+
table = Table(title="RG Backups")
|
|
34
|
+
table.add_column("ID", style="cyan")
|
|
35
|
+
table.add_column("Date", style="dim")
|
|
36
|
+
table.add_column("Branch")
|
|
37
|
+
table.add_column("Files")
|
|
38
|
+
table.add_column("Status", style="bold")
|
|
39
|
+
|
|
40
|
+
for backup in backups:
|
|
41
|
+
status = backup.get("status", "unknown")
|
|
42
|
+
status_color = {
|
|
43
|
+
"created": "yellow",
|
|
44
|
+
"completed": "green",
|
|
45
|
+
"failed": "red",
|
|
46
|
+
"restored": "blue"
|
|
47
|
+
}.get(status, "white")
|
|
48
|
+
|
|
49
|
+
table.add_row(
|
|
50
|
+
backup.get("id", "?"),
|
|
51
|
+
backup.get("created_at", "")[:19],
|
|
52
|
+
backup.get("base_branch", "?"),
|
|
53
|
+
str(len(backup.get("files", []))),
|
|
54
|
+
f"[{status_color}]{status}[/{status_color}]"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
console.print(table)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@app.command("restore")
|
|
61
|
+
def restore_cmd(
|
|
62
|
+
backup_id: str = typer.Argument("latest", help="Backup ID or 'latest'")
|
|
63
|
+
):
|
|
64
|
+
"""Restore working tree from backup."""
|
|
65
|
+
try:
|
|
66
|
+
gitops = GitOps()
|
|
67
|
+
except Exception:
|
|
68
|
+
gitops = None
|
|
69
|
+
|
|
70
|
+
backup_manager = BackupManager(gitops)
|
|
71
|
+
|
|
72
|
+
# Check if backup exists
|
|
73
|
+
backup = backup_manager.get_backup(backup_id)
|
|
74
|
+
if not backup:
|
|
75
|
+
console.print(f"[red]Backup not found: {backup_id}[/red]")
|
|
76
|
+
raise typer.Exit(1)
|
|
77
|
+
|
|
78
|
+
console.print(f"[yellow]Restoring backup: {backup.get('id', backup_id)}...[/yellow]")
|
|
79
|
+
console.print(f" Branch: {backup.get('base_branch', '?')}")
|
|
80
|
+
console.print(f" Files: {len(backup.get('files', []))}")
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
manifest = backup_manager.restore_backup(backup_id)
|
|
84
|
+
console.print("\n[green]✓ Backup restored successfully![/green]")
|
|
85
|
+
console.print("\n[dim]Run 'git status' to see restored files.[/dim]")
|
|
86
|
+
except Exception as e:
|
|
87
|
+
console.print(f"\n[red]Error restoring backup: {e}[/red]")
|
|
88
|
+
raise typer.Exit(1)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@app.command("show")
|
|
92
|
+
def show_cmd(
|
|
93
|
+
backup_id: str = typer.Argument("latest", help="Backup ID or 'latest'")
|
|
94
|
+
):
|
|
95
|
+
"""Show backup details."""
|
|
96
|
+
try:
|
|
97
|
+
gitops = GitOps()
|
|
98
|
+
except Exception:
|
|
99
|
+
gitops = None
|
|
100
|
+
|
|
101
|
+
backup_manager = BackupManager(gitops)
|
|
102
|
+
manifest = backup_manager.get_backup(backup_id)
|
|
103
|
+
|
|
104
|
+
if not manifest:
|
|
105
|
+
console.print(f"[red]Backup not found: {backup_id}[/red]")
|
|
106
|
+
raise typer.Exit(1)
|
|
107
|
+
|
|
108
|
+
status = manifest.get("status", "unknown")
|
|
109
|
+
status_color = {
|
|
110
|
+
"created": "yellow",
|
|
111
|
+
"completed": "green",
|
|
112
|
+
"failed": "red",
|
|
113
|
+
"restored": "blue"
|
|
114
|
+
}.get(status, "white")
|
|
115
|
+
|
|
116
|
+
console.print(f"\n[bold cyan]Backup: {manifest.get('id', '?')}[/bold cyan]\n")
|
|
117
|
+
console.print(f" Created: {manifest.get('created_at', '?')}")
|
|
118
|
+
console.print(f" Command: {manifest.get('command', '?')}")
|
|
119
|
+
console.print(f" Branch: {manifest.get('base_branch', '?')}")
|
|
120
|
+
console.print(f" Commit: {manifest.get('head_commit', '?')}")
|
|
121
|
+
console.print(f" Status: [{status_color}]{status}[/{status_color}]")
|
|
122
|
+
|
|
123
|
+
if manifest.get("error"):
|
|
124
|
+
console.print(f"\n [red]Error: {manifest['error']}[/red]")
|
|
125
|
+
|
|
126
|
+
files = manifest.get("files", [])
|
|
127
|
+
console.print(f"\n Files ({len(files)}):")
|
|
128
|
+
for f in files[:15]:
|
|
129
|
+
staged = "[S]" if f.get("staged") else " "
|
|
130
|
+
status_char = f.get("status", "M")
|
|
131
|
+
# Support both "file" and "path" keys
|
|
132
|
+
file_path = f.get("file") or f.get("path", "?")
|
|
133
|
+
console.print(f" {staged} {status_char} {file_path}")
|
|
134
|
+
|
|
135
|
+
if len(files) > 15:
|
|
136
|
+
console.print(f" ... and {len(files) - 15} more")
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@app.command("cleanup")
|
|
140
|
+
def cleanup_cmd(
|
|
141
|
+
keep: int = typer.Option(5, "--keep", "-k", help="Number of backups to keep")
|
|
142
|
+
):
|
|
143
|
+
"""Remove old backups, keep N most recent."""
|
|
144
|
+
try:
|
|
145
|
+
gitops = GitOps()
|
|
146
|
+
except Exception:
|
|
147
|
+
gitops = None
|
|
148
|
+
|
|
149
|
+
backup_manager = BackupManager(gitops)
|
|
150
|
+
|
|
151
|
+
before = len(backup_manager.list_backups())
|
|
152
|
+
backup_manager.cleanup_old_backups(keep=keep)
|
|
153
|
+
after = len(backup_manager.list_backups())
|
|
154
|
+
|
|
155
|
+
removed = before - after
|
|
156
|
+
if removed > 0:
|
|
157
|
+
console.print(f"[green]✓ Removed {removed} old backup(s). Keeping {after}.[/green]")
|
|
158
|
+
else:
|
|
159
|
+
console.print(f"[dim]No backups to remove. Total: {after}[/dim]")
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
# Default command (when just 'rg backup' is called)
|
|
163
|
+
@app.callback(invoke_without_command=True)
|
|
164
|
+
def main(ctx: typer.Context):
|
|
165
|
+
"""Manage working tree backups."""
|
|
166
|
+
if ctx.invoked_subcommand is None:
|
|
167
|
+
# Default to list
|
|
168
|
+
list_cmd()
|
|
@@ -15,8 +15,8 @@ from typing import Optional, List
|
|
|
15
15
|
from rich.console import Console
|
|
16
16
|
from rich.table import Table
|
|
17
17
|
|
|
18
|
-
from ..core.config import ConfigManager
|
|
19
|
-
from ..core.gitops import GitOps
|
|
18
|
+
from ..core.common.config import ConfigManager
|
|
19
|
+
from ..core.common.gitops import GitOps
|
|
20
20
|
from ..integrations.registry import get_cicd, get_integrations_by_type, IntegrationType
|
|
21
21
|
|
|
22
22
|
console = Console()
|
|
@@ -18,7 +18,7 @@ from rich.tree import Tree
|
|
|
18
18
|
import yaml
|
|
19
19
|
import os
|
|
20
20
|
|
|
21
|
-
from ..core.config import ConfigManager, CONFIG_PATH, DEFAULT_NOTIFICATIONS, DEFAULT_QUALITY
|
|
21
|
+
from ..core.common.config import ConfigManager, CONFIG_PATH, DEFAULT_NOTIFICATIONS, DEFAULT_QUALITY
|
|
22
22
|
|
|
23
23
|
console = Console()
|
|
24
24
|
config_app = typer.Typer(help="View and modify configuration")
|
|
@@ -300,7 +300,7 @@ def reset_cmd(
|
|
|
300
300
|
console.print(f"[green]Reset '{section}' to defaults[/green]")
|
|
301
301
|
|
|
302
302
|
elif section == "workflow":
|
|
303
|
-
from ..core.config import DEFAULT_WORKFLOW
|
|
303
|
+
from ..core.common.config import DEFAULT_WORKFLOW
|
|
304
304
|
if not force and not Confirm.ask(f"Reset '{section}' to defaults?", default=True):
|
|
305
305
|
return
|
|
306
306
|
|
|
@@ -580,8 +580,8 @@ def export_prompt_cmd(
|
|
|
580
580
|
rg config export-prompt quality # Export quality analysis prompt
|
|
581
581
|
"""
|
|
582
582
|
from pathlib import Path
|
|
583
|
-
from ..core.prompt import BUILTIN_PROMPTS_DIR
|
|
584
|
-
from ..core.config import RETGIT_DIR
|
|
583
|
+
from ..core.common.prompt import BUILTIN_PROMPTS_DIR
|
|
584
|
+
from ..core.common.config import RETGIT_DIR
|
|
585
585
|
from ..plugins.registry import get_plugin_by_name, get_builtin_plugins
|
|
586
586
|
from ..integrations.registry import get_all_integrations
|
|
587
587
|
|
|
@@ -750,8 +750,8 @@ def export_prompt_cmd(
|
|
|
750
750
|
def list_prompts_cmd():
|
|
751
751
|
"""List available prompt templates."""
|
|
752
752
|
from pathlib import Path
|
|
753
|
-
from ..core.prompt import BUILTIN_PROMPTS_DIR
|
|
754
|
-
from ..core.config import RETGIT_DIR
|
|
753
|
+
from ..core.common.prompt import BUILTIN_PROMPTS_DIR
|
|
754
|
+
from ..core.common.config import RETGIT_DIR
|
|
755
755
|
from ..plugins.registry import get_builtin_plugins
|
|
756
756
|
from ..integrations.registry import get_all_integrations
|
|
757
757
|
|
|
@@ -12,10 +12,10 @@ from rich.console import Console
|
|
|
12
12
|
from rich.panel import Panel
|
|
13
13
|
from rich.table import Table
|
|
14
14
|
|
|
15
|
-
from ..core.config import ConfigManager, RETGIT_DIR
|
|
16
|
-
from ..core.
|
|
17
|
-
from ..core.gitops import GitOps, NotAGitRepoError
|
|
18
|
-
from ..core.llm import LLMClient
|
|
15
|
+
from ..core.common.config import ConfigManager, RETGIT_DIR
|
|
16
|
+
from ..core.daily.state import DailyStateManager
|
|
17
|
+
from ..core.common.gitops import GitOps, NotAGitRepoError
|
|
18
|
+
from ..core.common.llm import LLMClient
|
|
19
19
|
|
|
20
20
|
console = Console()
|
|
21
21
|
|
|
@@ -3,8 +3,8 @@ import subprocess
|
|
|
3
3
|
import typer
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
|
|
6
|
-
from ..core.config import ConfigManager, RETGIT_DIR
|
|
7
|
-
from ..core.llm import load_providers, check_provider_available
|
|
6
|
+
from ..core.common.config import ConfigManager, RETGIT_DIR
|
|
7
|
+
from ..core.common.llm import load_providers, check_provider_available
|
|
8
8
|
from ..plugins.registry import detect_project_type, get_builtin_plugins
|
|
9
9
|
|
|
10
10
|
# Package source directories
|
|
@@ -2,7 +2,7 @@ import json
|
|
|
2
2
|
import typer
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
-
from ..core.config import ConfigManager
|
|
5
|
+
from ..core.common.config import ConfigManager
|
|
6
6
|
from ..integrations.registry import (
|
|
7
7
|
get_builtin_integrations,
|
|
8
8
|
get_all_integrations,
|
|
@@ -46,7 +46,7 @@ def _get_installed_integrations() -> set:
|
|
|
46
46
|
- Global directory: ~/.redgit/integrations/
|
|
47
47
|
- Project directory: .redgit/integrations/
|
|
48
48
|
"""
|
|
49
|
-
from ..core.config import GLOBAL_INTEGRATIONS_DIR
|
|
49
|
+
from ..core.common.config import GLOBAL_INTEGRATIONS_DIR
|
|
50
50
|
|
|
51
51
|
installed = set()
|
|
52
52
|
|
|
@@ -139,7 +139,7 @@ def list_cmd(
|
|
|
139
139
|
|
|
140
140
|
# Show available from taps
|
|
141
141
|
if all_integrations:
|
|
142
|
-
from ..core.tap import TapManager
|
|
142
|
+
from ..core.tap.manager import TapManager
|
|
143
143
|
|
|
144
144
|
tap_mgr = TapManager()
|
|
145
145
|
tap_integrations = tap_mgr.get_all_integrations(include_installed=True)
|
|
@@ -236,7 +236,7 @@ def configure_integration(name: str):
|
|
|
236
236
|
|
|
237
237
|
# Collect field values (overrides defaults)
|
|
238
238
|
for field in schema.get("fields", []):
|
|
239
|
-
value = _prompt_field(field)
|
|
239
|
+
value = _prompt_field(field, config_values)
|
|
240
240
|
if value is not None:
|
|
241
241
|
field_key = field.get("key") or field.get("name") or ""
|
|
242
242
|
config_values[field_key] = value
|
|
@@ -300,8 +300,11 @@ def _get_field_key(field: dict) -> str:
|
|
|
300
300
|
return field.get("key") or field.get("name") or ""
|
|
301
301
|
|
|
302
302
|
|
|
303
|
-
def _prompt_field(field: dict):
|
|
303
|
+
def _prompt_field(field: dict, config_values: dict = None):
|
|
304
304
|
"""Prompt user for a field value"""
|
|
305
|
+
if config_values is None:
|
|
306
|
+
config_values = {}
|
|
307
|
+
|
|
305
308
|
key = _get_field_key(field)
|
|
306
309
|
prompt_text = field.get("prompt") or field.get("label") or key
|
|
307
310
|
field_type = field.get("type", "text")
|
|
@@ -312,6 +315,29 @@ def _prompt_field(field: dict):
|
|
|
312
315
|
required = field.get("required", False)
|
|
313
316
|
help_text = field.get("help") or field.get("description")
|
|
314
317
|
env_var = field.get("env_var")
|
|
318
|
+
pre_prompt = field.get("pre_prompt", [])
|
|
319
|
+
dynamic_help_url = field.get("dynamic_help_url")
|
|
320
|
+
|
|
321
|
+
# Show pre_prompt instructions if available
|
|
322
|
+
if pre_prompt:
|
|
323
|
+
typer.echo("")
|
|
324
|
+
for line in pre_prompt:
|
|
325
|
+
# Replace placeholders with previously collected values
|
|
326
|
+
for k, v in config_values.items():
|
|
327
|
+
if v:
|
|
328
|
+
line = line.replace(f"{{{k}}}", str(v))
|
|
329
|
+
line = line.replace(f"BOT_TOKEN", str(v)) if k == "bot_token" else line
|
|
330
|
+
typer.echo(f" {line}")
|
|
331
|
+
typer.echo("")
|
|
332
|
+
|
|
333
|
+
# Show dynamic help URL with substituted values
|
|
334
|
+
if dynamic_help_url:
|
|
335
|
+
url = dynamic_help_url
|
|
336
|
+
for k, v in config_values.items():
|
|
337
|
+
if v:
|
|
338
|
+
url = url.replace(f"{{{k}}}", str(v))
|
|
339
|
+
typer.echo(f" 🔗 {url}")
|
|
340
|
+
typer.echo("")
|
|
315
341
|
|
|
316
342
|
# Show help text if available
|
|
317
343
|
if help_text:
|
|
@@ -330,7 +356,7 @@ def _prompt_field(field: dict):
|
|
|
330
356
|
value = typer.prompt(f" {prompt_text} (optional)", default="")
|
|
331
357
|
return value if value else None
|
|
332
358
|
|
|
333
|
-
elif field_type
|
|
359
|
+
elif field_type in ("secret", "password"):
|
|
334
360
|
if required:
|
|
335
361
|
value = typer.prompt(f" {prompt_text}", hide_input=True)
|
|
336
362
|
else:
|
|
@@ -338,14 +364,18 @@ def _prompt_field(field: dict):
|
|
|
338
364
|
hide_input=True, default="")
|
|
339
365
|
return value if value else None
|
|
340
366
|
|
|
341
|
-
elif field_type
|
|
342
|
-
choices = field.get("choices", [])
|
|
367
|
+
elif field_type in ("choice", "select"):
|
|
368
|
+
choices = field.get("choices", []) or field.get("options", [])
|
|
343
369
|
typer.echo(f" {prompt_text}")
|
|
344
370
|
for i, choice in enumerate(choices, 1):
|
|
345
371
|
marker = ">" if choice == default else " "
|
|
346
372
|
typer.echo(f" {marker} [{i}] {choice}")
|
|
347
373
|
|
|
348
|
-
|
|
374
|
+
default_idx = "1"
|
|
375
|
+
if default and default in choices:
|
|
376
|
+
default_idx = str(choices.index(default) + 1)
|
|
377
|
+
|
|
378
|
+
choice_idx = typer.prompt(f" Select", default=default_idx)
|
|
349
379
|
try:
|
|
350
380
|
idx = int(choice_idx) - 1
|
|
351
381
|
return choices[idx] if 0 <= idx < len(choices) else default
|
|
@@ -436,7 +466,7 @@ def update_cmd(
|
|
|
436
466
|
force: bool = typer.Option(False, "--force", "-f", help="Force reinstall even if up to date")
|
|
437
467
|
):
|
|
438
468
|
"""Update installed integrations from taps"""
|
|
439
|
-
from ..core.tap import TapManager, find_item_in_taps
|
|
469
|
+
from ..core.tap.manager import TapManager, find_item_in_taps
|
|
440
470
|
from .tap import install_from_tap
|
|
441
471
|
|
|
442
472
|
tap_mgr = TapManager()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import typer
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
|
|
4
|
-
from ..core.config import ConfigManager, GLOBAL_PLUGINS_DIR
|
|
4
|
+
from ..core.common.config import ConfigManager, GLOBAL_PLUGINS_DIR
|
|
5
5
|
from ..plugins.registry import get_all_plugins
|
|
6
6
|
|
|
7
7
|
plugin_app = typer.Typer(help="Plugin management")
|
|
@@ -52,7 +52,7 @@ def list_cmd(
|
|
|
52
52
|
|
|
53
53
|
# Show available from taps
|
|
54
54
|
if all_plugins:
|
|
55
|
-
from ..core.tap import TapManager
|
|
55
|
+
from ..core.tap.manager import TapManager
|
|
56
56
|
|
|
57
57
|
tap_mgr = TapManager()
|
|
58
58
|
tap_plugins = tap_mgr.get_all_plugins(include_installed=True)
|