Files
IdeA/agents-dev/L12-skills.md

7.3 KiB

L12 — Skills

Binôme : dev-skills / test-skills Zones : domain/skill, application/skill, infrastructure/store, frontend/features/skills Dépendances amont : L0, L1, L5, L6 (convention file généré à l'activation), L7 (store global réutilisé).

Objectif

Modéliser les Skills : workflows réutilisables (équivalent universel des slash-commands, sans dépendance à la syntaxe /command d'un modèle). Stockage global IDE + projet, assignation agent↔skills, injection des skills assignés dans le convention file généré à l'activation de l'agent. Cf. ARCHITECTURE §14.2.

Périmètre (DEV)

  • Domaine : entité Skill { id, name, content_md: MarkdownDoc, scope: SkillScope(Global|Project) }. Invariants : name non vide, content_md non vide. Event SkillAssigned.
  • Port SkillStore : CRUD skills globaux (<app_data>/IdeA/skills/) + skills projet (.ideai/skills/<name>.md), résolution selon scope (compose FileSystem/store global comme L7).
  • AgentManifest : étendre pour porter la liste skills: Vec<SkillRef> assignés à chaque agent (0..N).
  • Use cases (application/skill) : CreateSkill, UpdateSkill, DeleteSkill, ListSkills(scope), AssignSkillToAgent, UnassignSkillFromAgent.
  • Injection : à l'activation (fil L6), composer le convention file en concaténant persona agent + chemin project root + skills assignés (lus via SkillStore). Pas de mécanisme CLI propriétaire.
  • Front : onglet/section Skills (liste globale + projet, CRUD, éditeur md), assignation skills↔agent dans AgentsPanel.

Périmètre (TEST)

  • Skill rejette name/content_md vides.
  • SkillStore : CRUD round-trip en tmpdir pour les deux scopes ; un skill Project n'apparaît pas dans le scope Global et inversement.
  • AssignSkillToAgent / UnassignSkillFromAgent : mutent l'AgentManifest, émettent SkillAssigned, idempotents (pas de doublon).
  • Injection : le convention file généré contient bien le content_md des skills assignés et rien des skills non assignés ; ordre déterministe.
  • Front : CRUD skills + assignation via gateway mock (RTL) ; garde-fou « no direct invoke ».

Definition of Done

  • cargo test (skill/store/app) + vitest verts ; cycle manuel : créer un skill, l'assigner à un agent, l'activer → le skill apparaît dans le convention file de .ideai/run/<agent-id>/.
  • DoD commune (cf. README) respectée ; zéro régression.

Avancement

Domaine (vert)

  • Entité Skill (domain/skill.rs) : id: SkillId, name, content_md: MarkdownDoc, scope: SkillScope(Global|Project). Constructeur validant (name + content_md non vides), with_content re-valide l'invariant.
  • SkillRef { skill_id, scope } : référence d'assignation portée par l'agent ; From<&Skill>.
  • SkillId ajouté (ids.rs), event SkillAssigned { agent_id, skill_id, assigned } (events.rs), DTO + arm de mapping côté app-tauri (events.rs).
  • Agent étendu : champ skills: Vec<SkillRef> (serde default), méthodes assign_skill (idempotent), unassign_skill, with_skills (dédup). ManifestEntry : champ skills (serde default + skip_serializing_if → rétrocompat des manifests pré-L12) ; from_agent/to_agent préservent les skills.
  • Tests : 8 invariants (entities.rs) + 3 serde dont rétrocompat d'un manifest legacy sans clé skills (serde_roundtrip.rs). cargo test -p domain vert ; cargo test --workspace vert (0 régression) ; clippy clean.

Port + adapter store (vert)

  • Port SkillStore (domain/ports.rs) : list/get/save/delete portant scope + root: &ProjectPath par appel (root ignoré pour Global, résolu pour Project) — un seul store sert tous les projets ouverts, comme AgentContextStore.
  • Adapter FsSkillStore (infrastructure/store/skill.rs) : même forme on-disk que FsTemplateStore (index.json + md/<id>.md), deux racines disjointes : <app_data>/skills/ (Global) et <root>/.ideai/skills/ (Project). Delete laisse l'orphelin md (pas de remove dans le port FS), index = source de vérité. 7 tests d'intégration tmpdir (skill_store.rs) : round-trip 2 scopes, isolation de scope, upsert, delete idempotent, camelCase.

Use cases application (vert)

  • application/skill : CreateSkill, UpdateSkill, DeleteSkill, ListSkills(scope) (inputs portant project_root), AssignSkillToAgent / UnassignSkillFromAgent (mutent l'AgentManifest via to_agent/from_agent, dédup, émettent SkillAssigned, idempotents). 9 tests (skill_usecases.rs).

Injection dans le convention file (vert, fil L6)

  • LaunchAgent reçoit le port SkillStore ; resolve_skills lit les .md des skills assignés (ordre manifest, déterministe ; skill supprimé = SkillRef pendant → ignoré sans bloquer le lancement).
  • compose_convention_file étendu : section # Skills (sous-titres ## <name>) après le persona ; omise si aucun skill. 3 tests unitaires + e2e (agent_lifecycle.rs : injection ordonnée, ref pendant tolérée).
  • Composition root (app-tauri/state.rs) : FsSkillStore construit (app-data global), injecté dans LaunchAgent.

IPC app-tauri (vert)

  • DTOs (dto.rs) : SkillDto (transparent sur Skill, camelCase), SkillListDto, request DTOs (Create/Update/Assign/UnassignSkillRequestDto), parse_skill_id. scope désérialise directement vers SkillScope ("global"/"project").
  • Commandes (commands.rs) : create_skill, update_skill, list_skills, delete_skill, assign_skill_to_agent, unassign_skill_from_agent — shells fins qui résolvent le Project (→ project.root) puis appellent le use case. Enregistrées dans lib.rs.
  • Composition root (state.rs) : 6 use cases skill câblés sur le skill_store_port (déjà construit pour le launcher) et le contexts_port partagé.
  • cargo build -p app-tauri + cargo test --workspace (304) verts ; clippy clean.

Front features/skills (vert)

  • Domaine (domain/index.ts) : SkillScope, Skill, SkillRef ; Agent étendu avec skills: SkillRef[].
  • Port (ports/index.ts) : SkillGateway (list/create/update/delete + assign/unassign) + CreateSkillInput ; ajouté à Gateways.
  • Adapters : TauriSkillGateway (adapters/skill.ts, invoke camelCase) ; MockSkillGateway (adapters/mock, scopes disjoints + mutation partagée du MockAgentGateway via _setSkills, assign idempotent).
  • Feature : useSkills (VM 2 scopes), SkillEditor (overlay md edit/preview + sélecteur de scope), SkillsPanel (listes Project/Global, CRUD). Onglet Skills ajouté dans ProjectsView.
  • Assignation dans AgentsPanel : chips des skills assignés + sélecteur d'assignation + unassign, sur l'agent sélectionné ; refresh après mutation.
  • Tests (skills.test.tsx, RTL via DIProvider + mocks) : CRUD project/global, isolation de scope, édition, suppression, assign/unassign reflétés sur l'agent, idempotence, garde-fou « no direct invoke » (aucune action run/launch). vitest : 229 verts (0 régression ; test « ten gateways » mis à jour).

Reste à faire

  • Cycle manuel : créer un skill, l'assigner à un agent, l'activer → vérifier qu'il apparaît dans le convention file de .ideai/run/<agent-id>/ (à faire sur l'AppImage).