···290290 [tool.pixi-skills]
291291 agent = "pi"
292292293293- Then installs all skills from .pixi/envs/{env}/share/agent-skills/ to the
294294- agent's local skills directory.
293293+ Installs skills from .pixi/envs/{env}/share/agent-skills/ and removes
294294+ skills that are no longer available in the environment.
295295 """
296296 # Find and parse config
297297 config_path = find_pixi_toml()
···330330 f"Syncing to [cyan]{backend_name}[/cyan] -> [dim]{skills_dir}[/dim]\n"
331331 )
332332333333- # Discover available skills
334334- skills = discover_local_skills(env)
333333+ # Discover available skills from pixi environment
334334+ available_skills = discover_local_skills(env)
335335+ available_names = {s.name for s in available_skills}
335336336336- if not skills:
337337- console.print(
338338- f"[yellow]No skills found in .pixi/envs/{env}/share/agent-skills/[/yellow]"
339339- )
337337+ # Get currently installed skills in backend
338338+ installed_skills = backend_instance.get_installed_skills(Scope.LOCAL)
339339+ installed_names = {name for name, _ in installed_skills}
340340+341341+ # Determine what to install and remove
342342+ to_install = [s for s in available_skills if s.name not in installed_names]
343343+ to_remove = [name for name in installed_names if name not in available_names]
344344+345345+ if not to_install and not to_remove:
346346+ console.print(f"[dim]Already in sync ({len(available_skills)} skill(s))[/dim]")
340347 raise typer.Exit(0)
341348342342- # Get currently installed skills
343343- installed_names = {
344344- name for name, _ in backend_instance.get_installed_skills(Scope.LOCAL)
345345- }
349349+ # Show summary of changes
350350+ if available_skills:
351351+ console.print(f"[dim]Available in env: {len(available_skills)} skill(s)[/dim]")
352352+ else:
353353+ console.print(f"[dim]No skills in .pixi/envs/{env}/share/agent-skills/[/dim]")
346354347347- # Determine what to install
348348- to_install = [s for s in skills if s.name not in installed_names]
355355+ if installed_names:
356356+ console.print(f"[dim]Installed: {len(installed_names)} skill(s)[/dim]")
357357+ else:
358358+ console.print("[dim]Installed: 0 skill(s)[/dim]")
349359350350- if not to_install:
351351- console.print(f"[dim]All {len(skills)} skill(s) already synced.[/dim]")
352352- raise typer.Exit(0)
353353-354354- # Install skills
360360+ # Install new skills
355361 installed_count = 0
356356- for skill in sorted(to_install):
362362+ for skill in sorted(to_install, key=lambda s: s.name):
357363 try:
358364 symlink_path = backend_instance.install(skill)
359365 console.print(f"[green]+[/green] {skill.name} -> {symlink_path}")
···361367 except ValueError as e:
362368 console.print(f"[red]✗ {skill.name}: {e}[/red]")
363369364364- console.print(
365365- f"\n[green]Synced {installed_count}/{len(to_install)} skill(s)[/green]"
366366- )
370370+ # Remove skills no longer available
371371+ removed_count = 0
372372+ for name in sorted(to_remove):
373373+ if backend_instance.uninstall(name, Scope.LOCAL):
374374+ console.print(f"[red]-[/red] {name}")
375375+ removed_count += 1
376376+ else:
377377+ console.print(f"[yellow]?[/yellow] {name} (not found)")
378378+379379+ # Summary
380380+ changes = []
381381+ if installed_count:
382382+ changes.append(f"installed {installed_count}")
383383+ if removed_count:
384384+ changes.append(f"removed {removed_count}")
385385+386386+ if changes:
387387+ console.print(f"\n[green]Synced: {', '.join(changes)} skill(s)[/green]")
388388+389389+ # Show final state
390390+ final_count = len(available_names)
391391+ console.print(f"[dim]Result: {final_count} skill(s) in {skills_dir}[/dim]")
367392368393369394if __name__ == "__main__":
+1-1
pixi_skills/config.py
···6161 parent = current.parent
6262 if parent == current: # Reached filesystem root
6363 return None
6464- current = parent6464+ current = parent
+1-1
recipe/recipe.release.yaml
···3838 summary: Manage coding agent skills using pixi
3939 homepage: https://github.com/pavelzw/pixi-skills
4040 license: MIT
4141- license_file: LICENSE4141+ license_file: LICENSE
+1-1
recipe/recipe.yaml
···3434 summary: Manage coding agent skills using pixi
3535 homepage: https://github.com/pavelzw/pixi-skills
3636 license: MIT
3737- license_file: LICENSE3737+ license_file: LICENSE
+1-1
tests/test_config.py
···9292 deep_dir = tmp_path / "a" / "b" / "c"
9393 deep_dir.mkdir(parents=True)
9494 result = find_pixi_toml(deep_dir)
9595- assert result == config_file9595+ assert result == config_file