Background
When an unknown command is parsed, clapp emits only the generic `command:*` event:
```js // dist/index.js:1250-1256 if (!this.matchedCommand && this.args[0]) { this.emit('command:'); const hasWildcardListener = this.listenerCount('command:') > 0; if (!hasWildcardListener) { this.showCommandNotFound(this.args[0]); } } ```
This means `cli.on('<prefix>:', handler)` is dead — the literal event `<prefix>:` is never emitted. Historically (CAC v6-style behavior) listeners like `cli.on('migrate:*', ...)` would fire for any unknown subcommand of `migrate`, letting commands provide their own "did-you-mean" or contextual error path.
This blocks Stacks framework default templates (see stacksjs/stacks#893): `app/Commands/Inspire.ts` and `app/Listeners/Console.ts` both register `cli.on('inspire:*', ...)` expecting it to fire on `buddy inspire:bogus` — it doesn't. The user just sees the generic "Command not found" output.
Proposed behavior
When emitting for an unknown command:
```js const arg = this.args[0] this.emit('command:*', arg)
if (typeof arg === 'string' && arg.includes(':')) { const prefix = arg.slice(0, arg.indexOf(':')) this.emit(`${prefix}:*`, arg) }
const hasWildcardListener = this.listenerCount('command:') > 0 || (typeof arg === 'string' && arg.includes(':') && this.listenerCount(`${arg.slice(0, arg.indexOf(':'))}:`) > 0)
if (!hasWildcardListener) { this.showCommandNotFound(this.args[0]) } ```
Two small changes from current:
- Pass the unknown-command string as a payload to `command:*` (today's emit has no payload, which is also a footgun — handlers can't see which command was unknown without reading `cli.args`).
- When the unknown command contains a colon, also emit `${prefix}:*` with the same payload, and count those listeners toward the "someone handled it" check so we don't show the default not-found message.
Acceptance
- `cli.on('foo:*', handler)` fires for unknown `foo:bar` / `foo:baz` commands; handler receives the unknown command string
- `cli.on('command:*', handler)` still fires (backward compatible — it's the generic catchall)
- Default "Command not found" only prints when no wildcard listener (generic OR prefix-scoped) is registered
- Tests covering: prefix wildcard fires, generic wildcard fires, both registered (both fire), neither registered (not-found prints)
Once this lands, the Stacks-side fix on #893 is a no-op — the existing templates start working.