@@ -432,21 +432,10 @@ export async function executeSlashCommand(
432432 }
433433
434434 const { command, args } = resolution ;
435- if ( ! command . action ) {
436- if ( command . subCommands && command . subCommands . length > 0 ) {
437- const helpText = `Command '/${ command . name } ' requires a subcommand. Available:\n${ command . subCommands
438- . map ( ( sc ) => ` - ${ sc . name } : ${ sc . description || '' } ` )
439- . join ( '\n' ) } `;
440- return { kind : 'message' , messageType : 'info' , content : helpText } ;
441- }
442- return { kind : 'handled' } ;
443- }
444435
445- // Telemetry parity (ink slashCommandProcessor): skill invocations feed
446- // /stats skills via recordSkillInvocation + recordAutoSkillCommandUsage,
447- // and every executed command logs a SUCCESS/ERROR slash-command event.
448- const isSkillCommand = command . kind === CommandKind . SKILL ;
449- const skillName = command . skillDetail ?. name ?? command . name ;
436+ // Telemetry parity (ink slashCommandProcessor): every executed command
437+ // logs a SUCCESS/ERROR slash-command event, including parent commands
438+ // that return early (help listing / bare handled).
450439 const subcommand =
451440 resolution . canonicalPath . length > 1
452441 ? resolution . canonicalPath . slice ( 1 ) . join ( ' ' )
@@ -462,6 +451,23 @@ export async function executeSlashCommand(
462451 } ) ,
463452 ) ;
464453 } ;
454+
455+ if ( ! command . action ) {
456+ if ( command . subCommands && command . subCommands . length > 0 ) {
457+ const helpText = `Command '/${ command . name } ' requires a subcommand. Available:\n${ command . subCommands
458+ . map ( ( sc ) => ` - ${ sc . name } : ${ sc . description || '' } ` )
459+ . join ( '\n' ) } `;
460+ logEvent ( SlashCommandStatus . SUCCESS ) ;
461+ return { kind : 'message' , messageType : 'info' , content : helpText } ;
462+ }
463+ logEvent ( SlashCommandStatus . SUCCESS ) ;
464+ return { kind : 'handled' } ;
465+ }
466+
467+ // Skill-specific telemetry: skill invocations feed /stats skills via
468+ // recordSkillInvocation + recordAutoSkillCommandUsage.
469+ const isSkillCommand = command . kind === CommandKind . SKILL ;
470+ const skillName = command . skillDetail ?. name ?? command . name ;
465471 const recordSkill = ( success : boolean ) => {
466472 if ( env . config && isSkillCommand ) {
467473 recordSkillInvocation ( env . config , { skillName, success } ) ;
@@ -542,16 +548,18 @@ export async function executeSlashCommand(
542548 let result : SlashCommandActionReturn | void ;
543549 if ( env . abortSignal ) {
544550 const signal = env . abortSignal ;
545- // An already-aborted signal never fires addEventListener('abort'),
546- // so resolve immediately in that case instead of hanging.
547- const aborted = signal . aborted
548- ? Promise . resolve ( undefined )
549- : new Promise < undefined > ( ( resolve ) => {
550- signal . addEventListener ( 'abort' , ( ) => resolve ( undefined ) , {
551- once : true ,
552- } ) ;
551+ if ( signal . aborted ) {
552+ // Already aborted: skip the action entirely — its side effects
553+ // (clear, persist, addItem) must not run on a cancelled submission.
554+ result = undefined ;
555+ } else {
556+ const aborted = new Promise < undefined > ( ( resolve ) => {
557+ signal . addEventListener ( 'abort' , ( ) => resolve ( undefined ) , {
558+ once : true ,
553559 } ) ;
554- result = await Promise . race ( [ command . action ( context , args ) , aborted ] ) ;
560+ } ) ;
561+ result = await Promise . race ( [ command . action ( context , args ) , aborted ] ) ;
562+ }
555563 } else {
556564 result = await command . action ( context , args ) ;
557565 }
0 commit comments