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.
Files changed (84) hide show
  1. aiconfigkit/__init__.py +0 -0
  2. aiconfigkit/__main__.py +6 -0
  3. aiconfigkit/ai_tools/__init__.py +0 -0
  4. aiconfigkit/ai_tools/base.py +236 -0
  5. aiconfigkit/ai_tools/capability_registry.py +262 -0
  6. aiconfigkit/ai_tools/claude.py +91 -0
  7. aiconfigkit/ai_tools/claude_desktop.py +97 -0
  8. aiconfigkit/ai_tools/cline.py +92 -0
  9. aiconfigkit/ai_tools/copilot.py +92 -0
  10. aiconfigkit/ai_tools/cursor.py +109 -0
  11. aiconfigkit/ai_tools/detector.py +169 -0
  12. aiconfigkit/ai_tools/kiro.py +85 -0
  13. aiconfigkit/ai_tools/mcp_syncer.py +291 -0
  14. aiconfigkit/ai_tools/roo.py +110 -0
  15. aiconfigkit/ai_tools/translator.py +390 -0
  16. aiconfigkit/ai_tools/winsurf.py +102 -0
  17. aiconfigkit/cli/__init__.py +0 -0
  18. aiconfigkit/cli/delete.py +118 -0
  19. aiconfigkit/cli/download.py +274 -0
  20. aiconfigkit/cli/install.py +237 -0
  21. aiconfigkit/cli/install_new.py +937 -0
  22. aiconfigkit/cli/list.py +275 -0
  23. aiconfigkit/cli/main.py +454 -0
  24. aiconfigkit/cli/mcp_configure.py +232 -0
  25. aiconfigkit/cli/mcp_install.py +166 -0
  26. aiconfigkit/cli/mcp_sync.py +165 -0
  27. aiconfigkit/cli/package.py +383 -0
  28. aiconfigkit/cli/package_create.py +323 -0
  29. aiconfigkit/cli/package_install.py +472 -0
  30. aiconfigkit/cli/template.py +19 -0
  31. aiconfigkit/cli/template_backup.py +261 -0
  32. aiconfigkit/cli/template_init.py +499 -0
  33. aiconfigkit/cli/template_install.py +261 -0
  34. aiconfigkit/cli/template_list.py +172 -0
  35. aiconfigkit/cli/template_uninstall.py +146 -0
  36. aiconfigkit/cli/template_update.py +225 -0
  37. aiconfigkit/cli/template_validate.py +234 -0
  38. aiconfigkit/cli/tools.py +47 -0
  39. aiconfigkit/cli/uninstall.py +125 -0
  40. aiconfigkit/cli/update.py +309 -0
  41. aiconfigkit/core/__init__.py +0 -0
  42. aiconfigkit/core/checksum.py +211 -0
  43. aiconfigkit/core/component_detector.py +905 -0
  44. aiconfigkit/core/conflict_resolution.py +329 -0
  45. aiconfigkit/core/git_operations.py +539 -0
  46. aiconfigkit/core/mcp/__init__.py +1 -0
  47. aiconfigkit/core/mcp/credentials.py +279 -0
  48. aiconfigkit/core/mcp/manager.py +308 -0
  49. aiconfigkit/core/mcp/set_manager.py +1 -0
  50. aiconfigkit/core/mcp/validator.py +1 -0
  51. aiconfigkit/core/models.py +1661 -0
  52. aiconfigkit/core/package_creator.py +743 -0
  53. aiconfigkit/core/package_manifest.py +248 -0
  54. aiconfigkit/core/repository.py +298 -0
  55. aiconfigkit/core/secret_detector.py +438 -0
  56. aiconfigkit/core/template_manifest.py +283 -0
  57. aiconfigkit/core/version.py +201 -0
  58. aiconfigkit/storage/__init__.py +0 -0
  59. aiconfigkit/storage/library.py +429 -0
  60. aiconfigkit/storage/mcp_tracker.py +1 -0
  61. aiconfigkit/storage/package_tracker.py +234 -0
  62. aiconfigkit/storage/template_library.py +229 -0
  63. aiconfigkit/storage/template_tracker.py +296 -0
  64. aiconfigkit/storage/tracker.py +416 -0
  65. aiconfigkit/tui/__init__.py +5 -0
  66. aiconfigkit/tui/installer.py +511 -0
  67. aiconfigkit/utils/__init__.py +0 -0
  68. aiconfigkit/utils/atomic_write.py +90 -0
  69. aiconfigkit/utils/backup.py +169 -0
  70. aiconfigkit/utils/dotenv.py +128 -0
  71. aiconfigkit/utils/git_helpers.py +187 -0
  72. aiconfigkit/utils/logging.py +60 -0
  73. aiconfigkit/utils/namespace.py +134 -0
  74. aiconfigkit/utils/paths.py +205 -0
  75. aiconfigkit/utils/project.py +109 -0
  76. aiconfigkit/utils/streaming.py +216 -0
  77. aiconfigkit/utils/ui.py +194 -0
  78. aiconfigkit/utils/validation.py +187 -0
  79. devsync-0.5.5.dist-info/LICENSE +21 -0
  80. devsync-0.5.5.dist-info/METADATA +477 -0
  81. devsync-0.5.5.dist-info/RECORD +84 -0
  82. devsync-0.5.5.dist-info/WHEEL +5 -0
  83. devsync-0.5.5.dist-info/entry_points.txt +2 -0
  84. 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)