lite-kits 0.3.1__tar.gz → 0.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.
Files changed (41) hide show
  1. {lite_kits-0.3.1 → lite_kits-0.3.2}/PKG-INFO +1 -1
  2. {lite_kits-0.3.1 → lite_kits-0.3.2}/pyproject.toml +1 -1
  3. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/__init__.py +1 -1
  4. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/cli.py +102 -20
  5. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/core/detector.py +55 -18
  6. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/core/installer.py +17 -11
  7. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/core/validator.py +39 -2
  8. {lite_kits-0.3.1 → lite_kits-0.3.2}/.gitignore +0 -0
  9. {lite_kits-0.3.1 → lite_kits-0.3.2}/LICENSE +0 -0
  10. {lite_kits-0.3.1 → lite_kits-0.3.2}/README.md +0 -0
  11. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/core/__init__.py +0 -0
  12. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/core/banner.py +0 -0
  13. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/core/conflict_checker.py +0 -0
  14. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/core/manifest.py +0 -0
  15. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/kits/README.md +0 -0
  16. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/kits/dev/README.md +0 -0
  17. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/kits/dev/commands/.claude/audit.md +0 -0
  18. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/kits/dev/commands/.claude/cleanup.md +0 -0
  19. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/kits/dev/commands/.claude/commit.md +0 -0
  20. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/kits/dev/commands/.claude/orient.md +0 -0
  21. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/kits/dev/commands/.claude/pr.md +0 -0
  22. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/kits/dev/commands/.claude/review.md +0 -0
  23. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/kits/dev/commands/.claude/stats.md +0 -0
  24. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/kits/dev/commands/.github/audit.prompt.md +0 -0
  25. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/kits/dev/commands/.github/cleanup.prompt.md +0 -0
  26. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/kits/dev/commands/.github/commit.prompt.md +0 -0
  27. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/kits/dev/commands/.github/orient.prompt.md +0 -0
  28. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/kits/dev/commands/.github/pr.prompt.md +0 -0
  29. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/kits/dev/commands/.github/review.prompt.md +0 -0
  30. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/kits/dev/commands/.github/stats.prompt.md +0 -0
  31. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/kits/kits.yaml +0 -0
  32. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/kits/multiagent/README.md +0 -0
  33. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/kits/multiagent/commands/.claude/sync.md +0 -0
  34. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/kits/multiagent/commands/.github/sync.prompt.md +0 -0
  35. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/kits/multiagent/memory/git-worktrees-protocol.md +0 -0
  36. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/kits/multiagent/memory/parallel-work-protocol.md +0 -0
  37. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/kits/multiagent/memory/pr-workflow-guide.md +0 -0
  38. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/kits/multiagent/templates/collaboration-structure/README.md +0 -0
  39. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/kits/multiagent/templates/decision.md +0 -0
  40. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/kits/multiagent/templates/handoff.md +0 -0
  41. {lite_kits-0.3.1 → lite_kits-0.3.2}/src/lite_kits/kits/multiagent/templates/session-log.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lite-kits
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: Lightweight enhancement kits for vanilla dev tools (spec-kit, etc.)
5
5
  Project-URL: Homepage, https://github.com/tmorgan181/lite-kits
6
6
  Project-URL: Documentation, https://github.com/tmorgan181/lite-kits#readme
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "lite-kits"
7
- version = "0.3.1"
7
+ version = "0.3.2"
8
8
  description = "Lightweight enhancement kits for vanilla dev tools (spec-kit, etc.)"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -6,7 +6,7 @@ without forking or replacing any core files.
6
6
  """
7
7
 
8
8
  # Version
9
- __version__ = "0.3.1"
9
+ __version__ = "0.3.3"
10
10
 
11
11
  # Package metadata
12
12
  APP_NAME = "lite-kits"
@@ -51,7 +51,7 @@ def print_help_hint():
51
51
  def print_version_info():
52
52
  """Print version information."""
53
53
  console.print(f"[bold]Version:[/bold]")
54
- console.print(f" [bold cyan]{APP_NAME} version {__version__}[/bold cyan]\n")
54
+ console.print(f" [bold cyan]{APP_NAME} version {__version__}[/bold cyan]")
55
55
 
56
56
  def print_quick_start():
57
57
  console.print("[bold]Quick Start:[/bold]")
@@ -80,15 +80,68 @@ def print_spec_kit_error():
80
80
  console.print(" 4. More info: https://github.com/github/spec-kit\n")
81
81
  console.print()
82
82
 
83
+ def _build_kit_breakdown_table(target_dir: Path, kits: list[str]) -> Table:
84
+ """Build agent/shell breakdown table for kits.
85
+
86
+ Args:
87
+ target_dir: Target project directory
88
+ kits: List of kit names (e.g., ["dev", "multiagent"])
89
+
90
+ Returns:
91
+ Rich Table with agent/shell breakdown
92
+ """
93
+ from rich.box import ROUNDED
94
+
95
+ # Detect which agents/shells have files
96
+ agent_dirs = {
97
+ "Claude Code": target_dir / ".claude" / "commands",
98
+ "GitHub Copilot": target_dir / ".github" / "prompts"
99
+ }
100
+
101
+ shell_dirs = {
102
+ "Bash": target_dir / ".specify" / "scripts" / "bash",
103
+ "PowerShell": target_dir / ".specify" / "scripts" / "powershell"
104
+ }
105
+
106
+ # Build table
107
+ table = Table(show_header=True, header_style="bold cyan", box=ROUNDED, title=f"[bold magenta]Kit Breakdown[/bold magenta]")
108
+ table.add_column("Kit", style="cyan")
109
+ table.add_column("Agents", style="green")
110
+ table.add_column("Shells", style="white")
111
+
112
+ for kit in kits:
113
+ # Check which agents have this kit's files
114
+ agents_with_kit = []
115
+ for agent_name, agent_dir in agent_dirs.items():
116
+ if agent_dir.exists() and any(agent_dir.glob("*.md")):
117
+ # Check for at least one non-spec-kit file (not speckit.*)
118
+ non_speckit_files = [f for f in agent_dir.glob("*.md") if not f.stem.startswith("speckit.")]
119
+ if non_speckit_files:
120
+ agents_with_kit.append(agent_name)
121
+
122
+ # Check which shells have scripts
123
+ shells_with_kit = []
124
+ for shell_name, shell_dir in shell_dirs.items():
125
+ if shell_dir.exists() and (any(shell_dir.glob("*.sh")) or any(shell_dir.glob("*.ps1"))):
126
+ shells_with_kit.append(shell_name)
127
+
128
+ # Format output
129
+ agents_display = ", ".join(agents_with_kit) if agents_with_kit else "[dim]none[/dim]"
130
+ shells_display = ", ".join(shells_with_kit) if shells_with_kit else "[dim]none[/dim]"
131
+
132
+ # Use consistent kit naming format: "kit-name" not "kit-name-kit"
133
+ table.add_row(kit, agents_display, shells_display)
134
+
135
+ return table
136
+
83
137
  def print_kit_info(target_dir: Path, is_spec_kit: bool, installed_kits: list):
84
- """Print kit installation info."""
138
+ """Print kit installation info with agent/shell breakdown."""
85
139
  console.print()
86
140
  if is_spec_kit:
87
141
  console.print(f"[bold green][OK] Spec-kit project detected in {target_dir}.[/bold green]\n")
88
142
  if installed_kits:
89
- console.print("Installed kits:", style="bold")
90
- for kit in installed_kits:
91
- console.print(f" [green]+[/green] {kit}-kit")
143
+ table = _build_kit_breakdown_table(target_dir, installed_kits)
144
+ console.print(table)
92
145
  else:
93
146
  console.print("No kits installed.", style="dim yellow")
94
147
  else:
@@ -101,7 +154,6 @@ def version_callback(value: bool):
101
154
  if value:
102
155
  console.print()
103
156
  print_version_info()
104
- console.print()
105
157
  raise typer.Exit()
106
158
 
107
159
  @app.callback(invoke_without_command=True)
@@ -221,13 +273,17 @@ def add_kits(
221
273
  kits = [k.strip() for k in kit.split(',')]
222
274
  # else: kits=None will use default from manifest
223
275
 
276
+ # Parse comma-separated agents and shells
277
+ agents = [a.strip() for a in agent.split(',')] if agent else None
278
+ shells = [s.strip() for s in shell.split(',')] if shell else None
279
+
224
280
  try:
225
281
  installer = Installer(
226
282
  target_dir,
227
283
  kits=kits,
228
284
  force=force,
229
- agent=agent,
230
- shell=shell
285
+ agents=agents,
286
+ shells=shells
231
287
  )
232
288
  except ValueError as e:
233
289
  console.print()
@@ -259,10 +315,16 @@ def add_kits(
259
315
 
260
316
  # Always show preview unless --force flag was used
261
317
  if not skip_preview:
262
- console.print(f"\n[bold magenta]Previewing changes for:[/bold magenta]\n[bold yellow]{target_dir}[/bold yellow]\n")
263
- preview = installer.preview_installation()
318
+ try:
319
+ preview = installer.preview_installation()
320
+ except ValueError as e:
321
+ console.print()
322
+ console.print(f"[red]Error:[/red] {e}", style="bold")
323
+ console.print()
324
+ raise typer.Exit(1)
325
+
264
326
  normalized_preview = _normalize_preview_for_display(preview, operation="install")
265
- _display_changes(normalized_preview, verbose=verbose)
327
+ _display_changes(normalized_preview, target_dir, verbose=verbose)
266
328
 
267
329
  # Show warnings/conflicts
268
330
  if preview.get("warnings"):
@@ -288,8 +350,8 @@ def add_kits(
288
350
  target_dir,
289
351
  kits=kits,
290
352
  force=True, # Skip conflict checks after user confirmed
291
- agent=agent,
292
- shell=shell
353
+ agents=agents,
354
+ shells=shells
293
355
  )
294
356
 
295
357
  # Install
@@ -385,7 +447,6 @@ def remove(
385
447
  # Show preview and confirmation unless --force is used
386
448
  if not force:
387
449
  # Show preview of files to be removed
388
- console.print(f"\n[bold magenta]Previewing changes for:[/bold magenta]\n[bold yellow]{target_dir}[/bold yellow]\n")
389
450
  preview = installer.preview_removal()
390
451
 
391
452
  if preview["total_files"] == 0:
@@ -395,7 +456,7 @@ def remove(
395
456
 
396
457
  # Normalize removal preview to standard format for DRY display
397
458
  normalized_preview = _normalize_preview_for_display(preview, operation="remove")
398
- _display_changes(normalized_preview, verbose=verbose)
459
+ _display_changes(normalized_preview, target_dir, verbose=verbose)
399
460
 
400
461
  # Confirm removal
401
462
  if not typer.confirm("Continue with removal?"):
@@ -543,15 +604,19 @@ def _normalize_preview_for_display(preview: dict, operation: str = "install") ->
543
604
  else:
544
605
  raise ValueError(f"Unknown operation: {operation}")
545
606
 
546
- def _display_changes(changes: dict, verbose: bool = False):
607
+ def _display_changes(changes: dict, target_dir: Path, verbose: bool = False):
547
608
  """Display preview of changes.
548
609
 
549
610
  Args:
550
611
  changes: Normalized preview dict with file/directory changes
612
+ target_dir: Target directory being modified
551
613
  verbose: If True, show detailed file listings; if False, show only tables
552
614
  """
553
615
  from collections import defaultdict
554
616
 
617
+ # Show preview header
618
+ console.print(f"\n[bold magenta]Previewing changes for:[/bold magenta]\n[bold yellow]{target_dir}[/bold yellow]\n")
619
+
555
620
  # Collect stats for each kit
556
621
  kit_stats = {}
557
622
  total_stats = defaultdict(int)
@@ -859,13 +924,18 @@ def _display_removal_summary(result: dict, verbose: bool = False):
859
924
  console.print(f"\nRemoved {len(all_removed)} files")
860
925
 
861
926
  def _display_validation_results(validation_result: dict):
862
- """Display validation results in a user-friendly format.
927
+ """Display validation results with per-kit status and breakdown table.
928
+
929
+ Shows validation-specific information (file checks, missing files, integrity issues)
930
+ followed by the agent/shell breakdown table for kits that passed validation.
863
931
 
864
932
  Args:
865
- validation_result: Dict with 'valid' (bool) and 'checks' (dict of kit results)
933
+ validation_result: Dict with 'valid' (bool), 'checks' (dict of kit results), and 'target_dir' (Path)
866
934
  """
867
935
  checks = validation_result.get("checks", {})
936
+ target_dir = validation_result.get("target_dir", Path.cwd())
868
937
 
938
+ # Show per-kit validation status with detailed issues
869
939
  for kit_name, result in checks.items():
870
940
  status = result.get("status", "unknown")
871
941
 
@@ -874,13 +944,25 @@ def _display_validation_results(validation_result: dict):
874
944
  elif status == "not_installed":
875
945
  console.print(f"[dim][-] {kit_name} (not installed)[/dim]")
876
946
  elif status == "partial":
877
- console.print(f"[yellow][!] {kit_name} (partial - some files missing)[/yellow]")
947
+ console.print(f"[yellow][!] {kit_name} (partial - issues found)[/yellow]")
948
+ # Show detailed issues
878
949
  missing = result.get("missing_files", [])
950
+ corrupted = result.get("corrupted_files", [])
879
951
  if missing:
880
- console.print(f"[dim] Missing files: {', '.join(missing[:3])}" + (" ..." if len(missing) > 3 else "") + "[/dim]")
952
+ console.print(f"[dim] Missing: {', '.join(missing[:3])}" + (" ..." if len(missing) > 3 else "") + "[/dim]")
953
+ if corrupted:
954
+ console.print(f"[dim] Corrupted: {', '.join(corrupted[:3])}" + (" ..." if len(corrupted) > 3 else "") + "[/dim]")
881
955
  else:
882
956
  console.print(f"[red][X] {kit_name} ({status})[/red]")
883
957
 
958
+ # Show agent/shell breakdown table for validated kits
959
+ validated_kits = [kit_name for kit_name, result in checks.items() if result.get("status") == "installed"]
960
+
961
+ if validated_kits:
962
+ console.print()
963
+ table = _build_kit_breakdown_table(target_dir, validated_kits)
964
+ console.print(table)
965
+
884
966
  def _cleanup_empty_directories(target_dir: Path):
885
967
  """Clean up empty directories created by lite-kits."""
886
968
  directories_to_check = [
@@ -24,24 +24,37 @@ class Detector:
24
24
  self.target_dir = target_dir
25
25
  self.manifest = manifest
26
26
 
27
- def detect_agents(self, preferred: Optional[str] = None) -> List[str]:
27
+ def detect_agents(self, preferred: Optional[List[str]] = None) -> List[str]:
28
28
  """
29
29
  Auto-detect which AI agents are present.
30
30
 
31
31
  Args:
32
- preferred: Explicit agent preference (overrides auto-detection)
32
+ preferred: List of explicit agent preferences (overrides auto-detection)
33
33
 
34
34
  Returns:
35
35
  List of agent names sorted by priority
36
36
  """
37
- # If explicit preference, validate and return
37
+ # If explicit preferences, validate and return
38
38
  if preferred:
39
- config = self.manifest.get_agent_config(preferred)
40
- if not config:
41
- raise ValueError(f"Unknown agent: {preferred}")
42
- if not config.get('supported', False):
43
- raise ValueError(f"Agent not supported: {preferred}")
44
- return [preferred]
39
+ validated = []
40
+ for agent_name in preferred:
41
+ config = self.manifest.get_agent_config(agent_name)
42
+ if not config:
43
+ # Build helpful error message with valid options
44
+ agents = self.manifest.manifest.get('agents', {})
45
+ valid_agents = [
46
+ name for name, cfg in agents.items()
47
+ if cfg.get('supported', False)
48
+ ]
49
+ valid_list = ', '.join(valid_agents)
50
+ raise ValueError(
51
+ f"Unknown agent: '{agent_name}'\n"
52
+ f"Valid options: {valid_list}"
53
+ )
54
+ if not config.get('supported', False):
55
+ raise ValueError(f"Agent not supported: {agent_name}")
56
+ validated.append(agent_name)
57
+ return validated
45
58
 
46
59
  # Auto-detect from manifest
47
60
  detected = []
@@ -65,24 +78,48 @@ class Detector:
65
78
  detected.sort(key=lambda x: x['priority'])
66
79
  return [agent['name'] for agent in detected]
67
80
 
68
- def detect_shells(self, preferred: Optional[str] = None) -> List[str]:
81
+ def detect_shells(self, preferred: Optional[List[str]] = None) -> List[str]:
69
82
  """
70
83
  Determine which shells to install for.
71
84
 
72
85
  Args:
73
- preferred: Explicit shell preference (overrides auto-detection)
86
+ preferred: List of explicit shell preferences (overrides auto-detection)
74
87
 
75
88
  Returns:
76
89
  List of shell names
77
90
  """
78
- # If explicit preference, validate and return
91
+ # Shell aliases for common shorthands
92
+ shell_aliases = {
93
+ 'ps': 'powershell',
94
+ 'pwsh': 'powershell',
95
+ 'sh': 'bash',
96
+ }
97
+
98
+ # If explicit preferences, validate and return
79
99
  if preferred:
80
- config = self.manifest.manifest.get('shells', {}).get(preferred)
81
- if not config:
82
- raise ValueError(f"Unknown shell: {preferred}")
83
- if not config.get('supported', False):
84
- raise ValueError(f"Shell not supported: {preferred}")
85
- return [preferred]
100
+ validated = []
101
+ for shell_name in preferred:
102
+ # Normalize shell name using aliases
103
+ normalized = shell_aliases.get(shell_name.lower(), shell_name.lower())
104
+
105
+ config = self.manifest.manifest.get('shells', {}).get(normalized)
106
+ if not config:
107
+ # Build helpful error message with valid options
108
+ shells_config = self.manifest.manifest.get('shells', {})
109
+ valid_shells = [
110
+ name for name, cfg in shells_config.items()
111
+ if cfg.get('supported', False)
112
+ ]
113
+ valid_list = ', '.join(valid_shells)
114
+ raise ValueError(
115
+ f"Unknown shell: '{shell_name}'\n"
116
+ f"Valid options: {valid_list}\n"
117
+ f"Aliases: ps/pwsh->powershell, sh->bash"
118
+ )
119
+ if not config.get('supported', False):
120
+ raise ValueError(f"Shell not supported: {normalized}")
121
+ validated.append(normalized)
122
+ return validated
86
123
 
87
124
  # Check if shell detection is enabled
88
125
  options = self.manifest.manifest.get('options', {})
@@ -23,8 +23,8 @@ class Installer:
23
23
  target_dir: Path,
24
24
  kits: Optional[List[str]] = None,
25
25
  force: bool = False,
26
- agent: Optional[str] = None,
27
- shell: Optional[str] = None,
26
+ agents: Optional[List[str]] = None,
27
+ shells: Optional[List[str]] = None,
28
28
  ):
29
29
  """
30
30
  Initialize installer.
@@ -33,8 +33,8 @@ class Installer:
33
33
  target_dir: Target spec-kit project directory
34
34
  kits: List of kits to install (None = use default from manifest)
35
35
  force: Skip confirmations and overwrite existing files
36
- agent: Explicit agent preference (None = auto-detect)
37
- shell: Explicit shell preference (None = auto-detect)
36
+ agents: List of explicit agent preferences (None = auto-detect)
37
+ shells: List of explicit shell preferences (None = auto-detect)
38
38
  """
39
39
  self.target_dir = Path(target_dir).resolve()
40
40
  self.kits_dir = Path(__file__).parent.parent / "kits"
@@ -54,9 +54,15 @@ class Installer:
54
54
  # Operational modes
55
55
  self.force = force
56
56
 
57
- # Preferences
58
- self.preferred_agent = agent
59
- self.preferred_shell = shell
57
+ # Preferences - validate immediately during init
58
+ self.preferred_agents = agents
59
+ self.preferred_shells = shells
60
+
61
+ # Validate agent/shell preferences early (raises ValueError if invalid)
62
+ if agents:
63
+ self.detector.detect_agents(agents)
64
+ if shells:
65
+ self.detector.detect_shells(shells)
60
66
 
61
67
  # Kits to install
62
68
  self.kits = kits or [self.manifest.get_default_kit()]
@@ -82,8 +88,8 @@ class Installer:
82
88
 
83
89
  def preview_installation(self) -> Dict:
84
90
  """Preview installation without making changes."""
85
- agents = self.detector.detect_agents(self.preferred_agent)
86
- shells = self.detector.detect_shells(self.preferred_shell)
91
+ agents = self.detector.detect_agents(self.preferred_agents)
92
+ shells = self.detector.detect_shells(self.preferred_shells)
87
93
 
88
94
  preview = {
89
95
  "kits": [],
@@ -169,8 +175,8 @@ class Installer:
169
175
  }
170
176
 
171
177
  try:
172
- agents = self.detector.detect_agents(self.preferred_agent)
173
- shells = self.detector.detect_shells(self.preferred_shell)
178
+ agents = self.detector.detect_agents(self.preferred_agents)
179
+ shells = self.detector.detect_shells(self.preferred_shells)
174
180
 
175
181
  if not agents:
176
182
  supported = [
@@ -8,6 +8,7 @@ from pathlib import Path
8
8
  from typing import Dict
9
9
 
10
10
  from .manifest import KitManifest
11
+ from .detector import Detector
11
12
 
12
13
 
13
14
  class Validator:
@@ -23,6 +24,7 @@ class Validator:
23
24
  """
24
25
  self.target_dir = target_dir
25
26
  self.manifest = manifest
27
+ self.detector = Detector(target_dir, manifest)
26
28
 
27
29
  def validate_all(self) -> Dict:
28
30
  """
@@ -46,6 +48,7 @@ class Validator:
46
48
  return {
47
49
  "valid": any_installed,
48
50
  "checks": checks,
51
+ "target_dir": self.target_dir,
49
52
  }
50
53
 
51
54
  def validate_kit(self, kit_name: str, options: Dict) -> Dict:
@@ -75,14 +78,48 @@ class Validator:
75
78
  "message": f"{kit_info['name']}: not installed",
76
79
  }
77
80
 
78
- # Kit is installed, validate files
81
+ # Detect which agents are actually present in the project
82
+ detected_agents = self.detector.detect_agents()
83
+ detected_shells = self.detector.detect_shells()
84
+
85
+ # Kit is installed, validate only files for detected agents/shells that actually have kit files
86
+ files_to_validate = []
87
+
88
+ # Add agent-specific files (commands/prompts) - only if at least one kit file exists for that agent
89
+ for agent in detected_agents:
90
+ agent_files = self.manifest.get_kit_files(kit_name, agent=agent)
91
+ # Check if any of this kit's files exist for this agent
92
+ has_kit_files = any(
93
+ (self.target_dir / f['path']).exists()
94
+ for f in agent_files
95
+ if f.get('status') != 'planned'
96
+ )
97
+ if has_kit_files:
98
+ files_to_validate.extend(agent_files)
99
+
100
+ # Add shell-specific files (scripts) - only if at least one kit file exists for that shell
101
+ for shell in detected_shells:
102
+ shell_files = self.manifest.get_kit_files(kit_name, agent=shell)
103
+ has_kit_files = any(
104
+ (self.target_dir / f['path']).exists()
105
+ for f in shell_files
106
+ if f.get('status') != 'planned'
107
+ )
108
+ if has_kit_files:
109
+ files_to_validate.extend(shell_files)
110
+
111
+ # Add agent/shell-agnostic files (memory, templates, etc.)
79
112
  all_files = self.manifest.get_kit_files(kit_name, agent=None)
113
+ for file_info in all_files:
114
+ # Only add files that aren't agent/shell-specific
115
+ if file_info.get('type') not in ['command', 'prompt', 'script']:
116
+ files_to_validate.append(file_info)
80
117
 
81
118
  missing = []
82
119
  corrupted = []
83
120
  outdated = []
84
121
 
85
- for file_info in all_files:
122
+ for file_info in files_to_validate:
86
123
  # Skip non-required files
87
124
  if not file_info.get('required', True):
88
125
  continue
File without changes
File without changes
File without changes