diff --git a/src/bundle.rs b/src/bundle.rs index 4a6a37c..b98e334 100644 --- a/src/bundle.rs +++ b/src/bundle.rs @@ -1,5 +1,5 @@ use serde::Deserialize; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; /// Type of skill item #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -212,13 +212,13 @@ impl Bundle { } /// Check if a path uses the resources format - pub fn is_resources_format(path: &PathBuf) -> bool { + pub fn is_resources_format(path: &Path) -> bool { path.join("resources").is_dir() } /// Check if a path uses the Anthropic/marketplace format /// Structure: skills/{name}/SKILL.md at the root level - pub fn is_anthropic_format(path: &PathBuf) -> bool { + pub fn is_anthropic_format(path: &Path) -> bool { let skills_dir = path.join("skills"); if !skills_dir.is_dir() { return false; @@ -317,7 +317,7 @@ impl Bundle { } /// Load metadata from meta.yaml file - fn load_meta_yaml(dir: &PathBuf) -> Option { + fn load_meta_yaml(dir: &Path) -> Option { let meta_path = dir.join("meta.yaml"); if !meta_path.exists() { return None; @@ -328,7 +328,7 @@ impl Bundle { /// Scan a subdirectory for skill files. /// Handles BOTH flat .md files AND {name}/SKILL.md directory format. - fn scan_type(bundle_path: &PathBuf, skill_type: SkillType) -> anyhow::Result> { + fn scan_type(bundle_path: &Path, skill_type: SkillType) -> anyhow::Result> { let type_dir = bundle_path.join(skill_type.dir_name()); if !type_dir.exists() { @@ -511,11 +511,11 @@ mod tests { let dir = tempdir().unwrap(); // Without resources/ directory - assert!(!Bundle::is_resources_format(&dir.path().to_path_buf())); + assert!(!Bundle::is_resources_format(dir.path())); // With resources/ directory fs::create_dir(dir.path().join("resources")).unwrap(); - assert!(Bundle::is_resources_format(&dir.path().to_path_buf())); + assert!(Bundle::is_resources_format(dir.path())); } #[test] @@ -617,17 +617,17 @@ mod tests { let dir = tempdir().unwrap(); // Without skills/ directory - assert!(!Bundle::is_anthropic_format(&dir.path().to_path_buf())); + assert!(!Bundle::is_anthropic_format(dir.path())); // With skills/ directory but no SKILL.md fs::create_dir(dir.path().join("skills")).unwrap(); - assert!(!Bundle::is_anthropic_format(&dir.path().to_path_buf())); + assert!(!Bundle::is_anthropic_format(dir.path())); // With skills/{name}/SKILL.md let skill_dir = dir.path().join("skills").join("my-skill"); fs::create_dir_all(&skill_dir).unwrap(); fs::write(skill_dir.join("SKILL.md"), "# Skill content").unwrap(); - assert!(Bundle::is_anthropic_format(&dir.path().to_path_buf())); + assert!(Bundle::is_anthropic_format(dir.path())); } #[test] diff --git a/src/config.rs b/src/config.rs index ef1c11f..b0a72bb 100644 --- a/src/config.rs +++ b/src/config.rs @@ -267,9 +267,9 @@ impl SourceConfig { /// Expand ~ to home directory fn expand_tilde(path: &str) -> PathBuf { - if path.starts_with("~/") { + if let Some(stripped) = path.strip_prefix("~/") { if let Some(home) = dirs_home() { - return home.join(&path[2..]); + return home.join(stripped); } } else if path == "~" { if let Some(home) = dirs_home() { diff --git a/src/install.rs b/src/install.rs index 267ec8d..dc7dd8f 100644 --- a/src/install.rs +++ b/src/install.rs @@ -1,6 +1,6 @@ use anyhow::Result; use colored::Colorize; -use std::path::PathBuf; +use std::path::Path; use crate::bundle::SkillType; use crate::config::Config; @@ -18,7 +18,7 @@ pub fn install_bundle( config: &Config, bundle_name: &str, tool: &Tool, - target_dir: &PathBuf, + target_dir: &Path, types: &[SkillType], ) -> Result> { // Find the bundle in configured sources @@ -95,7 +95,7 @@ pub fn install_bundle( pub fn install_from_source( source: &dyn Source, tool: &Tool, - target_dir: &PathBuf, + target_dir: &Path, types: &[SkillType], ) -> Result> { let bundles = source.list_bundles()?; @@ -155,7 +155,7 @@ pub fn install_bundle_from_source( source: &dyn Source, bundle_name: &str, tool: &Tool, - target_dir: &PathBuf, + target_dir: &Path, types: &[SkillType], ) -> Result> { let bundles = source.list_bundles()?; @@ -220,6 +220,7 @@ pub fn install_bundle_from_source( mod tests { use super::*; use std::fs; + use std::path::PathBuf; use tempfile::tempdir; fn setup_test_source() -> (tempfile::TempDir, PathBuf) { @@ -258,7 +259,7 @@ mod tests { for cmd in &bundle.commands { Tool::Claude - .write_file(&target_dir.path().to_path_buf(), "test-bundle", cmd) + .write_file(target_dir.path(), "test-bundle", cmd) .unwrap(); } @@ -283,7 +284,7 @@ mod tests { // Test skill (should create directory structure) for skill in &bundle.skills { Tool::OpenCode - .write_file(&target_dir.path().to_path_buf(), "test-bundle", skill) + .write_file(target_dir.path(), "test-bundle", skill) .unwrap(); } @@ -296,7 +297,7 @@ mod tests { // Test command for cmd in &bundle.commands { Tool::OpenCode - .write_file(&target_dir.path().to_path_buf(), "test-bundle", cmd) + .write_file(target_dir.path(), "test-bundle", cmd) .unwrap(); } @@ -316,7 +317,7 @@ mod tests { // Test skill (should go to skills directory) for skill in &bundle.skills { Tool::Cursor - .write_file(&target_dir.path().to_path_buf(), "test-bundle", skill) + .write_file(target_dir.path(), "test-bundle", skill) .unwrap(); } @@ -329,7 +330,7 @@ mod tests { // Test agent (should go to agents directory as flat file) for agent in &bundle.agents { Tool::Cursor - .write_file(&target_dir.path().to_path_buf(), "test-bundle", agent) + .write_file(target_dir.path(), "test-bundle", agent) .unwrap(); } @@ -342,7 +343,7 @@ mod tests { // Test command (should go to commands directory as flat file) for command in &bundle.commands { Tool::Cursor - .write_file(&target_dir.path().to_path_buf(), "test-bundle", command) + .write_file(target_dir.path(), "test-bundle", command) .unwrap(); } diff --git a/src/main.rs b/src/main.rs index 6e1d76a..7afe720 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,7 +13,7 @@ use clap::{CommandFactory, Parser, Subcommand}; use clap_complete::{generate, Shell}; use colored::Colorize; use std::io; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use crate::bundle::SkillType; use crate::config::{Config, SourceConfig}; @@ -266,7 +266,7 @@ fn main() -> Result<()> { to_rule, output, }) => { - convert_format(&source, to_rule, output.as_ref())?; + convert_format(&source, to_rule, output.as_deref())?; } Some(Commands::Rm { bundle, yes }) => { let filter_tool = if cli.cursor { @@ -688,9 +688,9 @@ fn sources_add(name: Option, path: String) -> Result<()> { // Check if path exists for local sources if let SourceConfig::Local { ref path, .. } = source { - let expanded = if path.starts_with("~/") { + let expanded = if let Some(stripped) = path.strip_prefix("~/") { let home = std::env::var("HOME")?; - PathBuf::from(format!("{}/{}", home, &path[2..])) + PathBuf::from(format!("{}/{}", home, stripped)) } else { PathBuf::from(path) }; @@ -777,7 +777,7 @@ fn update_sources(config: &Config) -> Result<()> { fn refresh_installed_skills( config: &Config, tool: &Tool, - target_dir: &PathBuf, + target_dir: &Path, types: &[SkillType], ) -> Result<()> { use crate::discover::{discover_installed, filter_by_tool}; @@ -1007,7 +1007,7 @@ fn list_bundles(config: &Config) -> Result<()> { Ok(()) } -fn show_installed_skills(base: &PathBuf, filter_tool: Option<&str>) -> Result<()> { +fn show_installed_skills(base: &Path, filter_tool: Option<&str>) -> Result<()> { use crate::discover::{ discover_installed, filter_by_tool, group_by_tool, InstalledTool, SkillType, }; @@ -1108,7 +1108,7 @@ fn generate_completions(shell: Shell) { generate(shell, &mut cmd, "skm", &mut io::stdout()); } -fn interactive_remove(base: &PathBuf, filter_tool: Option<&str>) -> Result<()> { +fn interactive_remove(base: &Path, filter_tool: Option<&str>) -> Result<()> { use crate::discover::{discover_installed, filter_by_tool, group_same_skills, remove_skill}; use dialoguer::{theme::ColorfulTheme, Confirm, MultiSelect}; @@ -1222,7 +1222,7 @@ fn interactive_remove(base: &PathBuf, filter_tool: Option<&str>) -> Result<()> { Ok(()) } -fn clean_all_skills(base: &PathBuf, filter_tool: Option<&str>, skip_confirm: bool) -> Result<()> { +fn clean_all_skills(base: &Path, filter_tool: Option<&str>, skip_confirm: bool) -> Result<()> { use crate::discover::{discover_installed, filter_by_tool, remove_skill}; use dialoguer::{theme::ColorfulTheme, Confirm}; @@ -1316,7 +1316,7 @@ fn skill_matches_bundle(skill: &crate::discover::InstalledSkill, bundle_name: &s fn remove_bundle( bundle_name: &str, - base: &PathBuf, + base: &Path, filter_tool: Option<&str>, skip_confirm: bool, ) -> Result<()> { @@ -1434,7 +1434,7 @@ fn remove_bundle( Ok(()) } -fn convert_format(source: &PathBuf, to_rule: bool, output: Option<&PathBuf>) -> Result<()> { +fn convert_format(source: &Path, to_rule: bool, output: Option<&Path>) -> Result<()> { use std::fs; use std::io::Write; @@ -1472,7 +1472,7 @@ fn convert_format(source: &PathBuf, to_rule: bool, output: Option<&PathBuf>) -> Ok(()) } -fn convert_to_rule(content: &str, source_path: &PathBuf) -> String { +fn convert_to_rule(content: &str, source_path: &Path) -> String { let lines: Vec<&str> = content.lines().collect(); // Check if already has frontmatter @@ -1556,7 +1556,7 @@ fn do_install( config: &Config, bundle_ref: &str, tool: &Tool, - target_dir: &PathBuf, + target_dir: &Path, types: &[SkillType], ) -> Result<()> { let (source_name, bundle_name) = parse_bundle_ref(bundle_ref); diff --git a/src/manifest.rs b/src/manifest.rs index 095c798..2aebd2e 100644 --- a/src/manifest.rs +++ b/src/manifest.rs @@ -1,16 +1,18 @@ use serde::Deserialize; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use crate::bundle::{Bundle, BundleMeta, SkillFile, SkillType}; #[derive(Debug, Deserialize)] pub struct SourceManifest { + #[allow(dead_code)] pub source: Option, #[serde(default)] pub bundles: Vec, } #[derive(Debug, Deserialize)] +#[allow(dead_code)] pub struct SourceMeta { pub name: Option, pub description: Option, @@ -21,6 +23,7 @@ pub struct BundleDeclaration { pub name: String, pub path: String, pub description: Option, + #[allow(dead_code)] pub tags: Option>, #[serde(default)] pub paths: ComponentPaths, @@ -50,7 +53,7 @@ impl ComponentPaths { } /// Load and parse an skm.toml manifest from a source root directory -pub fn load_manifest(source_root: &PathBuf) -> Option { +pub fn load_manifest(source_root: &Path) -> Option { let manifest_path = source_root.join("skm.toml"); if !manifest_path.exists() { return None; @@ -61,7 +64,7 @@ pub fn load_manifest(source_root: &PathBuf) -> Option { /// Build a Bundle from a manifest declaration by scanning its declared paths pub fn bundle_from_declaration( - source_root: &PathBuf, + source_root: &Path, decl: &BundleDeclaration, ) -> anyhow::Result { let bundle_root = source_root.join(&decl.path); @@ -192,7 +195,7 @@ mod tests { #[test] fn test_load_manifest_not_present() { let dir = tempdir().unwrap(); - assert!(load_manifest(&dir.path().to_path_buf()).is_none()); + assert!(load_manifest(dir.path()).is_none()); } #[test] @@ -207,7 +210,7 @@ path = "src" "#, ) .unwrap(); - let manifest = load_manifest(&dir.path().to_path_buf()).unwrap(); + let manifest = load_manifest(dir.path()).unwrap(); assert_eq!(manifest.bundles.len(), 1); assert_eq!(manifest.bundles[0].name, "my-bundle"); assert!(manifest.source.is_none()); @@ -241,7 +244,7 @@ path = "plugins/b" "#, ) .unwrap(); - let manifest = load_manifest(&dir.path().to_path_buf()).unwrap(); + let manifest = load_manifest(dir.path()).unwrap(); assert_eq!( manifest.source.as_ref().unwrap().name.as_deref(), Some("test-source") @@ -343,7 +346,7 @@ path = "plugins/b" }, }; - let bundle = bundle_from_declaration(&dir.path().to_path_buf(), &decl).unwrap(); + let bundle = bundle_from_declaration(dir.path(), &decl).unwrap(); assert_eq!(bundle.name, "synapse-docs"); assert_eq!(bundle.skills.len(), 1); assert_eq!(bundle.agents.len(), 1); diff --git a/src/target.rs b/src/target.rs index 933a3a9..7f8cf18 100644 --- a/src/target.rs +++ b/src/target.rs @@ -66,7 +66,7 @@ impl Tool { /// Write a skill file to the appropriate location for this tool pub fn write_file( &self, - target_dir: &PathBuf, + target_dir: &Path, bundle_name: &str, skill: &SkillFile, ) -> Result { @@ -116,7 +116,7 @@ impl Tool { // Phase 1+4: detect agent format and reverse-transform if needed fn write_claude( &self, - target_dir: &PathBuf, + target_dir: &Path, bundle_name: &str, skill: &SkillFile, ) -> Result { @@ -190,7 +190,7 @@ impl Tool { // Phase 4: detect agent format before transforming fn write_opencode( &self, - target_dir: &PathBuf, + target_dir: &Path, bundle_name: &str, skill: &SkillFile, ) -> Result { @@ -253,7 +253,7 @@ impl Tool { // rules -> .cursor/rules/{bundle}-{name}/RULE.md (folder-based) fn write_cursor( &self, - target_dir: &PathBuf, + target_dir: &Path, bundle_name: &str, skill: &SkillFile, ) -> Result { @@ -314,7 +314,7 @@ impl Tool { // rules -> .codex/rules/{bundle}-{name}/RULE.md (folder-based) fn write_codex( &self, - target_dir: &PathBuf, + target_dir: &Path, bundle_name: &str, skill: &SkillFile, ) -> Result { @@ -1432,7 +1432,7 @@ This is the agent content. }; let result = Tool::Cursor.write_file(&target_dir, "tb", &skill).unwrap(); - + // Should be in .cursor/agents/ as a flat file let expected_path = target_dir.join(".cursor/agents/tb-my-agent.md"); assert_eq!(result, expected_path); @@ -1463,7 +1463,7 @@ This is the agent content. }; let result = Tool::Cursor.write_file(&target_dir, "tb", &skill).unwrap(); - + // Should be in .cursor/commands/ as a flat file let expected_path = target_dir.join(".cursor/commands/tb-my-command.md"); assert_eq!(result, expected_path);