Résolution des sélecteurs en langage naturel
Un script .demo dit ce qu'il veut dire :
selectors:
composer = ask "la boîte de message en bas du chat"
Votre travail consiste à trouver ce que cela représente réellement dans le DOM en direct et à l'écrire dans
scripts/<nom>.selectors.json, afin que script.mjs puisse le compiler en un sélecteur ordinaire
et que driver.mjs puisse enregistrer une prise déterministe.
Vous êtes le résolveur. Rien dans le pipeline n'appelle un modèle — script.mjs
lit le fichier compagnon et refuse de compiler sans lui. La résolution a lieu ici,
une fois, avant l'enregistrement, car le timing de la prise est la timeline vidéo et un
aller-retour avec un modèle pendant une exécution créerait un temps mort dans la vidéo finale.
Avant de commencer
- L'application doit être accessible et, si elle nécessite une connexion, déjà connectée dans le Chrome de l'utilisateur. C'est tout l'intérêt de l'extension : elle atteint la session réelle, donc pas de rituel SSO ni de code email à remplir.
- Lisez d'abord le script. Vous avez besoin de l'en-tête
app:/target:pour l'URL, et desbeats, car les sélecteurs doivent être résolus dans l'ordre du flux (voir ci-dessous).
Procédure
-
Obtenez la liste de travail.
cd pipeline node script.mjs ../scripts/<nom>.demo --intentsJSON sur stdout : chaque sélecteur
ask, son intention et sa résolution actuelle (nullsi non résolu). Résolvez lesnull. Re-résolvez les autres seulement si l'utilisateur dit que le flux est cassé. -
Chargez les outils navigateur en UN SEUL appel ToolSearch, puis appelez
tabs_context_mcpavant toute autre chose. Créez un nouvel onglet avectabs_create_mcpsauf si l'utilisateur a pointé vers un existant. -
Parcourez le flux dans l'ordre des
beats. C'est la partie facile à mal faire : beaucoup de sélecteurs n'existent pas sur la page d'accueil. Un tableau de résultats n'apparaît qu'après l'envoi d'une question ; un contrôle "Stop" n'existe que pendant la génération. Parcourez donc lesbeatsdu script, en résolvant chaqueaskau moment où son élément est à l'écran.Données réelles. Avant toute étape qui crée, modifie, envoie ou supprime quoi que ce soit, arrêtez-vous et demandez à l'utilisateur.
feature.demoenvoie quatre messages à un compte Salesmessage réel. Survolez plutôt que de cliquer là où vous avez seulement besoin de localiser quelque chose, et ne cliquez jamais sur un contrôle destructeur pour "voir ce que c'est". -
Trouvez l'élément et choisissez la poignée la plus durable. Par ordre de préférence :
Préférer Pourquoi [data-testid=...],data-test,data-qaplacés là pour être sélectionnés ; survivent aux refontes un idstablebon sauf s'il semble généré ( :r3a:,id-1a2b)bouton/lien par nom accessible exact → btn:Nomle btn:du pilote correspond au texte,aria-labeletimg[alt]interne[aria-label=...],[role=...]stable et généralement sémantique structure sémantique ( main table,nav a[href="/equipes"])résiste aux changements de style N'utilisez jamais : les chaînes
nth-child, les noms de classe générés (css-1a2b3c, longues piles Tailwind), ou quoi que ce soit basé sur du contenu généré. Ce dernier point est le piège que toute cette fonctionnalité vise à éviter — la réponse d'un modèle est formulée différemment à chaque exécution, donc un sélecteur correspondant à son texte est un pile ou face. Ancrez-vous sur le conteneur ou sur un signal d'achèvement à la place. -
Vérifiez avant d'écrire. Pour chaque candidat, confirmez avec
javascript_toolqu'il correspond exactement à un élément et que cet élément est visible :const q = 'div[data-testid="chat-input"]'; const n = document.querySelectorAll(q); console.log(JSON.stringify({ q, count: n.length, visible: n[0] ? !!(n[0].offsetWidth || n[0].offsetHeight) : false, tag: n[0]?.tagName, editable: n[0]?.isContentEditable, }));Relisez-le avec
read_console_messages. Plus d'une correspondance, ou zéro, signifie continuez à chercher — n'écrivez pas un sélecteur que vous n'avez pas vu correspondre. -
Écrivez le fichier compagnon,
scripts/<nom>.selectors.json:{ "resolvedAgainst": "https://claude.ai/new", "resolvedAt": "2026-08-10T09:00:00Z", "resolvedBy": "claude-in-chrome", "selectors": { "la boîte de message en bas du chat": { "spec": "div[data-testid=\\