Ajoute $.commande au moteur : le lancement d'une commande, partagé entre les mods, qui rend sa sortie et dit un échec sur la ligne d'état.

hooks/register.ts 42 lines1// Mod commun : ajoute `$.commande` au moteur, le code que les autres mods partagent.
2//
3// UN NOM SUR `$` : un import hors du dossier d'un plugin est refusé au chargement. Un nom ajouté à
4// `$` par `engine.create` est la seule voie par laquelle un mod appelle le code d'un autre.
5//
6// ÉCHEC OUVERT : `lancer` ne lève jamais. Un code non nul ou une exception se dit sur la ligne
7// d'état, préfixé du mod appelant, et `lancer` rend `undefined` : un rappel n'empêche ni une
8// session ni un sous-agent de démarrer.
9import type { Register } from 'claude-code'
10import type { CommandeLancer } from '../types/index.d.ts'
11
12/** Ajoute `$.commande` au moteur, à chaque construction de `$`. */
13export const register: Register = on => {
14 on('engine.create', async (_$, e, next) => {
15 // `lancer` appelle le moteur par le `$` que les étapes inférieures ont bâti : à
16 // `engine.create`, le `$` du hook est vide. Le validateur refuse que `built` passe en
17 // argument : la méthode vit donc ici, où `built.process.run` s'écrit tel quel.
18 const built = await next(e)
19 const lancer = async ({
20 argv,
21 cwd,
22 env,
23 source,
24 }: CommandeLancer): Promise<string | undefined> => {
25 try {
26 const ran = await built.process.run(argv, { cwd, env })
27 if (ran.exitCode !== 0) {
28 built.ui.status(`${source} : ${argv[0]} rend ${ran.exitCode} ${ran.stderr.slice(0, 60)}`)
29 return undefined
30 }
31 // Une commande qui sort en 0 sans rien rendre reste muette : rien à verser n'est pas une
32 // erreur.
33 return ran.stdout.trim() || undefined
34 } catch (erreur) {
35 built.ui.status(`${source} : ${String(erreur).slice(0, 60)}`)
36 return undefined
37 }
38 }
39 return { ...built, commande: { lancer } }
40 })
41}
42types/index.d.ts 28 lines1// Contrat de `$.commande`, le nom que le mod commun ajoute au moteur. Un mod client le type par
2// ce fichier ; à l'exécution, il appelle `$.commande.lancer`, avec ou sans `dependencies`.
3
4/** Ce que `$.commande.lancer` reçoit : la commande à lancer, et le mod qui la lance. */
5export type CommandeLancer = {
6 /** La commande et ses arguments, sans shell ; le premier se résout par le `PATH`. */
7 argv: readonly string[]
8 /** Le répertoire de travail de la commande ; absent, celui de la session. */
9 cwd?: string
10 /** Les variables posées sur l'environnement de la session. */
11 env?: Record<string, string>
12 /** Le nom du mod appelant, en tête de la ligne d'état qui dit un échec. */
13 source: string
14}
15
16/** Le lancement d'une commande, partagé entre les mods. */
17export type Commande = {
18 /** Lance la commande et rend sa sortie sans ses blancs ; rien si elle est vide ou échoue. */
19 lancer: (input: CommandeLancer) => Promise<string | undefined>
20}
21
22declare module 'claude-code' {
23 interface EngineInterface {
24 /** Le lancement d'une commande, ajouté par le mod commun. */
25 commande: Commande
26 }
27}
28