devsync 0.5.5__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.
- aiconfigkit/__init__.py +0 -0
- aiconfigkit/__main__.py +6 -0
- aiconfigkit/ai_tools/__init__.py +0 -0
- aiconfigkit/ai_tools/base.py +236 -0
- aiconfigkit/ai_tools/capability_registry.py +262 -0
- aiconfigkit/ai_tools/claude.py +91 -0
- aiconfigkit/ai_tools/claude_desktop.py +97 -0
- aiconfigkit/ai_tools/cline.py +92 -0
- aiconfigkit/ai_tools/copilot.py +92 -0
- aiconfigkit/ai_tools/cursor.py +109 -0
- aiconfigkit/ai_tools/detector.py +169 -0
- aiconfigkit/ai_tools/kiro.py +85 -0
- aiconfigkit/ai_tools/mcp_syncer.py +291 -0
- aiconfigkit/ai_tools/roo.py +110 -0
- aiconfigkit/ai_tools/translator.py +390 -0
- aiconfigkit/ai_tools/winsurf.py +102 -0
- aiconfigkit/cli/__init__.py +0 -0
- aiconfigkit/cli/delete.py +118 -0
- aiconfigkit/cli/download.py +274 -0
- aiconfigkit/cli/install.py +237 -0
- aiconfigkit/cli/install_new.py +937 -0
- aiconfigkit/cli/list.py +275 -0
- aiconfigkit/cli/main.py +454 -0
- aiconfigkit/cli/mcp_configure.py +232 -0
- aiconfigkit/cli/mcp_install.py +166 -0
- aiconfigkit/cli/mcp_sync.py +165 -0
- aiconfigkit/cli/package.py +383 -0
- aiconfigkit/cli/package_create.py +323 -0
- aiconfigkit/cli/package_install.py +472 -0
- aiconfigkit/cli/template.py +19 -0
- aiconfigkit/cli/template_backup.py +261 -0
- aiconfigkit/cli/template_init.py +499 -0
- aiconfigkit/cli/template_install.py +261 -0
- aiconfigkit/cli/template_list.py +172 -0
- aiconfigkit/cli/template_uninstall.py +146 -0
- aiconfigkit/cli/template_update.py +225 -0
- aiconfigkit/cli/template_validate.py +234 -0
- aiconfigkit/cli/tools.py +47 -0
- aiconfigkit/cli/uninstall.py +125 -0
- aiconfigkit/cli/update.py +309 -0
- aiconfigkit/core/__init__.py +0 -0
- aiconfigkit/core/checksum.py +211 -0
- aiconfigkit/core/component_detector.py +905 -0
- aiconfigkit/core/conflict_resolution.py +329 -0
- aiconfigkit/core/git_operations.py +539 -0
- aiconfigkit/core/mcp/__init__.py +1 -0
- aiconfigkit/core/mcp/credentials.py +279 -0
- aiconfigkit/core/mcp/manager.py +308 -0
- aiconfigkit/core/mcp/set_manager.py +1 -0
- aiconfigkit/core/mcp/validator.py +1 -0
- aiconfigkit/core/models.py +1661 -0
- aiconfigkit/core/package_creator.py +743 -0
- aiconfigkit/core/package_manifest.py +248 -0
- aiconfigkit/core/repository.py +298 -0
- aiconfigkit/core/secret_detector.py +438 -0
- aiconfigkit/core/template_manifest.py +283 -0
- aiconfigkit/core/version.py +201 -0
- aiconfigkit/storage/__init__.py +0 -0
- aiconfigkit/storage/library.py +429 -0
- aiconfigkit/storage/mcp_tracker.py +1 -0
- aiconfigkit/storage/package_tracker.py +234 -0
- aiconfigkit/storage/template_library.py +229 -0
- aiconfigkit/storage/template_tracker.py +296 -0
- aiconfigkit/storage/tracker.py +416 -0
- aiconfigkit/tui/__init__.py +5 -0
- aiconfigkit/tui/installer.py +511 -0
- aiconfigkit/utils/__init__.py +0 -0
- aiconfigkit/utils/atomic_write.py +90 -0
- aiconfigkit/utils/backup.py +169 -0
- aiconfigkit/utils/dotenv.py +128 -0
- aiconfigkit/utils/git_helpers.py +187 -0
- aiconfigkit/utils/logging.py +60 -0
- aiconfigkit/utils/namespace.py +134 -0
- aiconfigkit/utils/paths.py +205 -0
- aiconfigkit/utils/project.py +109 -0
- aiconfigkit/utils/streaming.py +216 -0
- aiconfigkit/utils/ui.py +194 -0
- aiconfigkit/utils/validation.py +187 -0
- devsync-0.5.5.dist-info/LICENSE +21 -0
- devsync-0.5.5.dist-info/METADATA +477 -0
- devsync-0.5.5.dist-info/RECORD +84 -0
- devsync-0.5.5.dist-info/WHEEL +5 -0
- devsync-0.5.5.dist-info/entry_points.txt +2 -0
- devsync-0.5.5.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
"""Template backup management commands."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
from rich.table import Table
|
|
8
|
+
|
|
9
|
+
from aiconfigkit.utils.backup import cleanup_old_backups, list_backups, restore_backup
|
|
10
|
+
from aiconfigkit.utils.project import find_project_root
|
|
11
|
+
|
|
12
|
+
console = Console()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def backup_list_command(
|
|
16
|
+
scope: str = typer.Option(
|
|
17
|
+
"project",
|
|
18
|
+
"--scope",
|
|
19
|
+
"-s",
|
|
20
|
+
help="Which backups to list (project, global)",
|
|
21
|
+
),
|
|
22
|
+
limit: int = typer.Option(
|
|
23
|
+
10,
|
|
24
|
+
"--limit",
|
|
25
|
+
"-n",
|
|
26
|
+
help="Maximum number of backups to show",
|
|
27
|
+
),
|
|
28
|
+
) -> None:
|
|
29
|
+
"""
|
|
30
|
+
List available template backups.
|
|
31
|
+
|
|
32
|
+
Backups are created automatically before overwriting templates
|
|
33
|
+
during update operations.
|
|
34
|
+
|
|
35
|
+
Example:
|
|
36
|
+
inskit template backup list
|
|
37
|
+
inskit template backup list --scope global
|
|
38
|
+
inskit template backup list --limit 20
|
|
39
|
+
"""
|
|
40
|
+
try:
|
|
41
|
+
# Determine backup directory
|
|
42
|
+
if scope == "project":
|
|
43
|
+
project_root = find_project_root()
|
|
44
|
+
if not project_root:
|
|
45
|
+
console.print("[red]Error: Not in a project directory[/red]")
|
|
46
|
+
raise typer.Exit(1)
|
|
47
|
+
backup_dir = project_root / ".instructionkit" / "backups"
|
|
48
|
+
elif scope == "global":
|
|
49
|
+
backup_dir = Path.home() / ".instructionkit" / "backups"
|
|
50
|
+
else:
|
|
51
|
+
console.print(f"[red]Error: Invalid scope '{scope}'. Must be 'project' or 'global'[/red]")
|
|
52
|
+
raise typer.Exit(1)
|
|
53
|
+
|
|
54
|
+
# Get backups
|
|
55
|
+
backups = list_backups(backup_dir)
|
|
56
|
+
|
|
57
|
+
if not backups:
|
|
58
|
+
console.print(f"[yellow]No backups found in {scope} scope[/yellow]")
|
|
59
|
+
console.print(f"[dim]Backup directory: {backup_dir}[/dim]")
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
# Limit results
|
|
63
|
+
backups = backups[:limit]
|
|
64
|
+
|
|
65
|
+
# Display table
|
|
66
|
+
table = Table(title=f"\n{scope.capitalize()} Template Backups")
|
|
67
|
+
table.add_column("Timestamp", style="cyan")
|
|
68
|
+
table.add_column("Backup Directory", style="green")
|
|
69
|
+
table.add_column("Files", justify="right")
|
|
70
|
+
|
|
71
|
+
for timestamp, backup_path in backups:
|
|
72
|
+
file_count = len(list(backup_path.iterdir()))
|
|
73
|
+
timestamp_str = timestamp.strftime("%Y-%m-%d %H:%M:%S")
|
|
74
|
+
table.add_row(timestamp_str, str(backup_path.relative_to(backup_dir.parent)), str(file_count))
|
|
75
|
+
|
|
76
|
+
console.print(table)
|
|
77
|
+
console.print(f"\n[dim]Showing {len(backups)} most recent backup(s)[/dim]")
|
|
78
|
+
|
|
79
|
+
if len(backups) >= limit:
|
|
80
|
+
console.print("[dim]Use --limit to see more backups[/dim]")
|
|
81
|
+
|
|
82
|
+
except typer.Exit:
|
|
83
|
+
raise
|
|
84
|
+
except Exception as e:
|
|
85
|
+
console.print(f"\n[red]Error: {e}[/red]")
|
|
86
|
+
raise typer.Exit(1)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def backup_cleanup_command(
|
|
90
|
+
days: int = typer.Option(
|
|
91
|
+
30,
|
|
92
|
+
"--days",
|
|
93
|
+
"-d",
|
|
94
|
+
help="Remove backups older than this many days",
|
|
95
|
+
),
|
|
96
|
+
scope: str = typer.Option(
|
|
97
|
+
"project",
|
|
98
|
+
"--scope",
|
|
99
|
+
"-s",
|
|
100
|
+
help="Which backups to clean up (project, global)",
|
|
101
|
+
),
|
|
102
|
+
force: bool = typer.Option(
|
|
103
|
+
False,
|
|
104
|
+
"--force",
|
|
105
|
+
"-f",
|
|
106
|
+
help="Skip confirmation prompt",
|
|
107
|
+
),
|
|
108
|
+
) -> None:
|
|
109
|
+
"""
|
|
110
|
+
Remove old template backups to free up space.
|
|
111
|
+
|
|
112
|
+
Example:
|
|
113
|
+
inskit template backup cleanup --days 30
|
|
114
|
+
inskit template backup cleanup --days 7 --force
|
|
115
|
+
inskit template backup cleanup --scope global --days 90
|
|
116
|
+
"""
|
|
117
|
+
try:
|
|
118
|
+
# Determine backup directory
|
|
119
|
+
if scope == "project":
|
|
120
|
+
project_root = find_project_root()
|
|
121
|
+
if not project_root:
|
|
122
|
+
console.print("[red]Error: Not in a project directory[/red]")
|
|
123
|
+
raise typer.Exit(1)
|
|
124
|
+
backup_dir = project_root / ".instructionkit" / "backups"
|
|
125
|
+
elif scope == "global":
|
|
126
|
+
backup_dir = Path.home() / ".instructionkit" / "backups"
|
|
127
|
+
else:
|
|
128
|
+
console.print(f"[red]Error: Invalid scope '{scope}'. Must be 'project' or 'global'[/red]")
|
|
129
|
+
raise typer.Exit(1)
|
|
130
|
+
|
|
131
|
+
# Get backups that will be removed
|
|
132
|
+
backups = list_backups(backup_dir)
|
|
133
|
+
from datetime import datetime
|
|
134
|
+
|
|
135
|
+
cutoff_date = datetime.now().timestamp() - (days * 24 * 60 * 60)
|
|
136
|
+
old_backups = [b for timestamp, b in backups if timestamp.timestamp() < cutoff_date]
|
|
137
|
+
|
|
138
|
+
if not old_backups:
|
|
139
|
+
console.print(f"[green]No backups older than {days} days found[/green]")
|
|
140
|
+
return
|
|
141
|
+
|
|
142
|
+
# Confirm deletion
|
|
143
|
+
if not force:
|
|
144
|
+
console.print(f"[yellow]Found {len(old_backups)} backup(s) older than {days} days:[/yellow]")
|
|
145
|
+
for backup_path in old_backups[:5]: # Show first 5
|
|
146
|
+
console.print(f" - {backup_path.name}")
|
|
147
|
+
if len(old_backups) > 5:
|
|
148
|
+
console.print(f" ... and {len(old_backups) - 5} more")
|
|
149
|
+
|
|
150
|
+
confirm = typer.confirm(f"\nRemove {len(old_backups)} old backup(s)?")
|
|
151
|
+
if not confirm:
|
|
152
|
+
console.print("[yellow]Cancelled[/yellow]")
|
|
153
|
+
raise typer.Exit(0)
|
|
154
|
+
|
|
155
|
+
# Clean up
|
|
156
|
+
removed = cleanup_old_backups(days, backup_dir)
|
|
157
|
+
console.print(f"[green]✓ Removed {removed} old backup(s)[/green]")
|
|
158
|
+
|
|
159
|
+
except typer.Exit:
|
|
160
|
+
raise
|
|
161
|
+
except Exception as e:
|
|
162
|
+
console.print(f"\n[red]Error: {e}[/red]")
|
|
163
|
+
raise typer.Exit(1)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def backup_restore_command(
|
|
167
|
+
backup_timestamp: str = typer.Argument(..., help="Timestamp of backup to restore (YYYYMMDD_HHMMSS)"),
|
|
168
|
+
file_name: str = typer.Argument(..., help="Name of file to restore"),
|
|
169
|
+
target: str = typer.Option(
|
|
170
|
+
None,
|
|
171
|
+
"--target",
|
|
172
|
+
"-t",
|
|
173
|
+
help="Target path (default: original location)",
|
|
174
|
+
),
|
|
175
|
+
scope: str = typer.Option(
|
|
176
|
+
"project",
|
|
177
|
+
"--scope",
|
|
178
|
+
"-s",
|
|
179
|
+
help="Where backup is located (project, global)",
|
|
180
|
+
),
|
|
181
|
+
) -> None:
|
|
182
|
+
"""
|
|
183
|
+
Restore a file from a backup.
|
|
184
|
+
|
|
185
|
+
Example:
|
|
186
|
+
# List backups first
|
|
187
|
+
inskit template backup list
|
|
188
|
+
|
|
189
|
+
# Restore specific file
|
|
190
|
+
inskit template backup restore 20251109_143052 company.test.md
|
|
191
|
+
|
|
192
|
+
# Restore to different location
|
|
193
|
+
inskit template backup restore 20251109_143052 company.test.md --target .claude/rules/company.test-restored.md
|
|
194
|
+
"""
|
|
195
|
+
try:
|
|
196
|
+
# Determine backup directory
|
|
197
|
+
if scope == "project":
|
|
198
|
+
project_root = find_project_root()
|
|
199
|
+
if not project_root:
|
|
200
|
+
console.print("[red]Error: Not in a project directory[/red]")
|
|
201
|
+
raise typer.Exit(1)
|
|
202
|
+
backup_dir = project_root / ".instructionkit" / "backups"
|
|
203
|
+
elif scope == "global":
|
|
204
|
+
backup_dir = Path.home() / ".instructionkit" / "backups"
|
|
205
|
+
else:
|
|
206
|
+
console.print(f"[red]Error: Invalid scope '{scope}'. Must be 'project' or 'global'[/red]")
|
|
207
|
+
raise typer.Exit(1)
|
|
208
|
+
|
|
209
|
+
# Find backup
|
|
210
|
+
backup_path = backup_dir / backup_timestamp / file_name
|
|
211
|
+
if not backup_path.exists():
|
|
212
|
+
console.print(f"[red]Error: Backup not found: {backup_path}[/red]")
|
|
213
|
+
console.print("\n[yellow]Available backups:[/yellow]")
|
|
214
|
+
# Show available backups for this timestamp
|
|
215
|
+
timestamp_dir = backup_dir / backup_timestamp
|
|
216
|
+
if timestamp_dir.exists():
|
|
217
|
+
for f in timestamp_dir.iterdir():
|
|
218
|
+
console.print(f" - {f.name}")
|
|
219
|
+
else:
|
|
220
|
+
console.print(f"[red]Backup directory not found: {timestamp_dir}[/red]")
|
|
221
|
+
console.print("\nUse 'inskit template backup list' to see available backups")
|
|
222
|
+
raise typer.Exit(1)
|
|
223
|
+
|
|
224
|
+
# Determine target path
|
|
225
|
+
if target:
|
|
226
|
+
target_path = Path(target)
|
|
227
|
+
else:
|
|
228
|
+
# Try to restore to original location based on file name
|
|
229
|
+
# Assume structure like .claude/rules/company.test.md
|
|
230
|
+
parts = file_name.split(".")
|
|
231
|
+
if len(parts) >= 3:
|
|
232
|
+
# namespace.template-name.ext format
|
|
233
|
+
# Default to .claude/rules as restore location
|
|
234
|
+
target_path = Path(f".claude/rules/{file_name}")
|
|
235
|
+
else:
|
|
236
|
+
console.print("[yellow]Could not determine original location[/yellow]")
|
|
237
|
+
console.print("Please specify --target path")
|
|
238
|
+
raise typer.Exit(1)
|
|
239
|
+
|
|
240
|
+
# Confirm restore
|
|
241
|
+
console.print("[yellow]Restore file:[/yellow]")
|
|
242
|
+
console.print(f" From: {backup_path}")
|
|
243
|
+
console.print(f" To: {target_path}")
|
|
244
|
+
|
|
245
|
+
if target_path.exists():
|
|
246
|
+
console.print("\n[red]⚠️ Target file exists and will be overwritten[/red]")
|
|
247
|
+
|
|
248
|
+
confirm = typer.confirm("\nProceed with restore?")
|
|
249
|
+
if not confirm:
|
|
250
|
+
console.print("[yellow]Cancelled[/yellow]")
|
|
251
|
+
raise typer.Exit(0)
|
|
252
|
+
|
|
253
|
+
# Restore
|
|
254
|
+
restore_backup(backup_path, target_path)
|
|
255
|
+
console.print(f"[green]✓ File restored to {target_path}[/green]")
|
|
256
|
+
|
|
257
|
+
except typer.Exit:
|
|
258
|
+
raise
|
|
259
|
+
except Exception as e:
|
|
260
|
+
console.print(f"\n[red]Error: {e}[/red]")
|
|
261
|
+
raise typer.Exit(1)
|