feat(plugins): paramètres projet plugins + attribution skills/tools aux agents — #282 (manifeste accepte contributes.skills IdeA-native multi-harness; store .ideai/project-plugins.json v1: enable plugin par projet + assignments skills/tools par agent; refs + use cases settings/assign/unassign; resolver skills pour idea_skill_read et injection catalogue lancement agent; permissions MCP dynamiques projetées par harness Claude Code/Codex/OpenCode; tombstones uninstall, hot reload recalcule catalogue et marque assignments cassés; frontend: Settings projet Plugins, détail capabilities, attribution agent, catalogue permissions; bump pointeur IdeaSDK 6eb8db3; QA verte: cargo test -p application, project_plugin_settings 3/3, dto_plugins 2/2, structured_launch_d3 26/26, catalogue mcp 7/7, frontend 5 tests ProjectPluginSettings/plugin, npm build frontend)
This commit is contained in:
@ -370,6 +370,41 @@ impl PluginMcpServerId {
|
||||
}
|
||||
}
|
||||
|
||||
/// Stable id of a skill contributed by a plugin manifest.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct PluginSkillId(String);
|
||||
|
||||
impl PluginSkillId {
|
||||
/// Validates and creates a plugin skill id.
|
||||
pub fn new(raw: impl Into<String>) -> Result<Self, PluginError> {
|
||||
PluginCommandId::new(raw).map(|v| Self(v.0))
|
||||
}
|
||||
|
||||
/// Returns the raw id.
|
||||
#[must_use]
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Read-only skill contribution whose body lives inside the plugin package.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PluginSkillContribution {
|
||||
/// Stable id within the plugin.
|
||||
pub id: PluginSkillId,
|
||||
/// Agent-facing name accepted by `idea_skill_read`.
|
||||
pub name: String,
|
||||
/// Short affordance shown in the injected skill catalogue.
|
||||
pub description: String,
|
||||
/// Capability nature.
|
||||
#[serde(default)]
|
||||
pub kind: crate::SkillKind,
|
||||
/// Package-relative Markdown file.
|
||||
pub path: RelativePath,
|
||||
}
|
||||
|
||||
/// Top-level menu contribution.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@ -484,6 +519,9 @@ pub struct PluginMcpServerContribution {
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PluginContributionSet {
|
||||
/// Read-only agent skills shipped in the package.
|
||||
#[serde(default)]
|
||||
pub skills: Vec<PluginSkillContribution>,
|
||||
/// Top-level menus.
|
||||
#[serde(default)]
|
||||
pub menus: Vec<PluginTopLevelMenuContribution>,
|
||||
@ -501,6 +539,94 @@ pub struct PluginContributionSet {
|
||||
pub mcp_servers: Vec<PluginMcpServerContribution>,
|
||||
}
|
||||
|
||||
/// Project-scoped plugin activation entry. It is retained when a plugin is
|
||||
/// temporarily uninstalled so reinstalling does not lose user intent.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ProjectPluginActivation {
|
||||
/// Globally-installed plugin id.
|
||||
pub plugin_id: PluginId,
|
||||
/// Whether this project may expose the plugin's capabilities.
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
/// Reference to one plugin skill assigned to an agent.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PluginSkillRef {
|
||||
/// Owning plugin.
|
||||
pub plugin_id: PluginId,
|
||||
/// Skill id within that plugin.
|
||||
pub skill_id: PluginSkillId,
|
||||
}
|
||||
|
||||
/// Stable namespace of one externally-provided MCP tool.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PluginToolRef {
|
||||
/// Owning plugin.
|
||||
pub plugin_id: PluginId,
|
||||
/// MCP server id within that plugin.
|
||||
pub server_id: PluginMcpServerId,
|
||||
/// Tool name advertised by the server.
|
||||
pub tool_name: String,
|
||||
}
|
||||
|
||||
/// Plugin capabilities explicitly assigned to one project agent.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AgentPluginAssignments {
|
||||
/// Agent receiving the capabilities.
|
||||
pub agent_id: crate::AgentId,
|
||||
/// Assigned plugin skills, in injection order.
|
||||
#[serde(default)]
|
||||
pub skills: Vec<PluginSkillRef>,
|
||||
/// Assigned external MCP tools.
|
||||
#[serde(default)]
|
||||
pub tools: Vec<PluginToolRef>,
|
||||
}
|
||||
|
||||
/// Versioned project-owned plugin settings persisted under
|
||||
/// `.ideai/project-plugins.json`.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ProjectPluginSettings {
|
||||
/// Schema version.
|
||||
pub version: u32,
|
||||
/// Explicit project activation choices.
|
||||
#[serde(default)]
|
||||
pub plugins: Vec<ProjectPluginActivation>,
|
||||
/// Per-agent assignments. Dangling entries are intentional tombstones.
|
||||
#[serde(default)]
|
||||
pub agents: Vec<AgentPluginAssignments>,
|
||||
}
|
||||
|
||||
impl Default for ProjectPluginSettings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
version: 1,
|
||||
plugins: Vec::new(),
|
||||
agents: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ProjectPluginSettings {
|
||||
/// Whether a plugin is explicitly enabled for this project.
|
||||
#[must_use]
|
||||
pub fn is_enabled(&self, plugin_id: &PluginId) -> bool {
|
||||
self.plugins
|
||||
.iter()
|
||||
.any(|p| &p.plugin_id == plugin_id && p.enabled)
|
||||
}
|
||||
|
||||
/// Assignments for an agent, if any.
|
||||
#[must_use]
|
||||
pub fn assignments_for(&self, agent_id: crate::AgentId) -> Option<&AgentPluginAssignments> {
|
||||
self.agents.iter().find(|a| a.agent_id == agent_id)
|
||||
}
|
||||
}
|
||||
|
||||
/// Validated plugin manifest.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
Reference in New Issue
Block a user