Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions src/bundle.rs
Original file line number Diff line number Diff line change
@@ -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)]
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -317,7 +317,7 @@ impl Bundle {
}

/// Load metadata from meta.yaml file
fn load_meta_yaml(dir: &PathBuf) -> Option<ResourceMeta> {
fn load_meta_yaml(dir: &Path) -> Option<ResourceMeta> {
let meta_path = dir.join("meta.yaml");
if !meta_path.exists() {
return None;
Expand All @@ -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<Vec<SkillFile>> {
fn scan_type(bundle_path: &Path, skill_type: SkillType) -> anyhow::Result<Vec<SkillFile>> {
let type_dir = bundle_path.join(skill_type.dir_name());

if !type_dir.exists() {
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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]
Expand Down
4 changes: 2 additions & 2 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
21 changes: 11 additions & 10 deletions src/install.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -18,7 +18,7 @@ pub fn install_bundle(
config: &Config,
bundle_name: &str,
tool: &Tool,
target_dir: &PathBuf,
target_dir: &Path,
types: &[SkillType],
) -> Result<Vec<InstallRecord>> {
// Find the bundle in configured sources
Expand Down Expand Up @@ -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<Vec<InstallRecord>> {
let bundles = source.list_bundles()?;
Expand Down Expand Up @@ -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<Vec<InstallRecord>> {
let bundles = source.list_bundles()?;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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();
}

Expand All @@ -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();
}

Expand All @@ -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();
}

Expand All @@ -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();
}

Expand All @@ -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();
}

Expand All @@ -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();
}

Expand Down
24 changes: 12 additions & 12 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -688,9 +688,9 @@ fn sources_add(name: Option<String>, 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)
};
Expand Down Expand Up @@ -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};
Expand Down Expand Up @@ -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,
};
Expand Down Expand Up @@ -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};

Expand Down Expand Up @@ -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};

Expand Down Expand Up @@ -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<()> {
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
17 changes: 10 additions & 7 deletions src/manifest.rs
Original file line number Diff line number Diff line change
@@ -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<SourceMeta>,
#[serde(default)]
pub bundles: Vec<BundleDeclaration>,
}

#[derive(Debug, Deserialize)]
#[allow(dead_code)]
pub struct SourceMeta {
pub name: Option<String>,
pub description: Option<String>,
Expand All @@ -21,6 +23,7 @@ pub struct BundleDeclaration {
pub name: String,
pub path: String,
pub description: Option<String>,
#[allow(dead_code)]
pub tags: Option<Vec<String>>,
#[serde(default)]
pub paths: ComponentPaths,
Expand Down Expand Up @@ -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<SourceManifest> {
pub fn load_manifest(source_root: &Path) -> Option<SourceManifest> {
let manifest_path = source_root.join("skm.toml");
if !manifest_path.exists() {
return None;
Expand All @@ -61,7 +64,7 @@ pub fn load_manifest(source_root: &PathBuf) -> Option<SourceManifest> {

/// 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<Bundle> {
let bundle_root = source_root.join(&decl.path);
Expand Down Expand Up @@ -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]
Expand All @@ -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());
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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);
Expand Down
Loading