Skip to content

Commit f9093be

Browse files
committed
feat(skills): add skills management and marketplace features
1 parent af3acec commit f9093be

38 files changed

Lines changed: 2555 additions & 29 deletions

src-tauri/Cargo.lock

Lines changed: 32 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ rustls = { version = "0.23", default-features = false, features = ["aws-lc-rs"]
6565
tauri-plugin-mcp-bridge = "0.2"
6666
font-kit = "0.14"
6767
tauri-plugin-clipboard-manager = "2"
68+
reqwest = { version = "0.12", features = ["json"] }
69+
zip = "2"
70+
tempfile = "3"
71+
open = "5"
72+
urlencoding = "2"
73+
dirs = "5"
6874

6975
[target.'cfg(target_vendor = "apple")'.dependencies]
7076
objc2 = { version = "0.6", features = ["relax-sign-encoding"] }

src-tauri/crates/core/src/entity/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub mod providers;
1717
pub mod search_citations;
1818
pub mod search_providers;
1919
pub mod settings;
20+
pub mod skill_states;
2021
pub mod tool_descriptors;
2122
pub mod tool_executions;
2223

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
use sea_orm::entity::prelude::*;
2+
use serde::{Deserialize, Serialize};
3+
4+
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
5+
#[sea_orm(table_name = "skill_states")]
6+
pub struct Model {
7+
#[sea_orm(primary_key, auto_increment = false)]
8+
pub name: String,
9+
pub enabled: i32,
10+
pub updated_at: i64,
11+
}
12+
13+
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
14+
pub enum Relation {}
15+
16+
impl ActiveModelBehavior for ActiveModel {}

src-tauri/crates/core/src/repo.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub mod program_policy;
1717
pub mod provider;
1818
pub mod search_provider;
1919
pub mod settings;
20+
pub mod skill;
2021
pub mod stored_file;
2122
pub mod tool_execution;
2223
pub mod agent_session;
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
use sea_orm::*;
2+
use sea_orm::prelude::Expr;
3+
use std::collections::HashSet;
4+
5+
use crate::entity::skill_states;
6+
use crate::error::Result;
7+
use crate::utils::now_ts;
8+
9+
/// Get all disabled skill names from the database.
10+
pub async fn get_disabled_skills(db: &DatabaseConnection) -> Result<HashSet<String>> {
11+
let rows = skill_states::Entity::find()
12+
.filter(skill_states::Column::Enabled.eq(0))
13+
.all(db)
14+
.await?;
15+
Ok(rows.into_iter().map(|r| r.name).collect())
16+
}
17+
18+
/// Set a skill's enabled state. Creates or updates the record.
19+
pub async fn set_skill_enabled(
20+
db: &DatabaseConnection,
21+
name: &str,
22+
enabled: bool,
23+
) -> Result<()> {
24+
let now = now_ts();
25+
let existing = skill_states::Entity::find_by_id(name).one(db).await?;
26+
27+
if let Some(_) = existing {
28+
skill_states::Entity::update_many()
29+
.col_expr(
30+
skill_states::Column::Enabled,
31+
Expr::value(if enabled { 1 } else { 0 }),
32+
)
33+
.col_expr(skill_states::Column::UpdatedAt, Expr::value(now))
34+
.filter(skill_states::Column::Name.eq(name))
35+
.exec(db)
36+
.await?;
37+
} else {
38+
let am = skill_states::ActiveModel {
39+
name: Set(name.to_string()),
40+
enabled: Set(if enabled { 1 } else { 0 }),
41+
updated_at: Set(now),
42+
};
43+
am.insert(db).await?;
44+
}
45+
Ok(())
46+
}

src-tauri/crates/core/src/types.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1453,3 +1453,61 @@ pub struct UpdateMemoryItemInput {
14531453
pub title: Option<String>,
14541454
pub content: Option<String>,
14551455
}
1456+
1457+
// ── Skills ────────────────────────────────────────────────────────────
1458+
1459+
#[derive(Debug, Clone, Serialize, Deserialize)]
1460+
#[serde(rename_all = "camelCase")]
1461+
pub struct SkillInfo {
1462+
pub name: String,
1463+
pub description: String,
1464+
pub author: Option<String>,
1465+
pub version: Option<String>,
1466+
pub source: String,
1467+
pub source_path: String,
1468+
pub enabled: bool,
1469+
pub has_update: bool,
1470+
pub user_invocable: bool,
1471+
pub argument_hint: Option<String>,
1472+
pub group: Option<String>,
1473+
}
1474+
1475+
#[derive(Debug, Clone, Serialize, Deserialize)]
1476+
#[serde(rename_all = "camelCase")]
1477+
pub struct SkillDetail {
1478+
pub info: SkillInfo,
1479+
pub content: String,
1480+
pub files: Vec<String>,
1481+
pub manifest: Option<SkillManifest>,
1482+
}
1483+
1484+
#[derive(Debug, Clone, Serialize, Deserialize)]
1485+
#[serde(rename_all = "camelCase")]
1486+
pub struct SkillManifest {
1487+
pub source_kind: String,
1488+
pub source_ref: Option<String>,
1489+
pub branch: Option<String>,
1490+
pub commit: Option<String>,
1491+
pub installed_at: String,
1492+
pub installed_via: Option<String>,
1493+
}
1494+
1495+
#[derive(Debug, Clone, Serialize, Deserialize)]
1496+
#[serde(rename_all = "camelCase")]
1497+
pub struct SkillUpdateInfo {
1498+
pub name: String,
1499+
pub current_commit: String,
1500+
pub latest_commit: String,
1501+
pub source_ref: String,
1502+
}
1503+
1504+
#[derive(Debug, Clone, Serialize, Deserialize)]
1505+
#[serde(rename_all = "camelCase")]
1506+
pub struct MarketplaceSkill {
1507+
pub name: String,
1508+
pub description: String,
1509+
pub repo: String,
1510+
pub stars: i64,
1511+
pub installs: i64,
1512+
pub installed: bool,
1513+
}

src-tauri/crates/migration/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ mod m20250122_000001_merge_thinking_to_content;
2424
mod m20250123_000001_add_category_system_prompt;
2525
mod m20250717_000001_add_agent_support;
2626
mod m20250718_000001_add_sdk_context_backup;
27+
mod m20250719_000001_add_skill_states;
2728

2829
pub struct Migrator;
2930

@@ -55,6 +56,7 @@ impl MigratorTrait for Migrator {
5556
Box::new(m20250123_000001_add_category_system_prompt::Migration),
5657
Box::new(m20250717_000001_add_agent_support::Migration),
5758
Box::new(m20250718_000001_add_sdk_context_backup::Migration),
59+
Box::new(m20250719_000001_add_skill_states::Migration),
5860
]
5961
}
6062
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
use sea_orm_migration::prelude::*;
2+
3+
#[derive(DeriveMigrationName)]
4+
pub struct Migration;
5+
6+
#[async_trait::async_trait]
7+
impl MigrationTrait for Migration {
8+
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
9+
manager
10+
.create_table(
11+
Table::create()
12+
.table(SkillStates::Table)
13+
.if_not_exists()
14+
.col(
15+
ColumnDef::new(SkillStates::Name)
16+
.string()
17+
.not_null()
18+
.primary_key(),
19+
)
20+
.col(
21+
ColumnDef::new(SkillStates::Enabled)
22+
.integer()
23+
.not_null()
24+
.default(1),
25+
)
26+
.col(
27+
ColumnDef::new(SkillStates::UpdatedAt)
28+
.integer()
29+
.not_null(),
30+
)
31+
.to_owned(),
32+
)
33+
.await
34+
}
35+
36+
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
37+
manager
38+
.drop_table(Table::drop().table(SkillStates::Table).to_owned())
39+
.await
40+
}
41+
}
42+
43+
#[derive(DeriveIden)]
44+
enum SkillStates {
45+
Table,
46+
Name,
47+
Enabled,
48+
UpdatedAt,
49+
}

0 commit comments

Comments
 (0)