[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6CSZGQ8X9XNXRBYX0AA3CYE":3,"skill-same:01M6CSZGQ8X9XNXRBYX0AA3CYE":208},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":24,"linkOnly":25,"markdownContent":26,"markdownBody":27,"frontMatter":28,"refName":38,"commitSha":39,"skillDirectory":40,"forksCount":14,"repositoryHtmlUrl":41,"repositoryHomepageUrl":42,"ownerHtmlUrl":43,"ownerBlogUrl":6,"ownerDescription":44,"updatedAt":45,"install":46,"resources":59},"01M6CSZGQ8X9XNXRBYX0AA3CYE","symfony:voter",null,"Genere un Voter de securite Symfony pour le controle d'acces granulaire","atournayre\u002Fclaude-personas","atournayre","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F4262077?v=4","01M6CSSGYW0E0D0M3E2T6J9X2Z","Aurélien Tournayre","01M6CSSH3DENJF2XWZ41H0F43K",0,"MIT","https:\u002F\u002Fgithub.com\u002Fatournayre\u002Fclaude-personas\u002Fblob\u002Fmain\u002Fsymfony\u002Fskills\u002Fvoter\u002FSKILL.md",[],[5,19,20,21,22,23],"Bash","grep","read","write","edit","2026-11-10 10:12:30",false,"---\nname: symfony:voter\ndescription: Genere un Voter de securite Symfony pour le controle d'acces granulaire\nmodel: sonnet\nallowed-tools: [AskUserQuestion, Bash, Read, Write, Edit, Grep, Glob]\nversion: 1.0.0\nlicense: MIT\n---\n\n# Symfony Voter Skill\n\nGenere un Voter Symfony pour le controle d'acces granulaire sur des objets ou des actions specifiques.\n\n## Variables\n- **{VoterName}** - Nom du voter en PascalCase (ex: PostVoter)\n- **{subject}** - Type d'objet sur lequel porte le vote\n- **{permissions}** - Liste des permissions gerees\n- **{namespace}** - Namespace du projet (defaut: App)\n\n## Outputs\n- `src\u002FSecurity\u002FVoter\u002F{VoterName}.php`\n\n## Instructions a Executer\n\n**IMPORTANT : Execute ce workflow etape par etape :**\n\n### 1. Demander le nom du voter\n\n- Utilise AskUserQuestion pour demander :\n  ```\n  Question: \"Quel est le nom du voter ? (ex: Post, Comment, Invoice)\"\n  Header: \"Voter\"\n  ```\n- Ajoute le suffixe `Voter` si absent\n\n### 2. Demander le sujet\n\n- Utilise AskUserQuestion pour demander :\n  ```\n  Question: \"Sur quel type d'objet porte le vote ? (ex: App\\Entity\\Post, App\\Entity\\Invoice)\"\n  Header: \"Sujet\"\n  ```\n\n### 3. Demander les permissions\n\n- Utilise AskUserQuestion pour demander :\n  ```\n  Question: \"Quelles permissions gerer ? Format: VIEW,EDIT,DELETE,PUBLISH (separees par des virgules)\"\n  Header: \"Permissions\"\n  ```\n- Parse la liste des permissions\n- Genere les constantes correspondantes\n\n### 4. Demander la strategie de decision\n\n- Utilise AskUserQuestion pour demander :\n  ```\n  Question: \"Comment gerer un utilisateur non connecte ?\"\n  Header: \"Strategie\"\n  Options:\n    - \"Refuser (Recommande)\" : \"Un utilisateur non connecte est toujours refuse\"\n    - \"Permettre VIEW\" : \"Permettre la consultation aux anonymes, refuser le reste\"\n  ```\n\n### 5. Detecter le namespace du projet\n\n- Lis `composer.json` avec Read\n- Extrais le namespace depuis `autoload.psr-4`\n- Si non trouve, utilise `App` par defaut\n\n### 6. Generer le Voter\n\n- Cree le fichier `src\u002FSecurity\u002FVoter\u002F{VoterName}.php` avec Write\n\n```php\n\u003C?php\n\ndeclare(strict_types=1);\n\nnamespace {namespace}\\Security\\Voter;\n\nuse {namespace}\\Entity\\{Subject};\nuse Symfony\\Component\\Security\\Core\\Authentication\\Token\\TokenInterface;\nuse Symfony\\Component\\Security\\Core\\Authorization\\Voter\\Voter;\nuse Symfony\\Component\\Security\\Core\\User\\UserInterface;\n\nfinal class {VoterName} extends Voter\n{\n    public const string VIEW = 'VIEW';\n    public const string EDIT = 'EDIT';\n    public const string DELETE = 'DELETE';\n\n    protected function supports(string $attribute, mixed $subject): bool\n    {\n        return in_array($attribute, [self::VIEW, self::EDIT, self::DELETE], true)\n            && $subject instanceof {Subject};\n    }\n\n    protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool\n    {\n        $user = $token->getUser();\n\n        if (!$user instanceof UserInterface) {\n            return false;\n        }\n\n        \u002F** @var {Subject} $subject *\u002F\n\n        return match ($attribute) {\n            self::VIEW => $this->canView($subject, $user),\n            self::EDIT => $this->canEdit($subject, $user),\n            self::DELETE => $this->canDelete($subject, $user),\n            default => false,\n        };\n    }\n\n    private function canView({Subject} $subject, UserInterface $user): bool\n    {\n        \u002F\u002F Tout utilisateur connecte peut voir\n        return true;\n    }\n\n    private function canEdit({Subject} $subject, UserInterface $user): bool\n    {\n        \u002F\u002F Seul le proprietaire peut editer\n        return $subject->getOwner() === $user;\n    }\n\n    private function canDelete({Subject} $subject, UserInterface $user): bool\n    {\n        \u002F\u002F Seul le proprietaire peut supprimer\n        return $subject->getOwner() === $user;\n    }\n}\n```\n\n**Regles de generation :**\n- Classe `final` etendant `Voter`\n- Constantes `public const string` pour chaque permission\n- `supports()` verifie l'attribut ET le type du sujet\n- `voteOnAttribute()` utilise `match` pour dispatcher\n- Methodes privees pour chaque permission\n- Verification `$user instanceof UserInterface` pour les anonymes\n- Type-hint `@var` sur le sujet apres la verification de type\n\n### 7. Afficher le resume\n\nAffiche :\n```\nVoter {VoterName} genere avec succes\n\nFichiers crees :\n- src\u002FSecurity\u002FVoter\u002F{VoterName}.php\n\nPermissions :\n{liste des permissions}\n\nUtilisation dans un controleur :\n  $this->denyAccessUnlessGranted('EDIT', $post);\n\nUtilisation dans Twig :\n  {% if is_granted('EDIT', post) %}...{% endif %}\n\nUtilisation avec attribut :\n  #[IsGranted('EDIT', subject: 'post')]\n\nProchaines etapes :\n- Implementer la logique de chaque permission\n- Ajuster les regles d'acces selon le domaine metier\n```\n\n## Patterns appliques\n\n### Voter\n- Classe `final`, extends `Voter`\n- Constantes pour les permissions (string typed)\n- Pattern `match` pour le dispatch\n- Methodes privees par permission\n- Verification du type utilisateur\n\n## Notes\n- Preferer `Voter` a `VoterInterface` pour le typage automatique du sujet\n- Les constantes de permission sont reutilisables dans les controleurs et templates\n- `match` plutot que `switch` pour le dispatch des permissions\n- Toujours verifier `$user instanceof UserInterface` avant de traiter\n- Utiliser `#[IsGranted]` dans les controleurs plutot que `denyAccessUnlessGranted()` quand possible\n","# Symfony Voter Skill\n\nGenere un Voter Symfony pour le controle d'acces granulaire sur des objets ou des actions specifiques.\n\n## Variables\n- **{VoterName}** - Nom du voter en PascalCase (ex: PostVoter)\n- **{subject}** - Type d'objet sur lequel porte le vote\n- **{permissions}** - Liste des permissions gerees\n- **{namespace}** - Namespace du projet (defaut: App)\n\n## Outputs\n- `src\u002FSecurity\u002FVoter\u002F{VoterName}.php`\n\n## Instructions a Executer\n\n**IMPORTANT : Execute ce workflow etape par etape :**\n\n### 1. Demander le nom du voter\n\n- Utilise AskUserQuestion pour demander :\n  ```\n  Question: \"Quel est le nom du voter ? (ex: Post, Comment, Invoice)\"\n  Header: \"Voter\"\n  ```\n- Ajoute le suffixe `Voter` si absent\n\n### 2. Demander le sujet\n\n- Utilise AskUserQuestion pour demander :\n  ```\n  Question: \"Sur quel type d'objet porte le vote ? (ex: App\\Entity\\Post, App\\Entity\\Invoice)\"\n  Header: \"Sujet\"\n  ```\n\n### 3. Demander les permissions\n\n- Utilise AskUserQuestion pour demander :\n  ```\n  Question: \"Quelles permissions gerer ? Format: VIEW,EDIT,DELETE,PUBLISH (separees par des virgules)\"\n  Header: \"Permissions\"\n  ```\n- Parse la liste des permissions\n- Genere les constantes correspondantes\n\n### 4. Demander la strategie de decision\n\n- Utilise AskUserQuestion pour demander :\n  ```\n  Question: \"Comment gerer un utilisateur non connecte ?\"\n  Header: \"Strategie\"\n  Options:\n    - \"Refuser (Recommande)\" : \"Un utilisateur non connecte est toujours refuse\"\n    - \"Permettre VIEW\" : \"Permettre la consultation aux anonymes, refuser le reste\"\n  ```\n\n### 5. Detecter le namespace du projet\n\n- Lis `composer.json` avec Read\n- Extrais le namespace depuis `autoload.psr-4`\n- Si non trouve, utilise `App` par defaut\n\n### 6. Generer le Voter\n\n- Cree le fichier `src\u002FSecurity\u002FVoter\u002F{VoterName}.php` avec Write\n\n```php\n\u003C?php\n\ndeclare(strict_types=1);\n\nnamespace {namespace}\\Security\\Voter;\n\nuse {namespace}\\Entity\\{Subject};\nuse Symfony\\Component\\Security\\Core\\Authentication\\Token\\TokenInterface;\nuse Symfony\\Component\\Security\\Core\\Authorization\\Voter\\Voter;\nuse Symfony\\Component\\Security\\Core\\User\\UserInterface;\n\nfinal class {VoterName} extends Voter\n{\n    public const string VIEW = 'VIEW';\n    public const string EDIT = 'EDIT';\n    public const string DELETE = 'DELETE';\n\n    protected function supports(string $attribute, mixed $subject): bool\n    {\n        return in_array($attribute, [self::VIEW, self::EDIT, self::DELETE], true)\n            && $subject instanceof {Subject};\n    }\n\n    protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool\n    {\n        $user = $token->getUser();\n\n        if (!$user instanceof UserInterface) {\n            return false;\n        }\n\n        \u002F** @var {Subject} $subject *\u002F\n\n        return match ($attribute) {\n            self::VIEW => $this->canView($subject, $user),\n            self::EDIT => $this->canEdit($subject, $user),\n            self::DELETE => $this->canDelete($subject, $user),\n            default => false,\n        };\n    }\n\n    private function canView({Subject} $subject, UserInterface $user): bool\n    {\n        \u002F\u002F Tout utilisateur connecte peut voir\n        return true;\n    }\n\n    private function canEdit({Subject} $subject, UserInterface $user): bool\n    {\n        \u002F\u002F Seul le proprietaire peut editer\n        return $subject->getOwner() === $user;\n    }\n\n    private function canDelete({Subject} $subject, UserInterface $user): bool\n    {\n        \u002F\u002F Seul le proprietaire peut supprimer\n        return $subject->getOwner() === $user;\n    }\n}\n```\n\n**Regles de generation :**\n- Classe `final` etendant `Voter`\n- Constantes `public const string` pour chaque permission\n- `supports()` verifie l'attribut ET le type du sujet\n- `voteOnAttribute()` utilise `match` pour dispatcher\n- Methodes privees pour chaque permission\n- Verification `$user instanceof UserInterface` pour les anonymes\n- Type-hint `@var` sur le sujet apres la verification de type\n\n### 7. Afficher le resume\n\nAffiche :\n```\nVoter {VoterName} genere avec succes\n\nFichiers crees :\n- src\u002FSecurity\u002FVoter\u002F{VoterName}.php\n\nPermissions :\n{liste des permissions}\n\nUtilisation dans un controleur :\n  $this->denyAccessUnlessGranted('EDIT', $post);\n\nUtilisation dans Twig :\n  {% if is_granted('EDIT', post) %}...{% endif %}\n\nUtilisation avec attribut :\n  #[IsGranted('EDIT', subject: 'post')]\n\nProchaines etapes :\n- Implementer la logique de chaque permission\n- Ajuster les regles d'acces selon le domaine metier\n```\n\n## Patterns appliques\n\n### Voter\n- Classe `final`, extends `Voter`\n- Constantes pour les permissions (string typed)\n- Pattern `match` pour le dispatch\n- Methodes privees par permission\n- Verification du type utilisateur\n\n## Notes\n- Preferer `Voter` a `VoterInterface` pour le typage automatique du sujet\n- Les constantes de permission sont reutilisables dans les controleurs et templates\n- `match` plutot que `switch` pour le dispatch des permissions\n- Toujours verifier `$user instanceof UserInterface` avant de traiter\n- Utiliser `#[IsGranted]` dans les controleurs plutot que `denyAccessUnlessGranted()` quand possible\n",{"name":5,"model":29,"license":15,"version":30,"description":7,"allowed-tools":31},"sonnet","1.0.0",[32,19,33,34,35,36,37],"AskUserQuestion","Read","Write","Edit","Grep","Glob","main","bb0335112ea0991dc1dc6a29fa5a861397a5a6f6","symfony\u002Fskills\u002Fvoter","https:\u002F\u002Fgithub.com\u002Fatournayre\u002Fclaude-personas","https:\u002F\u002Fatournayre.github.io\u002Fclaude-personas\u002F","https:\u002F\u002Fgithub.com\u002Fatournayre","🔵, Back End Developer, @symfony  :heart:, @rectorphp :heart:,  \r\n:bug: fixer (creator sometimes 🤫), member of @afup","2026-03-02 23:42:20",{"source":8,"skillName":5,"commands":47,"prompt":57,"zipAvailable":58},[48,51,54],{"tool":49,"command":50},"npx","npx skills add atournayre\u002Fclaude-personas --skill \"symfony:voter\"",{"tool":52,"command":53},"bunx","bunx skills add atournayre\u002Fclaude-personas --skill \"symfony:voter\"",{"tool":55,"command":56},"pnpm","pnpm dlx skills add atournayre\u002Fclaude-personas --skill \"symfony:voter\"","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fatournayre\u002Fclaude-personas\u002Fblob\u002Fmain\u002Fsymfony\u002Fskills\u002Fvoter\u002FSKILL.md",true,[60,66,70,74,78,82,86,90,94,98,102,106,110,114,118,122,126,130,134,138,142,146,150,154,158,162,166,170,174,178,182,186,190,196,200,204],{"path":61,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":64,"sourceHtmlUrl":65},"ArtIntegration.md","REFERENCE","参考文档",2695,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FArtIntegration.md",{"path":67,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":68,"sourceHtmlUrl":69},"CriticalRules.md",3421,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FCriticalRules.md",{"path":71,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":72,"sourceHtmlUrl":73},"Patterns.md",2992,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FPatterns.md",{"path":75,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":76,"sourceHtmlUrl":77},"Tools\u002Fpackage.json",574,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002Fpackage.json",{"path":79,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":80,"sourceHtmlUrl":81},"Tools\u002FRef-3d.md",2290,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FRef-3d.md",{"path":83,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":84,"sourceHtmlUrl":85},"Tools\u002FRef-animations.md",790,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FRef-animations.md",{"path":87,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":88,"sourceHtmlUrl":89},"Tools\u002FRef-assets.md",1590,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FRef-assets.md",{"path":91,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":92,"sourceHtmlUrl":93},"Tools\u002FRef-audio.md",3822,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FRef-audio.md",{"path":95,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":96,"sourceHtmlUrl":97},"Tools\u002FRef-calculate-metadata.md",2920,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FRef-calculate-metadata.md",{"path":99,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":100,"sourceHtmlUrl":101},"Tools\u002FRef-can-decode.md",1587,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FRef-can-decode.md",{"path":103,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":104,"sourceHtmlUrl":105},"Tools\u002FRef-charts.md",1751,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FRef-charts.md",{"path":107,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":108,"sourceHtmlUrl":109},"Tools\u002FRef-compositions.md",3445,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FRef-compositions.md",{"path":111,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":112,"sourceHtmlUrl":113},"Tools\u002FRef-display-captions.md",3698,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FRef-display-captions.md",{"path":115,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":116,"sourceHtmlUrl":117},"Tools\u002FRef-extract-frames.md",5496,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FRef-extract-frames.md",{"path":119,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":120,"sourceHtmlUrl":121},"Tools\u002FRef-fonts.md",3484,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FRef-fonts.md",{"path":123,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":124,"sourceHtmlUrl":125},"Tools\u002FRef-get-audio-duration.md",1354,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FRef-get-audio-duration.md",{"path":127,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":128,"sourceHtmlUrl":129},"Tools\u002FRef-get-video-dimensions.md",1627,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FRef-get-video-dimensions.md",{"path":131,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":132,"sourceHtmlUrl":133},"Tools\u002FRef-get-video-duration.md",1341,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FRef-get-video-duration.md",{"path":135,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":136,"sourceHtmlUrl":137},"Tools\u002FRef-gifs.md",3868,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FRef-gifs.md",{"path":139,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":140,"sourceHtmlUrl":141},"Tools\u002FRef-images.md",2783,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FRef-images.md",{"path":143,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":144,"sourceHtmlUrl":145},"Tools\u002FRef-import-srt-captions.md",2113,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FRef-import-srt-captions.md",{"path":147,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":148,"sourceHtmlUrl":149},"Tools\u002FRef-lottie.md",1779,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FRef-lottie.md",{"path":151,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":152,"sourceHtmlUrl":153},"Tools\u002FRef-measuring-dom-nodes.md",974,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FRef-measuring-dom-nodes.md",{"path":155,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":156,"sourceHtmlUrl":157},"Tools\u002FRef-measuring-text.md",3000,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FRef-measuring-text.md",{"path":159,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":160,"sourceHtmlUrl":161},"Tools\u002FRef-sequencing.md",2389,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FRef-sequencing.md",{"path":163,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":164,"sourceHtmlUrl":165},"Tools\u002FRef-tailwind.md",422,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FRef-tailwind.md",{"path":167,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":168,"sourceHtmlUrl":169},"Tools\u002FRef-text-animations.md",700,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FRef-text-animations.md",{"path":171,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":172,"sourceHtmlUrl":173},"Tools\u002FRef-timing.md",3939,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FRef-timing.md",{"path":175,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":176,"sourceHtmlUrl":177},"Tools\u002FRef-transcribe-captions.md",850,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FRef-transcribe-captions.md",{"path":179,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":180,"sourceHtmlUrl":181},"Tools\u002FRef-transitions.md",3730,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FRef-transitions.md",{"path":183,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":184,"sourceHtmlUrl":185},"Tools\u002FRef-trimming.md",1210,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FRef-trimming.md",{"path":187,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":188,"sourceHtmlUrl":189},"Tools\u002FRef-videos.md",3509,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FRef-videos.md",{"path":191,"resourceType":192,"resourceTypeDesc":193,"sizeBytes":194,"sourceHtmlUrl":195},"Tools\u002FRender.ts","SCRIPT","脚本",9252,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FRender.ts",{"path":197,"resourceType":192,"resourceTypeDesc":193,"sizeBytes":198,"sourceHtmlUrl":199},"Tools\u002FTheme.ts",4531,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002FTheme.ts",{"path":201,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":202,"sourceHtmlUrl":203},"Tools\u002Ftsconfig.json",441,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FTools\u002Ftsconfig.json",{"path":205,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":206,"sourceHtmlUrl":207},"Workflows\u002FContentToAnimation.md",23455,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002FPersonal_AI_Infrastructure\u002FPacks\u002FMedia\u002Fsrc\u002FRemotion\u002FWorkflows\u002FContentToAnimation.md",[209,221,261,269,280,288],{"skillNo":210,"name":211,"nameZh":6,"description":212,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":213,"categories":214,"keywords":215,"publishedAt":24},"01M6CSZPR0W1K1S9BX3247CZJ1","twig:make-component","Génère un Twig Component Symfony UX (classe PHP + template Twig)","https:\u002F\u002Fgithub.com\u002Fatournayre\u002Fclaude-personas\u002Fblob\u002Fmain\u002Ftwig\u002Fskills\u002Fmake-component\u002FSKILL.md",[],[216,217,23,22,21,218,20,219,220,19],"twig:make component","component","ux","template","php",{"skillNo":222,"name":223,"nameZh":6,"description":224,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":225,"categories":226,"keywords":243,"publishedAt":24},"01M6CSZKCYYT4EAW4CDSTS3TSH","tester:ui-test","Teste automatiquement une interface utilisateur dans Chrome : navigation, clics, validation visuelle, tests fonctionnels, responsive, comportement et debug. Génère un rapport détaillé avec screenshots et optionnellement un GIF du parcours.","https:\u002F\u002Fgithub.com\u002Fatournayre\u002Fclaude-personas\u002Fblob\u002Fmain\u002Ftester\u002Fskills\u002Fui-test\u002FSKILL.md",[227,232,235,238],{"categoryNo":228,"categoryName":229,"taxonomyType":230,"taxonomyTypeDesc":231},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",{"categoryNo":233,"categoryName":234,"taxonomyType":230,"taxonomyTypeDesc":231},"01JBSEEDCAT000000000000016","图像与多媒体",{"categoryNo":236,"categoryName":237,"taxonomyType":230,"taxonomyTypeDesc":231},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":239,"categoryName":240,"taxonomyType":241,"taxonomyTypeDesc":242},"01JBSEEDCAT000000000000032","设计师","PROFESSION","适用职业",[244,245,246,247,248,249,250,251,252,253,254,255,19,256,257,258,21,22,259,260],"test","ui","tester","ui test","tester:ui test","plan","MCP","debug","create","update","validation","messages","tool","page","find","javascript","chrome",{"skillNo":262,"name":263,"nameZh":6,"description":264,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":265,"categories":266,"keywords":267,"publishedAt":24},"01M6CSZEJ3BNHCNZT18SYFGDME","symfony:message","Genere un Message Symfony Messenger avec son Handler pour traitement asynchrone","https:\u002F\u002Fgithub.com\u002Fatournayre\u002Fclaude-personas\u002Fblob\u002Fmain\u002Fsymfony\u002Fskills\u002Fmessage\u002FSKILL.md",[],[263,268,19,20,21,22,23],"message",{"skillNo":270,"name":271,"nameZh":6,"description":272,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":273,"categories":274,"keywords":278,"publishedAt":24},"01M6CSZBWSK9M6YMJX886BVWYF","symfony:mailer","Genere un email Symfony (TemplatedEmail ou Notification) avec template Twig","https:\u002F\u002Fgithub.com\u002Fatournayre\u002Fclaude-personas\u002Fblob\u002Fmain\u002Fsymfony\u002Fskills\u002Fmailer\u002FSKILL.md",[275],{"categoryNo":276,"categoryName":277,"taxonomyType":230,"taxonomyTypeDesc":231},"01JBSEEDCAT000000000000005","邮件与沟通",[271,19,279,219,20,21,22,23],"email",{"skillNo":281,"name":282,"nameZh":6,"description":283,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":284,"categories":285,"keywords":286,"publishedAt":24},"01M6CSZ9DXMYZ9HTRE8A324AWG","symfony:form","Genere un FormType Symfony avec contraintes de validation et options","https:\u002F\u002Fgithub.com\u002Fatournayre\u002Fclaude-personas\u002Fblob\u002Fmain\u002Fsymfony\u002Fskills\u002Fform\u002FSKILL.md",[],[282,254,19,20,21,22,23,287],"options",{"skillNo":289,"name":290,"nameZh":6,"description":291,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":292,"categories":293,"keywords":294,"publishedAt":24},"01M6CSZ73A0RY9G0WY091HJGYV","symfony:event","Genere un Event Symfony + EventSubscriber ou EventListener","https:\u002F\u002Fgithub.com\u002Fatournayre\u002Fclaude-personas\u002Fblob\u002Fmain\u002Fsymfony\u002Fskills\u002Fevent\u002FSKILL.md",[],[290,20,21,22,23,19]]