feat(skills,mcp): menus Skills et MCP unifies avec selection cochable des tools #297 (SkillsPanel devient la surface unique d'assignation cochable des skills IdeA + plugins — cible agent, recherche, groupes IdeA/Plugins, tombstones retirables, CRUD catalogue natif conserve, stores separes SkillGateway/PluginGateway; nouveau panneau MCP dedie McpPanel desktop+web — tools MCP IdeA via PermissionGateway et tools MCP plugins catalogues via PluginGateway groupes par plugin/serveur, selection cochable, fin de la saisie manuelle du nom exact, serveurs sans catalogue en tombstones retirables uniquement; retrait complet de la surface Capacites de la fiche agent et suppression d'AgentPluginCapabilities.tsx; PermissionsPanel reserve aux permissions systeme/sandbox, onglet MCP retire; raccourci Parametres > Plugins ouvre Skills avec renvois vers Skills/MCP; integration ProjectsView/ViewPanelBody/viewPlacement/WebWorkspace/webSurfaceCatalog; QA verte: tsc+vite build OK, 28+24 tests cibles OK, suite complete 1223 OK / 32 echecs sur 6 fichiers web hors perimetre classes non bloquants; scripts/ et sync tickets #296 hors perimetre; aucun push)

This commit is contained in:
2026-09-09 20:38:39 +02:00
parent f78f0093e8
commit e6ce7d6e74
17 changed files with 452 additions and 631 deletions

View File

@ -1,5 +1,5 @@
/**
* Ticket #82 — the "Tools MCP IdeA" tab of the project `Permissions` panel.
* Ticket #82/#297 — the dedicated project `MCP` panel.
*
* Pins the UX contract from carnet #82 (Conception UX/F): an agent without an
* override shows it inherits the project default with non-editable controls
@ -14,11 +14,12 @@ import { fireEvent, render, screen, waitFor, within } from "@testing-library/rea
import {
MockAgentGateway,
MockPermissionGateway,
MockPluginGateway,
MockProfileGateway,
} from "@/adapters/mock";
import { DIProvider } from "@/app/di";
import type { Gateways } from "@/ports";
import { PermissionsPanel } from "./PermissionsPanel";
import { McpPanel } from "./McpPanel";
const PROJECT_ID = "proj-mcp-permissions-test";
@ -32,12 +33,13 @@ async function renderPanel() {
const gateways = {
agent,
permission,
plugin: new MockPluginGateway(),
profile: new MockProfileGateway(),
} as unknown as Gateways;
render(
<DIProvider gateways={gateways}>
<PermissionsPanel projectId={PROJECT_ID} />
<McpPanel projectId={PROJECT_ID} />
</DIProvider>,
);
@ -48,9 +50,7 @@ async function renderPanel() {
return { agent, permission, agentId: created.id };
}
/** Switches to the "Tools MCP IdeA" tab and waits past the doc load. */
async function openMcpToolsTab() {
fireEvent.click(screen.getByRole("tab", { name: "Tools MCP IdeA" }));
await screen.findByRole("navigation", { name: "cibles des permissions MCP" });
}
@ -58,20 +58,16 @@ async function selectAgentTarget(name: string) {
const nav = screen.getByRole("navigation", { name: "cibles des permissions MCP" });
fireEvent.click(within(nav).getByRole("button", { name: new RegExp(name) }));
await waitFor(() => {
expect(screen.getByRole("heading", { name: `Tools MCP IdeA — ${name}` })).toBeTruthy();
expect(screen.getByRole("heading", { name: `MCP IdeA — ${name}` })).toBeTruthy();
});
}
describe("PermissionsPanel — Tools MCP IdeA tab", () => {
it("has an accessible tablist and defaults to Système", async () => {
describe("McpPanel", () => {
it("is exposed as a dedicated surface", async () => {
await renderPanel();
const tablist = screen.getByRole("tablist", { name: "Permissions" });
const tabs = within(tablist).getAllByRole("tab");
expect(tabs.map((t) => t.textContent)).toEqual(["Système", "Tools MCP IdeA"]);
expect(screen.getByRole("tab", { name: "Système" }).getAttribute("aria-selected")).toBe(
"true",
);
await openMcpToolsTab();
expect(screen.getByRole("heading", { name: "MCP" })).toBeTruthy();
expect(screen.queryByRole("tablist", { name: "Permissions" })).toBeNull();
});
it("an agent without an override shows it inherits the project default, read-only", async () => {
@ -131,11 +127,12 @@ describe("PermissionsPanel — Tools MCP IdeA tab", () => {
const gateways = {
agent,
permission,
plugin: new MockPluginGateway(),
profile: new MockProfileGateway(),
} as unknown as Gateways;
render(
<DIProvider gateways={gateways}>
<PermissionsPanel projectId={PROJECT_ID} />
<McpPanel projectId={PROJECT_ID} />
</DIProvider>,
);
@ -262,7 +259,7 @@ describe("PermissionsPanel — Tools MCP IdeA tab", () => {
expect(window.confirm).toHaveBeenCalled();
// Still on the agent editor, with the unsaved edit intact.
expect(
screen.getByRole("heading", { name: "Tools MCP IdeA — DevFrontend" }),
screen.getByRole("heading", { name: "MCP IdeA — DevFrontend" }),
).toBeTruthy();
expect(
(screen.getByRole("checkbox", { name: /idea_ticket_update_carnet$/ }) as HTMLInputElement)