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:
2026-09-08 15:48:57 +02:00
parent bdc20a134a
commit 86ed0aa00b
38 changed files with 2127 additions and 89 deletions

View File

@ -3,19 +3,24 @@
use std::future::Future;
use std::path::{Path, PathBuf};
use application::{ReviewPluginPackageInput, SetPluginEnabledInput, UninstallPluginInput};
use application::{
plugin_skill_ref, plugin_tool_ref, AppError, AssignmentMutation, ReviewPluginPackageInput,
SetPluginEnabledInput, UninstallPluginInput,
};
use backend::dto::{
ErrorDto, PluginAdminDto, PluginConfigDocumentDto, PluginConfigDocumentReadDto,
PluginConfigDocumentUpdateDto, PluginConfigDocumentWriteResultDto, PluginEventBatchDto,
PluginEventPollDto, PluginEventSubscribeDto, PluginEventSubscriptionDto,
parse_agent_id, parse_project_id, ErrorDto, MutateAgentPluginSkillRequestDto,
MutateAgentPluginToolRequestDto, PluginAdminDto, PluginConfigDocumentDto,
PluginConfigDocumentReadDto, PluginConfigDocumentUpdateDto, PluginConfigDocumentWriteResultDto,
PluginEventBatchDto, PluginEventPollDto, PluginEventSubscribeDto, PluginEventSubscriptionDto,
PluginEventUnsubscribeDto, PluginInstallResultDto, PluginProjectStructureDto,
PluginProjectStructureQueryDto, PluginReloadResultDto, PluginReviewDto, PluginRunCommandDto,
PluginRuntimeContributionCatalogDto, PluginStorageGetDto, PluginStorageSetDto, PluginTaskDto,
PluginTaskStatusDto, PluginToolchainDiagnosticDto, PluginToolchainDiagnosticRequestDto,
PluginUninstallResultDto, PluginWorkspaceBinaryFileDto, PluginWorkspaceDirectoryListingDto,
PluginWorkspacePathDto, PluginWorkspaceStatDto, PluginWorkspaceTextFileDto,
PluginWorkspaceWriteBinaryDto, PluginWorkspaceWriteTextDto, ReloadPluginDto,
ReviewPluginPackageDto,
PluginWorkspaceWriteBinaryDto, PluginWorkspaceWriteTextDto, ProjectPluginSettingsDto,
ProjectPluginSettingsViewDto, ReloadPluginDto, ReviewPluginPackageDto,
SetProjectPluginEnabledRequestDto,
};
use domain::ports::{PluginManifestValidator, PluginPackageStore, PluginRegistryStore};
use domain::{PluginId, RelativePath};
@ -27,6 +32,14 @@ use crate::state::AppState;
const PLUGIN_ASSET_CORS_ORIGIN: &str = "*";
const PLUGIN_ASSET_CORS_METHODS: &str = "GET";
async fn resolve_project(state: &AppState, project_id: &str) -> Result<domain::Project, ErrorDto> {
state
.project_store
.load_project(parse_project_id(project_id)?)
.await
.map_err(|error| ErrorDto::from(AppError::from(error)))
}
/// Lists installed plugins for the admin surface.
#[tauri::command]
pub async fn plugin_list_plugins(
@ -40,6 +53,107 @@ pub async fn plugin_list_plugins(
.map_err(ErrorDto::from)
}
/// Lists project plugin activation, assignments, and installed manifests.
#[tauri::command]
pub async fn plugin_list_project_settings(
project_id: String,
state: State<'_, AppState>,
) -> Result<ProjectPluginSettingsViewDto, ErrorDto> {
let project = resolve_project(&state, &project_id).await?;
state
.list_project_plugin_settings
.execute(project)
.await
.map(ProjectPluginSettingsViewDto::from)
.map_err(ErrorDto::from)
}
/// Enables or disables an installed plugin for one project.
#[tauri::command]
pub async fn plugin_set_project_enabled(
input: SetProjectPluginEnabledRequestDto,
state: State<'_, AppState>,
) -> Result<ProjectPluginSettingsDto, ErrorDto> {
let project = resolve_project(&state, &input.project_id).await?;
let plugin_id =
PluginId::new(input.plugin_id).map_err(|error| ErrorDto::invalid(error.to_string()))?;
state
.set_project_plugin_enabled
.execute(project, plugin_id, input.enabled)
.await
.map(ProjectPluginSettingsDto::from)
.map_err(ErrorDto::from)
}
async fn mutate_project_skill(
input: MutateAgentPluginSkillRequestDto,
mutation: AssignmentMutation,
state: &AppState,
) -> Result<ProjectPluginSettingsDto, ErrorDto> {
let project = resolve_project(state, &input.project_id).await?;
let agent_id = parse_agent_id(&input.agent_id)?;
let reference = plugin_skill_ref(input.plugin_id, input.skill_id).map_err(ErrorDto::from)?;
state
.mutate_agent_plugin_skill
.execute(project, agent_id, reference, mutation)
.await
.map(ProjectPluginSettingsDto::from)
.map_err(ErrorDto::from)
}
/// Assigns a contributed plugin skill to a project agent.
#[tauri::command]
pub async fn plugin_assign_agent_skill(
input: MutateAgentPluginSkillRequestDto,
state: State<'_, AppState>,
) -> Result<ProjectPluginSettingsDto, ErrorDto> {
mutate_project_skill(input, AssignmentMutation::Assign, &state).await
}
/// Unassigns a contributed plugin skill from a project agent.
#[tauri::command]
pub async fn plugin_unassign_agent_skill(
input: MutateAgentPluginSkillRequestDto,
state: State<'_, AppState>,
) -> Result<ProjectPluginSettingsDto, ErrorDto> {
mutate_project_skill(input, AssignmentMutation::Unassign, &state).await
}
async fn mutate_project_tool(
input: MutateAgentPluginToolRequestDto,
mutation: AssignmentMutation,
state: &AppState,
) -> Result<ProjectPluginSettingsDto, ErrorDto> {
let project = resolve_project(state, &input.project_id).await?;
let agent_id = parse_agent_id(&input.agent_id)?;
let reference = plugin_tool_ref(input.plugin_id, input.server_id, input.tool_name)
.map_err(ErrorDto::from)?;
state
.mutate_agent_plugin_tool
.execute(project, agent_id, reference, mutation)
.await
.map(ProjectPluginSettingsDto::from)
.map_err(ErrorDto::from)
}
/// Assigns a namespaced plugin MCP tool to a project agent.
#[tauri::command]
pub async fn plugin_assign_agent_tool(
input: MutateAgentPluginToolRequestDto,
state: State<'_, AppState>,
) -> Result<ProjectPluginSettingsDto, ErrorDto> {
mutate_project_tool(input, AssignmentMutation::Assign, &state).await
}
/// Unassigns a namespaced plugin MCP tool from a project agent.
#[tauri::command]
pub async fn plugin_unassign_agent_tool(
input: MutateAgentPluginToolRequestDto,
state: State<'_, AppState>,
) -> Result<ProjectPluginSettingsDto, ErrorDto> {
mutate_project_tool(input, AssignmentMutation::Unassign, &state).await
}
/// Reviews a local plugin package without committing it.
#[tauri::command]
pub async fn plugin_review_package(