@xpulse/cli – Component Spec
Status: APPROVED · Aktualisiert März 2026
Übergeordnet: GLOBAL_concept-ecosystem.md
ADR: COMP_cli_adr-001-command-registration.md
Prinzip
@xpulse/cli ist ein Runner – kein Command-Owner.
Das Package stellt den Einstiegspunkt xpulse, den Command-Parser, die
Command-Basisklasse, Input/Output und die Autodiscovery bereit.
Welche Commands existieren, entscheidet jedes xPart selbst – durch
Mitliefern von Command-Klassen unter src/commands/.
1 xpulse doc:fetch chat -tag="v1.3.0" --dry-run -V 2 ↓ 3 @xpulse/cli discovert node_modules/ @xpulse/*/ src/commands/ *.js 4 findet FetchCommand (name = 'doc:fetch' aus configure()) 5 baut Input + Output auf, injiziert keepAlive 6 ruft new FetchCommand().run(input, output) auf 7 ↓ 8 FetchCommand.run() führt aus, gibt Exit Code zurück 9 ↓ 10 @xpulse/cli process.exit (code)
Dependencies
1 @xpulse /cli2 └── @xpulse /event ← cli :keepalive (intern, xParts sehen das nicht)
Command-Syntax
1 xpulse namespace :area :command [arguments] [-option="value" ] [--flag] [-V] [-q]
Teil
Beschreibung
Beispiel
namespace
Domäne des zuständigen xParts
repo, doc, project
area
Unterbereich (optional)
changelog, fetch
command
Aktion
generate, fetch, show
argument
Positionales Argument
chat
-option="value"
Benannte Option
-tag="v1.3.0"
--flag
Boolean, camelCase
--dry-run → dryRun: true
Command-Basisklasse (`src/command.js`)
xParts importieren Command und extenden sie in src/commands/.
Ein Command beschreibt sich in configure() und implementiert run().
Er weiß nichts von Events, Timeouts oder dem CLI selbst.
1 import { Command } from '@xpulse/cli/command' ;2 3 export class FetchCommand extends Command {4 5 configure ( ) { 6 this 7 .setName ('doc:fetch' ) 8 .setAlias ('df' ) 9 .setDescription ('Docs via git archive fetchen' ) 10 .setHelp ('Fetcht Docs aus einem xPulse Repo und legt sie lokal ab.' ) 11 .addArgument ('tool' , null , 'Name des Tools (z.B. chat)' ) 12 .addOption ('tag' , null , 'Git-Tag zum Fetchen' ) 13 .addFlag ('dry-run' , false , 'Nur simulieren, nichts schreiben' ); 14 } 15 16 async run (input, output ) { 17 const tool = input.getArgument ('tool' ); 18 const dryRun = input.getFlag ('dryRun' ); 19 20 while (!done) { 21 await doWork (); 22 this .keepAlive (); 23 } 24 25 output.success ('Fertig!' ); 26 return 0 ; 27 } 28 }
Fluent API
Methode
Beschreibung
setName(name)
Command-Name – namespace:area:command
setAlias(alias)
Kurzname (z.B. df für doc:fetch)
setDescription(text)
Kurzbeschreibung für xpulse --help
setHelp(text)
Längerer Text für xpulse --help <command>
addArgument(name, default, description)
Positionales Argument
addOption(name, default, description)
Benannte Option (-key=value)
addFlag(name, default, description)
Boolean-Flag (--flag)
Exit Codes
Code
Bedeutung
0
Erfolg
1
Fehler
2
Misuse – falsche oder fehlende Argumente
`keepAlive()`
Setzt den Timeout-Timer zurück. Wird von @xpulse/cli vor run() injiziert.
Kein Import von @xpulse/event nötig – der Command abstrahiert das vollständig.
1 input.getArgument ('tool' ) 2 input.getOption ('tag' ) 3 input.hasOption ('tag' ) 4 input.getFlag ('dryRun' ) 5 input.isVerbose () 6 input.isQuiet () 7 input.isAnsi ()
Output (`src/output.js`)
Respektiert --no-ansi und --quiet automatisch.
1 output.write ('...' ) 2 output.writeln ('...' ) 3 output.success ('...' ) 4 output.warn ('...' ) 5 output.error ('...' ) 6 output.table (headers, rows)
Globale CLI-Flags
Flag
Shortcut
Beschreibung
--help [command]
-h
CLI-Hilfe oder Command-Hilfe
--list
–
Alle Command-Namen, maschinenlesbar (einer pro Zeile)
--version
-v
Version ausgeben
--verbose
-V
Ausführliche Ausgabe → input.isVerbose()
--quiet
-q
Nur Errors → input.isQuiet()
--no-ansi
–
Farblos → input.isAnsi() = false
`--help` Verhalten
1 xpulse --help 2 xpulse --help doc:fetch 3
`--list` Verhalten
Maschinenlesbar – ein Command-Name pro Zeile, kein Ansi, kein Padding.
Autodiscovery
Suchreihenfolge beim Start:
1 1 . node_modules/@xpulse/ */src/ commands/*.js ← installierte xParts2 2 . src/commands/ *.js ← lokales Projekt
Beide Pfade werden zusammengeführt. Das lokale Projekt verhält sich damit
wie ein implizites xPart – kein Symlink, kein npm link nötig.
Timeout
Timer startet mit run(). Bei this.keepAlive() vollständig resettet.
Kein cli:done – run() resolved mit Exit Code, @xpulse/cli beendet den Prozess.
Ablauf
1 xpulse doc:fetch chat -tag="v1.3.0" --dry-run -V 2 ↓ 3 @xpulse/cli startet 4 → discovert node_modules/@xpulse/ */src/ commands/*.js 5 → parst Input → { name: 'doc:fetch' , args: { tag: 'v1.3.0' , dryRun: true }, 6 rawArguments: ['chat' ], verbose: true, ansi: true } 7 → findet FetchCommand (name = 'doc:fetch' ) 8 → baut Input + Output auf 9 → injiziert cmd._keepAliveFn = startTimer 10 → startTimer() 11 → exitCode = await cmd.run(input, output) 12 → clearTimeout + process.exit (exitCode)
Bekannte Commands (Beispiele)
Command
Bereitgestellt von
doc:fetch
@xpulse/doc
project:config:show
@xpulse/config
repo:changelog:generate
@xpulse/repo (TBD)
release:prepare
@xpulse/release (TBD)
Paket-Struktur
1 @xpulse /cli/2 bin/ 3 xpulse.js ← Einstiegspunkt : Autodiscovery, Parser, Input/Output, Timeout 4 src/ 5 command.js ← Command-Basisklasse mit configure (), keepAlive (), run () 6 input.js ← Input-Klasse : getArgument, getOption, getFlag, isVerbose ... 7 output.js ← Output-Klasse + interne Helfer (--help, --list, --version) 8 loader.js ← node_modules/@xpulse 9 parser.js ← CLI-Input → { name, args, rawArguments, verbose, quiet, ansi } 10 docs/ 11 index.md 12 de/ index, guide, api, schema 13 en/ index, guide, api, schema 14 test/ 15 command.test.js 16 input.test.js 17 parser.test.js 18 loader.test.js 19 README.md 20 package.json 21 xpulse.json
Installation & Verwendung
@xpulse/cli wird lokal im Projekt installiert – kein globales Install nötig.
Der Aufruf erfolgt immer via npx.
Wichtig: Die package.json des Projekts muss "type": "module" enthalten,
da das gesamte xPulse Ökosystem ESM verwendet:
1 { 2 "name" : "mein-projekt" , 3 "type" : "module" , 4 "dependencies" : { 5 "@xpulse/cli" : "^1.0.0" 6 } 7 }
Aufruf – immer mit `npx`
1 npx xpulse --version 2 npx xpulse --list 3 npx xpulse --help 4 npx xpulse --help doc:fetch 5 npx xpulse doc:fetch chat -tag="v1.3.0" --dry-run
npx nutzt immer die lokale Version aus node_modules/.bin/xpulse –
konsistent, kein Versions-Mismatch mit global installierten Versionen.
Argument vs. Option – Syntaxregeln
Typ
Definition
Aufruf
Argument
addArgument('name', ...)
npx xpulse cmd Johnny (positional)
Option
addOption('name', ...)
npx xpulse cmd -name="Johnny"
Flag
addFlag('dry-run', ...)
npx xpulse cmd --dry-run
Wichtig: Optionen müssen immer mit = übergeben werden:
1 npx xpulse cmd -name="Johnny" 2 npx xpulse cmd --name="Johnny" 3 npx xpulse cmd -name Johnny 4 npx xpulse cmd --name Johnny
Bei falschem Syntax gibt @xpulse/cli eine Warnung aus:
1 ! Ungültige Syntax: "--name Johnny" – meintest du --name ="Johnny" ? 2 "Johnny" wird als positionales Argument interpretiert.