SLOPSHOPPER

mod-commun

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.

newstatusprocess
v0.1.0no licenseupdated 2026-10-15NeOzay/.claude/skills/mod-commun
A shopper browsing a rack in a slop shop
Source 2 files
hooks/register.ts 42 lines
1// 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}
42
types/index.d.ts 28 lines
1// 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