cl-preset 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.
- cl_preset/__init__.py +19 -0
- cl_preset/cli.py +479 -0
- cl_preset/git_strategy.py +478 -0
- cl_preset/manager.py +199 -0
- cl_preset/models.py +60 -0
- cl_preset/py.typed +0 -0
- cl_preset/templates/git_strategy_template.yaml +60 -0
- cl_preset-0.1.0.dist-info/METADATA +394 -0
- cl_preset-0.1.0.dist-info/RECORD +12 -0
- cl_preset-0.1.0.dist-info/WHEEL +4 -0
- cl_preset-0.1.0.dist-info/entry_points.txt +2 -0
- cl_preset-0.1.0.dist-info/licenses/LICENSE +21 -0
cl_preset/__init__.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""
|
|
2
|
+
cl-preset: A package for managing Claude Code configuration presets and strategies.
|
|
3
|
+
|
|
4
|
+
This package allows users to package their Claude Code strategies and install them
|
|
5
|
+
with different scopes (global, project, etc.).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
__version__ = "0.1.0"
|
|
9
|
+
|
|
10
|
+
from cl_preset.manager import PresetManager
|
|
11
|
+
from cl_preset.models import Preset, PresetConfig, PresetScope
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"__version__",
|
|
15
|
+
"Preset",
|
|
16
|
+
"PresetScope",
|
|
17
|
+
"PresetConfig",
|
|
18
|
+
"PresetManager",
|
|
19
|
+
]
|
cl_preset/cli.py
ADDED
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI interface for cl-preset package.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
from rich.panel import Panel
|
|
11
|
+
|
|
12
|
+
from cl_preset.git_strategy import GitStrategyManager
|
|
13
|
+
from cl_preset.manager import PresetManager
|
|
14
|
+
from cl_preset.models import PresetMetadata, PresetScope, PresetType
|
|
15
|
+
|
|
16
|
+
console = Console()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@click.group()
|
|
20
|
+
@click.version_option(version="0.1.0", prog_name="cl-preset")
|
|
21
|
+
def main() -> None:
|
|
22
|
+
"""cl-preset: Manage Claude Code configuration presets and strategies."""
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@main.command()
|
|
27
|
+
@click.argument("source_path", type=click.Path(exists=True, path_type=Path))
|
|
28
|
+
@click.option("--name", "-n", required=True, help="Unique name for the preset")
|
|
29
|
+
@click.option("--version", "-v", default="1.0.0", help="Version (default: 1.0.0)")
|
|
30
|
+
@click.option("--description", "-d", required=True, help="Description of the preset")
|
|
31
|
+
@click.option("--author", "-a", default="", help="Author name")
|
|
32
|
+
@click.option("--type", "-t",
|
|
33
|
+
type=click.Choice(["agent", "skill", "command", "strategy"]),
|
|
34
|
+
default="strategy",
|
|
35
|
+
help="Type of preset (default: strategy)")
|
|
36
|
+
@click.option("--scope", "-s",
|
|
37
|
+
type=click.Choice(["global", "user", "project"]),
|
|
38
|
+
default="user",
|
|
39
|
+
help="Installation scope (default: user)")
|
|
40
|
+
def install(
|
|
41
|
+
source_path: Path,
|
|
42
|
+
name: str,
|
|
43
|
+
version: str,
|
|
44
|
+
description: str,
|
|
45
|
+
author: str,
|
|
46
|
+
type: str,
|
|
47
|
+
scope: str
|
|
48
|
+
) -> None:
|
|
49
|
+
"""Install a preset from a source directory.
|
|
50
|
+
|
|
51
|
+
SOURCE_PATH is the path to the directory containing the preset files.
|
|
52
|
+
|
|
53
|
+
Example:
|
|
54
|
+
cl-preset install ./my-strategy --name my-strategy --description "My custom strategy"
|
|
55
|
+
"""
|
|
56
|
+
manager = PresetManager()
|
|
57
|
+
|
|
58
|
+
metadata = PresetMetadata(
|
|
59
|
+
name=name,
|
|
60
|
+
version=version,
|
|
61
|
+
description=description,
|
|
62
|
+
author=author,
|
|
63
|
+
preset_type=PresetType(type)
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
preset = manager.create_from_directory(
|
|
67
|
+
source_path=source_path,
|
|
68
|
+
metadata=metadata,
|
|
69
|
+
scope=PresetScope(scope)
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
manager.install(preset)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@main.command()
|
|
76
|
+
@click.argument("name")
|
|
77
|
+
@click.option("--scope", "-s",
|
|
78
|
+
type=click.Choice(["global", "user", "project"]),
|
|
79
|
+
help="Filter by scope")
|
|
80
|
+
def uninstall(name: str, scope: str | None) -> None:
|
|
81
|
+
"""Uninstall a preset by name.
|
|
82
|
+
|
|
83
|
+
NAME is the name of the preset to uninstall.
|
|
84
|
+
|
|
85
|
+
Example:
|
|
86
|
+
cl-preset uninstall my-strategy
|
|
87
|
+
"""
|
|
88
|
+
manager = PresetManager()
|
|
89
|
+
scope_enum = PresetScope(scope) if scope else None
|
|
90
|
+
manager.uninstall(name, scope_enum)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@main.command()
|
|
94
|
+
@click.option("--scope", "-s",
|
|
95
|
+
type=click.Choice(["global", "user", "project"]),
|
|
96
|
+
help="Filter by scope")
|
|
97
|
+
@click.option("--json", "json_output", is_flag=True, help="Output as JSON")
|
|
98
|
+
def list_cmd(scope: str | None, json_output: bool) -> None:
|
|
99
|
+
"""List installed presets.
|
|
100
|
+
|
|
101
|
+
Example:
|
|
102
|
+
cl-preset list
|
|
103
|
+
cl-preset list --scope user
|
|
104
|
+
"""
|
|
105
|
+
manager = PresetManager()
|
|
106
|
+
scope_enum = PresetScope(scope) if scope else None
|
|
107
|
+
|
|
108
|
+
if json_output:
|
|
109
|
+
presets = manager.list(scope_enum)
|
|
110
|
+
console.print_json(json.dumps(presets, indent=2))
|
|
111
|
+
else:
|
|
112
|
+
manager.print_list(scope_enum)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@main.command()
|
|
116
|
+
@click.argument("name")
|
|
117
|
+
@click.option("--output", "-o", type=click.Path(path_type=Path),
|
|
118
|
+
default=None,
|
|
119
|
+
help="Output path for preset metadata")
|
|
120
|
+
def init(name: str, output: Path | None) -> None:
|
|
121
|
+
"""Initialize a new preset scaffold.
|
|
122
|
+
|
|
123
|
+
Creates a template directory structure for a new preset.
|
|
124
|
+
|
|
125
|
+
NAME is the name of the preset to create.
|
|
126
|
+
|
|
127
|
+
Example:
|
|
128
|
+
cl-preset init my-strategy
|
|
129
|
+
"""
|
|
130
|
+
if output is None:
|
|
131
|
+
output = Path.cwd() / "preset.json"
|
|
132
|
+
|
|
133
|
+
preset_dir = Path.cwd() / name
|
|
134
|
+
preset_dir.mkdir(exist_ok=True)
|
|
135
|
+
|
|
136
|
+
# Create directory structure
|
|
137
|
+
(preset_dir / "agents").mkdir(exist_ok=True)
|
|
138
|
+
(preset_dir / "skills").mkdir(exist_ok=True)
|
|
139
|
+
(preset_dir / "commands").mkdir(exist_ok=True)
|
|
140
|
+
(preset_dir / "examples").mkdir(exist_ok=True)
|
|
141
|
+
|
|
142
|
+
# Create README template
|
|
143
|
+
readme_content = f"""# {name}
|
|
144
|
+
|
|
145
|
+
## Description
|
|
146
|
+
|
|
147
|
+
Add your description here.
|
|
148
|
+
|
|
149
|
+
## Installation
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
cl-preset install . --name {name} --description "Your description"
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Structure
|
|
156
|
+
|
|
157
|
+
- `agents/` - Agent definitions
|
|
158
|
+
- `skills/` - Skill definitions
|
|
159
|
+
- `commands/` - Command definitions
|
|
160
|
+
- `examples/` - Usage examples
|
|
161
|
+
|
|
162
|
+
## Usage
|
|
163
|
+
|
|
164
|
+
After installation, the preset will be available in your Claude Code environment.
|
|
165
|
+
|
|
166
|
+
## Author
|
|
167
|
+
|
|
168
|
+
Add your name here
|
|
169
|
+
"""
|
|
170
|
+
(preset_dir / "README.md").write_text(readme_content)
|
|
171
|
+
|
|
172
|
+
# Create preset.json metadata template
|
|
173
|
+
metadata = {
|
|
174
|
+
"name": name,
|
|
175
|
+
"version": "1.0.0",
|
|
176
|
+
"description": "Add your description here",
|
|
177
|
+
"author": "",
|
|
178
|
+
"license": "MIT",
|
|
179
|
+
"preset_type": "strategy",
|
|
180
|
+
"tags": []
|
|
181
|
+
}
|
|
182
|
+
output.write_text(json.dumps(metadata, indent=2))
|
|
183
|
+
|
|
184
|
+
console.print(f"[green]Created preset scaffold at {preset_dir}[/green]")
|
|
185
|
+
console.print(f"[cyan]Metadata written to {output}[/cyan]")
|
|
186
|
+
console.print("\nEdit the metadata and preset files, then install with:")
|
|
187
|
+
console.print(f" cl-preset install {preset_dir} --name {name} --description 'Your description'")
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
@main.command()
|
|
191
|
+
@click.argument("name")
|
|
192
|
+
def info(name: str) -> None:
|
|
193
|
+
"""Show detailed information about an installed preset.
|
|
194
|
+
|
|
195
|
+
NAME is the name of the preset.
|
|
196
|
+
|
|
197
|
+
Example:
|
|
198
|
+
cl-preset info my-strategy
|
|
199
|
+
"""
|
|
200
|
+
manager = PresetManager()
|
|
201
|
+
registry = manager._load_registry()
|
|
202
|
+
|
|
203
|
+
installed = registry.get("installed", [])
|
|
204
|
+
preset_info = None
|
|
205
|
+
|
|
206
|
+
for preset in installed:
|
|
207
|
+
if preset["name"] == name:
|
|
208
|
+
preset_info = preset
|
|
209
|
+
break
|
|
210
|
+
|
|
211
|
+
if preset_info is None:
|
|
212
|
+
console.print(f"[red]Preset '{name}' not found.[/red]")
|
|
213
|
+
return
|
|
214
|
+
|
|
215
|
+
# Try to load detailed metadata
|
|
216
|
+
preset_path = Path(preset_info["path"])
|
|
217
|
+
metadata_path = preset_path / "preset.json"
|
|
218
|
+
|
|
219
|
+
if metadata_path.exists():
|
|
220
|
+
metadata = json.loads(metadata_path.read_text())
|
|
221
|
+
console.print(f"[cyan]Name:[/cyan] {metadata.get('name', 'N/A')}")
|
|
222
|
+
console.print(f"[cyan]Version:[/cyan] {metadata.get('version', 'N/A')}")
|
|
223
|
+
console.print(f"[cyan]Description:[/cyan] {metadata.get('description', 'N/A')}")
|
|
224
|
+
console.print(f"[cyan]Author:[/cyan] {metadata.get('author', 'N/A')}")
|
|
225
|
+
console.print(f"[cyan]License:[/cyan] {metadata.get('license', 'N/A')}")
|
|
226
|
+
console.print(f"[cyan]Type:[/cyan] {metadata.get('preset_type', 'N/A')}")
|
|
227
|
+
console.print(f"[cyan]Tags:[/cyan] {', '.join(metadata.get('tags', [])) or 'None'}")
|
|
228
|
+
else:
|
|
229
|
+
console.print(f"[cyan]Name:[/cyan] {preset_info['name']}")
|
|
230
|
+
console.print(f"[cyan]Version:[/cyan] {preset_info['version']}")
|
|
231
|
+
console.print(f"[cyan]Scope:[/cyan] {preset_info['scope']}")
|
|
232
|
+
console.print(f"[cyan]Path:[/cyan] {preset_info['path']}")
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
# ============================================================================
|
|
236
|
+
# Git Strategy Commands
|
|
237
|
+
# ============================================================================
|
|
238
|
+
|
|
239
|
+
@click.group()
|
|
240
|
+
def git() -> None:
|
|
241
|
+
"""Git strategy management commands."""
|
|
242
|
+
pass
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
@git.command("list")
|
|
246
|
+
@click.option("--builtin/--no-builtin", default=True, help="Include built-in strategies")
|
|
247
|
+
def git_list(builtin: bool) -> None:
|
|
248
|
+
"""List available git strategies.
|
|
249
|
+
|
|
250
|
+
Example:
|
|
251
|
+
cl-preset git list
|
|
252
|
+
cl-preset git list --no-builtin
|
|
253
|
+
"""
|
|
254
|
+
manager = GitStrategyManager()
|
|
255
|
+
manager.print_list(include_builtin=builtin)
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
@git.command("info")
|
|
259
|
+
@click.argument("name")
|
|
260
|
+
def git_info(name: str) -> None:
|
|
261
|
+
"""Show detailed information about a git strategy.
|
|
262
|
+
|
|
263
|
+
NAME is the name of the strategy.
|
|
264
|
+
|
|
265
|
+
Example:
|
|
266
|
+
cl-preset git info dual-repo
|
|
267
|
+
"""
|
|
268
|
+
manager = GitStrategyManager()
|
|
269
|
+
strategy = manager.get_strategy(name)
|
|
270
|
+
|
|
271
|
+
if strategy is None:
|
|
272
|
+
console.print(f"[red]Strategy '{name}' not found.[/red]")
|
|
273
|
+
return
|
|
274
|
+
|
|
275
|
+
# Handle strategy_type being either a string or enum (due to use_enum_values=True)
|
|
276
|
+
strategy_type = (
|
|
277
|
+
strategy.strategy_type if isinstance(strategy.strategy_type, str)
|
|
278
|
+
else strategy.strategy_type.value
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
console.print(Panel.fit(f"[bold cyan]{strategy.name}[/bold cyan]", title="Strategy"))
|
|
282
|
+
console.print(f"[cyan]Type:[/cyan] {strategy_type}")
|
|
283
|
+
console.print(f"[cyan]Description:[/cyan] {strategy.description}")
|
|
284
|
+
console.print()
|
|
285
|
+
|
|
286
|
+
console.print("[bold]Branch Configuration:[/bold]")
|
|
287
|
+
console.print(f" Main Branch: [green]{strategy.main_branch}[/green]")
|
|
288
|
+
console.print(f" Develop Branch: [green]{strategy.develop_branch}[/green]")
|
|
289
|
+
console.print()
|
|
290
|
+
|
|
291
|
+
console.print("[bold]Branch Patterns:[/bold]")
|
|
292
|
+
console.print(f" Feature: [green]{strategy.branch_patterns.feature}[/green]")
|
|
293
|
+
console.print(f" Bugfix: [green]{strategy.branch_patterns.bugfix}[/green]")
|
|
294
|
+
console.print(f" Release: [green]{strategy.branch_patterns.release}[/green]")
|
|
295
|
+
console.print(f" Hotfix: [green]{strategy.branch_patterns.hotfix}[/green]")
|
|
296
|
+
console.print()
|
|
297
|
+
|
|
298
|
+
console.print("[bold]Merge Rules:[/bold]")
|
|
299
|
+
console.print(f" Require PR: [green]{strategy.merge_rules.require_pr}[/green]")
|
|
300
|
+
console.print(f" Require Approval: [green]{strategy.merge_rules.require_approval}[/green]")
|
|
301
|
+
console.print(f" Allow Squash: [green]{strategy.merge_rules.allow_squash}[/green]")
|
|
302
|
+
console.print(f" Allow Merge Commit: [green]{strategy.merge_rules.allow_merge_commit}[/green]")
|
|
303
|
+
console.print(f" Allow Rebase: [green]{strategy.merge_rules.allow_rebase}[/green]")
|
|
304
|
+
console.print()
|
|
305
|
+
|
|
306
|
+
if strategy.protection_rules:
|
|
307
|
+
console.print("[bold]Protection Rules:[/bold]")
|
|
308
|
+
for branch, rules in strategy.protection_rules.items():
|
|
309
|
+
status = "[green]enabled[/green]" if rules.enabled else "[red]disabled[/red]"
|
|
310
|
+
console.print(f" {branch}: {status}")
|
|
311
|
+
if rules.enabled:
|
|
312
|
+
if rules.required_check_names:
|
|
313
|
+
console.print(f" Required checks: {', '.join(rules.required_check_names)}")
|
|
314
|
+
|
|
315
|
+
if strategy.dev_repository:
|
|
316
|
+
console.print()
|
|
317
|
+
console.print("[bold]Dev Repository:[/bold]")
|
|
318
|
+
for key, value in strategy.dev_repository.items():
|
|
319
|
+
console.print(f" {key}: [green]{value}[/green]")
|
|
320
|
+
|
|
321
|
+
if strategy.release_repository:
|
|
322
|
+
console.print()
|
|
323
|
+
console.print("[bold]Release Repository:[/bold]")
|
|
324
|
+
for key, value in strategy.release_repository.items():
|
|
325
|
+
console.print(f" {key}: [green]{value}[/green]")
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
@git.command("export")
|
|
329
|
+
@click.argument("name")
|
|
330
|
+
@click.option("--output", "-o", type=click.Path(path_type=Path), default=None,
|
|
331
|
+
help="Output path (default: ./<name>.yaml)")
|
|
332
|
+
def git_export(name: str, output: Path | None) -> None:
|
|
333
|
+
"""Export a git strategy to YAML file.
|
|
334
|
+
|
|
335
|
+
NAME is the name of the strategy to export.
|
|
336
|
+
|
|
337
|
+
Example:
|
|
338
|
+
cl-preset git export dual-repo
|
|
339
|
+
cl-preset git export dual-repo -o ./my-strategy.yaml
|
|
340
|
+
"""
|
|
341
|
+
manager = GitStrategyManager()
|
|
342
|
+
|
|
343
|
+
if output is None:
|
|
344
|
+
output = Path.cwd() / f"{name}.yaml"
|
|
345
|
+
|
|
346
|
+
success = manager.export_strategy_yaml(name, output)
|
|
347
|
+
if not success:
|
|
348
|
+
raise click.ClickException(f"Failed to export strategy '{name}'")
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
@git.command("apply")
|
|
352
|
+
@click.argument("name")
|
|
353
|
+
@click.option("--dry-run", is_flag=True, help="Show what would be done without making changes")
|
|
354
|
+
def git_apply(name: str, dry_run: bool) -> None:
|
|
355
|
+
"""Apply a git strategy to the current project.
|
|
356
|
+
|
|
357
|
+
NAME is the name of the strategy to apply.
|
|
358
|
+
|
|
359
|
+
Example:
|
|
360
|
+
cl-preset git apply github-flow
|
|
361
|
+
cl-preset git apply github-flow --dry-run
|
|
362
|
+
"""
|
|
363
|
+
import subprocess
|
|
364
|
+
|
|
365
|
+
manager = GitStrategyManager()
|
|
366
|
+
strategy = manager.get_strategy(name)
|
|
367
|
+
|
|
368
|
+
if strategy is None:
|
|
369
|
+
console.print(f"[red]Strategy '{name}' not found.[/red]")
|
|
370
|
+
raise click.Abort()
|
|
371
|
+
|
|
372
|
+
console.print(f"[cyan]Applying strategy: {strategy.name}[/cyan]")
|
|
373
|
+
console.print(f"[dim]{strategy.description}[/dim]")
|
|
374
|
+
console.print()
|
|
375
|
+
|
|
376
|
+
if dry_run:
|
|
377
|
+
console.print("[yellow]Dry run mode - no changes will be made[/yellow]")
|
|
378
|
+
console.print()
|
|
379
|
+
|
|
380
|
+
# Show what would be configured
|
|
381
|
+
console.print("[bold]Branch Configuration:[/bold]")
|
|
382
|
+
console.print(f" Main branch: [green]{strategy.main_branch}[/green]")
|
|
383
|
+
if strategy.develop_branch:
|
|
384
|
+
console.print(f" Develop branch: [green]{strategy.develop_branch}[/green]")
|
|
385
|
+
console.print()
|
|
386
|
+
|
|
387
|
+
console.print("[bold]Branch Patterns:[/bold]")
|
|
388
|
+
console.print(f" Feature: [green]{strategy.branch_patterns.feature}[/green]")
|
|
389
|
+
console.print(f" Bugfix: [green]{strategy.branch_patterns.bugfix}[/green]")
|
|
390
|
+
console.print(f" Release: [green]{strategy.branch_patterns.release}[/green]")
|
|
391
|
+
console.print(f" Hotfix: [green]{strategy.branch_patterns.hotfix}[/green]")
|
|
392
|
+
console.print()
|
|
393
|
+
|
|
394
|
+
if not dry_run:
|
|
395
|
+
# Check if we're in a git repository
|
|
396
|
+
try:
|
|
397
|
+
result = subprocess.run(
|
|
398
|
+
["git", "rev-parse", "--git-dir"],
|
|
399
|
+
capture_output=True,
|
|
400
|
+
text=True,
|
|
401
|
+
cwd=Path.cwd()
|
|
402
|
+
)
|
|
403
|
+
if result.returncode != 0:
|
|
404
|
+
console.print("[red]Not in a git repository. Initialize one first with 'git init'[/red]")
|
|
405
|
+
raise click.Abort()
|
|
406
|
+
|
|
407
|
+
# Create .moai/config/sections directory
|
|
408
|
+
config_dir = Path.cwd() / ".moai" / "config" / "sections"
|
|
409
|
+
config_dir.mkdir(parents=True, exist_ok=True)
|
|
410
|
+
|
|
411
|
+
# Export strategy config
|
|
412
|
+
strategy_file = config_dir / "git-strategy.yaml"
|
|
413
|
+
success = manager.export_strategy_yaml(name, strategy_file)
|
|
414
|
+
|
|
415
|
+
if success:
|
|
416
|
+
console.print(f"[green]Strategy configuration written to {strategy_file}[/green]")
|
|
417
|
+
console.print()
|
|
418
|
+
console.print("[bold]Next Steps:[/bold]")
|
|
419
|
+
console.print(" 1. Review the strategy configuration")
|
|
420
|
+
console.print(" 2. Create required branches if needed")
|
|
421
|
+
console.print(" 3. Configure branch protection rules in your Git host")
|
|
422
|
+
|
|
423
|
+
except Exception as e:
|
|
424
|
+
console.print(f"[red]Error applying strategy: {e}[/red]")
|
|
425
|
+
raise click.Abort()
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
@git.command("validate")
|
|
429
|
+
@click.argument("name")
|
|
430
|
+
def git_validate(name: str) -> None:
|
|
431
|
+
"""Validate current git setup against a strategy.
|
|
432
|
+
|
|
433
|
+
NAME is the name of the expected strategy.
|
|
434
|
+
|
|
435
|
+
Example:
|
|
436
|
+
cl-preset git validate github-flow
|
|
437
|
+
"""
|
|
438
|
+
manager = GitStrategyManager()
|
|
439
|
+
results = manager.validate_current_setup(name)
|
|
440
|
+
|
|
441
|
+
console.print(f"[cyan]Validating against strategy: {name}[/cyan]")
|
|
442
|
+
console.print()
|
|
443
|
+
|
|
444
|
+
if results.get("info"):
|
|
445
|
+
info = results["info"]
|
|
446
|
+
if "current_branch" in info:
|
|
447
|
+
console.print(f"[dim]Current branch: {info['current_branch']}[/dim]")
|
|
448
|
+
|
|
449
|
+
console.print()
|
|
450
|
+
|
|
451
|
+
if results["valid"]:
|
|
452
|
+
console.print("[green]Validation passed![/green]")
|
|
453
|
+
else:
|
|
454
|
+
console.print("[red]Validation failed![/red]")
|
|
455
|
+
|
|
456
|
+
if results["errors"]:
|
|
457
|
+
console.print()
|
|
458
|
+
console.print("[bold red]Errors:[/bold red]")
|
|
459
|
+
for error in results["errors"]:
|
|
460
|
+
console.print(f" [red]x[/red] {error}")
|
|
461
|
+
|
|
462
|
+
if results["warnings"]:
|
|
463
|
+
console.print()
|
|
464
|
+
console.print("[bold yellow]Warnings:[/bold yellow]")
|
|
465
|
+
for warning in results["warnings"]:
|
|
466
|
+
console.print(f" [yellow]![/yellow] {warning}")
|
|
467
|
+
|
|
468
|
+
if not results["errors"] and not results["warnings"]:
|
|
469
|
+
console.print("[dim]No issues found.[/dim]")
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
# Add git group to main
|
|
473
|
+
main.add_command(git)
|
|
474
|
+
|
|
475
|
+
# Rename list command to avoid conflict with Python's list built-in
|
|
476
|
+
main.add_command(list_cmd, name="list")
|
|
477
|
+
|
|
478
|
+
if __name__ == "__main__":
|
|
479
|
+
main()
|