@moostjs/event-cli 0.6.6 → 0.6.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moostjs/event-cli",
3
- "version": "0.6.6",
3
+ "version": "0.6.7",
4
4
  "description": "@moostjs/event-cli",
5
5
  "keywords": [
6
6
  "composables",
@@ -21,13 +21,8 @@
21
21
  "url": "git+https://github.com/moostjs/moostjs.git",
22
22
  "directory": "packages/event-cli"
23
23
  },
24
- "bin": {
25
- "moostjs-event-cli-skill": "./scripts/setup-skills.js"
26
- },
27
24
  "files": [
28
- "dist",
29
- "skills",
30
- "scripts/setup-skills.js"
25
+ "dist"
31
26
  ],
32
27
  "type": "module",
33
28
  "sideEffects": false,
@@ -43,19 +38,18 @@
43
38
  }
44
39
  },
45
40
  "dependencies": {
46
- "@wooksjs/event-cli": "^0.7.8"
41
+ "@wooksjs/event-cli": "^0.7.9"
47
42
  },
48
43
  "devDependencies": {
49
44
  "vitest": "3.2.4"
50
45
  },
51
46
  "peerDependencies": {
52
- "@wooksjs/event-core": "^0.7.8",
53
- "wooks": "^0.7.8",
54
- "moost": "^0.6.6"
47
+ "@wooksjs/event-core": "^0.7.9",
48
+ "wooks": "^0.7.9",
49
+ "moost": "^0.6.7"
55
50
  },
56
51
  "scripts": {
57
52
  "pub": "pnpm publish --access public",
58
- "test": "vitest",
59
- "setup-skills": "node ./scripts/setup-skills.js"
53
+ "test": "vitest"
60
54
  }
61
55
  }
@@ -1,78 +0,0 @@
1
- #!/usr/bin/env node
2
- /* prettier-ignore */
3
- import fs from 'fs'
4
- import path from 'path'
5
- import os from 'os'
6
- import { fileURLToPath } from 'url'
7
-
8
- const __dirname = path.dirname(fileURLToPath(import.meta.url))
9
-
10
- const SKILL_NAME = 'moostjs-event-cli'
11
- const SKILL_SRC = path.join(__dirname, '..', 'skills', SKILL_NAME)
12
-
13
- if (!fs.existsSync(SKILL_SRC)) {
14
- console.error(`No skills found at ${SKILL_SRC}`)
15
- console.error('Add your SKILL.md files to the skills/' + SKILL_NAME + '/ directory first.')
16
- process.exit(1)
17
- }
18
-
19
- const AGENTS = {
20
- 'Claude Code': { dir: '.claude/skills', global: path.join(os.homedir(), '.claude', 'skills') },
21
- 'Cursor': { dir: '.cursor/skills', global: path.join(os.homedir(), '.cursor', 'skills') },
22
- 'Windsurf': { dir: '.windsurf/skills', global: path.join(os.homedir(), '.windsurf', 'skills') },
23
- 'Codex': { dir: '.codex/skills', global: path.join(os.homedir(), '.codex', 'skills') },
24
- 'OpenCode': { dir: '.opencode/skills', global: path.join(os.homedir(), '.opencode', 'skills') },
25
- }
26
-
27
- const args = process.argv.slice(2)
28
- const isGlobal = args.includes('--global') || args.includes('-g')
29
- const isPostinstall = args.includes('--postinstall')
30
- let installed = 0, skipped = 0
31
- const installedDirs = []
32
-
33
- for (const [agentName, cfg] of Object.entries(AGENTS)) {
34
- const targetBase = isGlobal ? cfg.global : path.join(process.cwd(), cfg.dir)
35
- const agentRootDir = path.dirname(cfg.global) // Check if the agent has ever been installed globally
36
-
37
- // In postinstall mode: silently skip agents that aren't set up globally
38
- if (isPostinstall || isGlobal) {
39
- if (!fs.existsSync(agentRootDir)) { skipped++; continue }
40
- }
41
-
42
- const dest = path.join(targetBase, SKILL_NAME)
43
- try {
44
- fs.mkdirSync(dest, { recursive: true })
45
- fs.cpSync(SKILL_SRC, dest, { recursive: true })
46
- console.log(`✅ ${agentName}: installed to ${dest}`)
47
- installed++
48
- if (!isGlobal) installedDirs.push(cfg.dir + '/' + SKILL_NAME)
49
- } catch (err) {
50
- console.warn(`⚠️ ${agentName}: failed — ${err.message}`)
51
- }
52
- }
53
-
54
- // Add locally-installed skill dirs to .gitignore
55
- if (!isGlobal && installedDirs.length > 0) {
56
- const gitignorePath = path.join(process.cwd(), '.gitignore')
57
- let gitignoreContent = ''
58
- try { gitignoreContent = fs.readFileSync(gitignorePath, 'utf8') } catch {}
59
- const linesToAdd = installedDirs.filter(d => !gitignoreContent.includes(d))
60
- if (linesToAdd.length > 0) {
61
- const hasHeader = gitignoreContent.includes('# AI agent skills')
62
- const block = (gitignoreContent && !gitignoreContent.endsWith('\n') ? '\n' : '')
63
- + (hasHeader ? '' : '\n# AI agent skills (auto-generated by setup-skills)\n')
64
- + linesToAdd.join('\n') + '\n'
65
- fs.appendFileSync(gitignorePath, block)
66
- console.log(`📝 Added ${linesToAdd.length} entries to .gitignore`)
67
- }
68
- }
69
-
70
- if (installed === 0 && isPostinstall) {
71
- // Silence is fine — no agents present, nothing to do
72
- } else if (installed === 0 && skipped === Object.keys(AGENTS).length) {
73
- console.log('No agent directories detected. Try --global or run without it for project-local install.')
74
- } else if (installed === 0) {
75
- console.log('Nothing installed. Run without --global to install project-locally.')
76
- } else {
77
- console.log(`\n✨ Done! Restart your AI agent to pick up the "${SKILL_NAME}" skill.`)
78
- }
@@ -1,38 +0,0 @@
1
- ---
2
- name: moostjs-event-cli
3
- description: Use this skill when working with @moostjs/event-cli — to build CLI applications with CliApp or MoostCli, define commands with @Cli(), parse flags and options with @CliOption(), organize commands into controllers with @Controller() and @ImportController(), generate auto-help with cliHelpInterceptor() and @CliHelpInterceptor, define command aliases with @CliAlias(), add usage examples with @CliExample(), set global options with @CliGlobalOption(), use composables like useCliOption()/useCliOptions()/useCliHelp(), combine CLI with HTTP via multi-adapter setup, or access cliKind for event type identification.
4
- ---
5
-
6
- # @moostjs/event-cli
7
-
8
- Moost adapter for building decorator-driven CLI applications. Wraps `@wooksjs/event-cli` and integrates with Moost's DI, interceptors, and pipes.
9
-
10
- ## How to use this skill
11
-
12
- Read the domain file that matches the task. Do not load all files — only what you need.
13
-
14
- | Domain | File | Load when... |
15
- |--------|------|------------|
16
- | Core concepts & setup | [core.md](core.md) | Starting a new project, understanding the mental model, installing the package |
17
- | Commands | [commands.md](commands.md) | Defining commands with @Cli(), command paths, positional arguments, aliases, examples |
18
- | Options & arguments | [options.md](options.md) | Adding flags with @CliOption(), boolean options, @Description, @Optional, @Value, @CliGlobalOption |
19
- | Controllers | [controllers.md](controllers.md) | Organizing commands into groups, nesting with @ImportController, path composition |
20
- | Help system | [help.md](help.md) | Configuring auto-help, cliHelpInterceptor, @CliHelpInterceptor, unknown command handling |
21
- | Interceptors | [interceptors.md](interceptors.md) | Adding guards, error handlers, timing, cross-cutting CLI logic |
22
- | Advanced | [advanced.md](advanced.md) | Manual MoostCli setup, Wooks composables, DI scopes, multi-adapter CLI+HTTP, cliKind |
23
-
24
- ## Quick reference
25
-
26
- ```ts
27
- // Most common imports
28
- import { CliApp, Cli, CliOption, CliAlias, CliExample, CliGlobalOption } from '@moostjs/event-cli'
29
- import { Controller, Param, Description, Optional, Intercept } from '@moostjs/event-cli'
30
- import { cliHelpInterceptor, CliHelpInterceptor } from '@moostjs/event-cli'
31
- import { MoostCli, cliKind } from '@moostjs/event-cli'
32
-
33
- // Minimal app
34
- new CliApp()
35
- .controllers(AppController)
36
- .useHelp({ name: 'my-cli' })
37
- .start()
38
- ```
@@ -1,202 +0,0 @@
1
- # Advanced — @moostjs/event-cli
2
-
3
- > Manual adapter setup, Wooks composables, DI scopes, multi-adapter patterns, and cliKind.
4
-
5
- ## Concepts
6
-
7
- `CliApp` handles the common case. For advanced scenarios — sharing a Moost instance across adapters, using Wooks composables directly, or fine-tuning DI — use `MoostCli` as a standalone adapter.
8
-
9
- Wooks composables are functions that access the current CLI event context (via AsyncLocalStorage). They work anywhere in the handler call chain — in handlers, services, or interceptors.
10
-
11
- ## API Reference
12
-
13
- ### `MoostCli`
14
-
15
- Low-level CLI adapter class implementing `TMoostAdapter<TCliHandlerMeta>`.
16
-
17
- Constructor options (`TMoostCliOpts`):
18
- - `wooksCli?: WooksCli | TWooksCliOptions` — a WooksCli instance or configuration options
19
- - `debug?: boolean` — enable internal logging (default: `false`)
20
- - `globalCliOptions?: { keys: string[]; description?: string; type?: TFunction }[]` — options shown in help for every command
21
-
22
- ```ts
23
- import { MoostCli, cliHelpInterceptor } from '@moostjs/event-cli'
24
- import { Moost } from 'moost'
25
-
26
- const app = new Moost()
27
-
28
- app.applyGlobalInterceptors(
29
- cliHelpInterceptor({ colors: true, lookupLevel: 3 }),
30
- )
31
-
32
- app.registerControllers(AppController)
33
-
34
- app.adapter(new MoostCli({
35
- wooksCli: {
36
- cliHelp: { name: 'my-cli' },
37
- },
38
- globalCliOptions: [
39
- { keys: ['help'], description: 'Display instructions for the command.' },
40
- ],
41
- }))
42
-
43
- void app.init()
44
- ```
45
-
46
- ### When to use MoostCli vs CliApp
47
-
48
- | Use case | Recommendation |
49
- |----------|---------------|
50
- | Standard CLI app | `CliApp` — less boilerplate |
51
- | Pre-configured WooksCli instance | `MoostCli` — pass your own instance |
52
- | Multiple adapters (CLI + HTTP) | `MoostCli` — attach to existing Moost instance |
53
- | Custom error handling on WooksCli | `MoostCli` — configure `wooksCli.onError` |
54
-
55
- ### `cliKind`
56
-
57
- Event type identifier for CLI events, re-exported from `@wooksjs/event-cli`. Useful when writing adapters or composables that distinguish event types.
58
-
59
- ```ts
60
- import { cliKind } from '@moostjs/event-cli'
61
- ```
62
-
63
- ## Wooks composables
64
-
65
- These are from `@wooksjs/event-cli` and `@wooksjs/event-core`. They work inside any handler or service during a CLI event.
66
-
67
- ### `useCliOption(name: string)`
68
-
69
- Read a single option value. This is what `@CliOption()` uses under the hood.
70
-
71
- ```ts
72
- import { useCliOption } from '@wooksjs/event-cli'
73
-
74
- @Cli('build')
75
- build() {
76
- const verbose = useCliOption('verbose')
77
- if (verbose) console.log('Verbose mode on')
78
- return 'Building...'
79
- }
80
- ```
81
-
82
- ### `useCliOptions()`
83
-
84
- Read all parsed CLI options at once.
85
-
86
- ```ts
87
- import { useCliOptions } from '@wooksjs/event-cli'
88
-
89
- @Cli('build')
90
- build() {
91
- const opts = useCliOptions()
92
- // opts: { verbose: true, target: 'production', ... }
93
- return `Building with ${JSON.stringify(opts)}`
94
- }
95
- ```
96
-
97
- ### `useRouteParams()`
98
-
99
- Read positional arguments. This is what `@Param()` uses under the hood.
100
-
101
- ```ts
102
- import { useRouteParams } from '@wooksjs/event-core'
103
-
104
- @Cli('deploy/:env')
105
- deploy() {
106
- const params = useRouteParams()
107
- return `Deploying to ${params.get('env')}`
108
- }
109
- ```
110
-
111
- ### `useCliHelp()`
112
-
113
- Access the help renderer programmatically.
114
-
115
- ```ts
116
- import { useCliHelp } from '@wooksjs/event-cli'
117
-
118
- @Cli('info')
119
- info() {
120
- const { print } = useCliHelp()
121
- print(true) // print help with colors
122
- }
123
- ```
124
-
125
- ## Common Patterns
126
-
127
- ### Pattern: Multi-adapter CLI + HTTP
128
-
129
- A single Moost instance serving both CLI commands and HTTP routes:
130
-
131
- ```ts
132
- import { Cli, Controller, Param } from '@moostjs/event-cli'
133
- import { Get } from '@moostjs/event-http'
134
-
135
- @Controller('health')
136
- export class HealthController {
137
- @Cli('check')
138
- @Get('check')
139
- check() {
140
- return { status: 'ok', uptime: process.uptime() }
141
- }
142
- }
143
- ```
144
-
145
- Wire up both adapters:
146
-
147
- ```ts
148
- import { MoostCli } from '@moostjs/event-cli'
149
- import { MoostHttp } from '@moostjs/event-http'
150
- import { Moost } from 'moost'
151
-
152
- const app = new Moost()
153
- app.registerControllers(HealthController)
154
-
155
- app.adapter(new MoostCli())
156
- app.adapter(new MoostHttp()).listen(3000)
157
-
158
- void app.init()
159
- ```
160
-
161
- Now `my-cli health check` and `GET /health/check` both invoke the same handler.
162
-
163
- ### Pattern: DI scopes in CLI
164
-
165
- ```ts
166
- import { Injectable } from 'moost'
167
-
168
- @Injectable('SINGLETON')
169
- export class ConfigService {
170
- readonly env = process.env.NODE_ENV ?? 'development'
171
- }
172
-
173
- @Injectable('FOR_EVENT')
174
- export class CommandState {
175
- startedAt = Date.now()
176
- }
177
- ```
178
-
179
- - `SINGLETON` — one instance for the app lifetime (shared config, DB connections)
180
- - `FOR_EVENT` — new instance per CLI command execution (command-specific state)
181
-
182
- Controllers default to `SINGLETON` scope, which is correct for most CLI apps.
183
-
184
- ## Integration
185
-
186
- - Wooks composables (`useCliOption`, `useCliOptions`, etc.) are imported from `@wooksjs/event-cli`
187
- - Route param composables (`useRouteParams`) come from `@wooksjs/event-core`
188
- - DI decorators (`@Injectable`, `@Inject`, `@Provide`) come from `moost`
189
- - HTTP decorators (`@Get`, `@Post`, etc.) come from `@moostjs/event-http`
190
-
191
- ## Best Practices
192
-
193
- - Use `MoostCli` only when `CliApp` isn't flexible enough
194
- - When `debug` is `false` (default), DI container logging is suppressed for clean terminal output
195
- - In multi-adapter setups, decorate shared methods with both `@Cli()` and `@Get()`/`@Post()`
196
- - Composables work in interceptors and services too — not just handlers
197
-
198
- ## Gotchas
199
-
200
- - `MoostCli` calls `app.init()` — don't call `init()` yourself after attaching the adapter in `CliApp.start()`
201
- - Wooks composables only work inside an active event context (during handler execution)
202
- - When using a custom `WooksCli` instance with `MoostCli`, the `onNotFound` callback is overridden by the adapter
@@ -1,152 +0,0 @@
1
- # Commands — @moostjs/event-cli
2
-
3
- > Defining CLI commands, command paths, positional arguments, aliases, and examples.
4
-
5
- ## Concepts
6
-
7
- Every CLI command is a method decorated with `@Cli()`. The decorator argument defines the command path — the words a user types to invoke it. Command paths support positional arguments (`:param` segments), space or slash separation, and escaped colons.
8
-
9
- ## API Reference
10
-
11
- ### `@Cli(path?: string)`
12
-
13
- Method decorator. Registers the method as a CLI command handler.
14
-
15
- - `path` — command path with segments separated by spaces or `/`. Segments starting with `:` are positional arguments. If omitted, the method name is used as the command.
16
-
17
- ```ts
18
- import { Cli, Controller } from '@moostjs/event-cli'
19
-
20
- @Controller()
21
- export class AppController {
22
- @Cli('deploy/:env')
23
- deploy(@Param('env') env: string) {
24
- return `Deploying to ${env}...`
25
- }
26
- }
27
- ```
28
-
29
- ### `@CliAlias(alias: string)`
30
-
31
- Method decorator. Defines an alternative name for a command. Stack multiple decorators for multiple aliases.
32
-
33
- ```ts
34
- import { Cli, CliAlias, Controller, Param } from '@moostjs/event-cli'
35
-
36
- @Controller()
37
- export class AppController {
38
- @Cli('install/:package')
39
- @CliAlias('i/:package')
40
- @CliAlias('add/:package')
41
- install(@Param('package') pkg: string) {
42
- return `Installing ${pkg}...`
43
- }
44
- }
45
- ```
46
-
47
- All three invoke the same handler:
48
- ```bash
49
- my-cli install lodash
50
- my-cli i lodash
51
- my-cli add lodash
52
- ```
53
-
54
- ### `@CliExample(cmd: string, description?: string)`
55
-
56
- Method decorator. Adds a usage example displayed in `--help` output. Stack multiple decorators for multiple examples.
57
-
58
- ```ts
59
- import { Cli, CliExample, Controller } from '@moostjs/event-cli'
60
-
61
- @Controller()
62
- export class DeployController {
63
- @Cli('deploy/:env')
64
- @CliExample('deploy dev -p my-app', 'Deploy to development')
65
- @CliExample('deploy prod --verbose', 'Deploy with verbose logging')
66
- deploy(@Param('env') env: string) {
67
- return `Deploying to ${env}`
68
- }
69
- }
70
- ```
71
-
72
- ### `@Param(name: string)`
73
-
74
- Parameter decorator (re-exported from `moost`). Extracts a positional argument from a `:name` segment in the command path.
75
-
76
- ```ts
77
- @Cli('copy/:source/:dest')
78
- copy(
79
- @Param('source') source: string,
80
- @Param('dest') dest: string,
81
- ) {
82
- return `Copying ${source} to ${dest}`
83
- }
84
- ```
85
-
86
- ```bash
87
- my-cli copy fileA.txt fileB.txt
88
- ```
89
-
90
- ## Common Patterns
91
-
92
- ### Pattern: Space-separated vs slash-separated paths
93
-
94
- Both notations are equivalent. The user always types space-separated words:
95
-
96
- ```ts
97
- // These are identical:
98
- @Cli('config set')
99
- @Cli('config/set')
100
- ```
101
-
102
- ```bash
103
- my-cli config set
104
- ```
105
-
106
- ### Pattern: Default path from method name
107
-
108
- When `@Cli()` is called without arguments, the method name becomes the command:
109
-
110
- ```ts
111
- @Cli()
112
- status() {
113
- return 'All systems operational'
114
- }
115
- ```
116
-
117
- ```bash
118
- my-cli status
119
- ```
120
-
121
- ### Pattern: Escaped colons
122
-
123
- If a command contains a literal colon (like `build:dev`), escape it:
124
-
125
- ```ts
126
- @Cli('build\\:dev')
127
- buildDev() {
128
- return 'Building for dev...'
129
- }
130
- ```
131
-
132
- ```bash
133
- my-cli build:dev
134
- ```
135
-
136
- ### Pattern: Return values
137
-
138
- Whatever a handler returns is printed to stdout. Return a string for plain text, or an object/array for JSON:
139
-
140
- ```ts
141
- @Cli('info')
142
- info() {
143
- return { name: 'my-app', version: '1.0.0' }
144
- }
145
- ```
146
-
147
- ## Gotchas
148
-
149
- - Command paths with `:` that are NOT parameters must be escaped with `\\:` (e.g. `'build\\:dev'`)
150
- - Space-separated and slash-separated paths are equivalent internally — `'config set'` becomes `'config/set'`
151
- - If you omit the path in `@Cli()`, the method name is used as the command name
152
- - Multiple `@Cli()` decorators on the same method register multiple commands pointing to the same handler
@@ -1,142 +0,0 @@
1
- # Controllers — @moostjs/event-cli
2
-
3
- > Organizing commands into logical groups with prefixes and nesting.
4
-
5
- ## Concepts
6
-
7
- Controllers group related commands under a shared prefix, creating natural command hierarchies like `git remote add` or `docker compose up`. A controller is a class with `@Controller(prefix?)`. The prefix is prepended to all command paths inside it.
8
-
9
- Controllers can be nested via `@ImportController()` — the child's prefix appends to the parent's, building multi-level command trees.
10
-
11
- ## API Reference
12
-
13
- ### `@Controller(prefix?: string)`
14
-
15
- Class decorator (re-exported from `moost`). Marks a class as a CLI controller. The optional `prefix` sets a command prefix for all methods inside.
16
-
17
- ```ts
18
- import { Cli, Controller, Param } from '@moostjs/event-cli'
19
-
20
- @Controller('user')
21
- export class UserController {
22
- @Cli('create/:name')
23
- create(@Param('name') name: string) {
24
- return `Created user: ${name}`
25
- }
26
-
27
- @Cli('delete/:name')
28
- delete(@Param('name') name: string) {
29
- return `Deleted user: ${name}`
30
- }
31
- }
32
- ```
33
-
34
- ```bash
35
- my-cli user create Alice
36
- my-cli user delete Bob
37
- ```
38
-
39
- Without a prefix (`@Controller()`), commands are registered at the root level.
40
-
41
- ### `@ImportController(ctrl: Function)`
42
-
43
- Class decorator (from `moost`). Nests a child controller inside the parent. The child's prefix appends to the parent's. Register only the top-level controller — imported children are registered automatically.
44
-
45
- ```ts
46
- import { Cli, Controller, Param } from '@moostjs/event-cli'
47
- import { ImportController } from 'moost'
48
-
49
- @Controller('view')
50
- export class ViewCommand {
51
- @Cli(':id')
52
- view(@Param('id') id: string) {
53
- return `Viewing profile ${id}`
54
- }
55
- }
56
-
57
- @Controller('profile')
58
- @ImportController(ViewCommand)
59
- export class ProfileController {
60
- @Cli('list')
61
- list() {
62
- return 'Listing profiles...'
63
- }
64
- }
65
- ```
66
-
67
- ```bash
68
- my-cli profile list # ProfileController.list()
69
- my-cli profile view 42 # ViewCommand.view("42")
70
- ```
71
-
72
- ## Common Patterns
73
-
74
- ### Pattern: Registering controllers
75
-
76
- With `CliApp`:
77
- ```ts
78
- new CliApp()
79
- .controllers(UserController, ConfigController)
80
- .useHelp({ name: 'my-cli' })
81
- .start()
82
- ```
83
-
84
- With standard Moost:
85
- ```ts
86
- const app = new Moost()
87
- app.registerControllers(UserController, ConfigController)
88
- ```
89
-
90
- ### Pattern: Multi-level nesting
91
-
92
- ```ts
93
- @Controller('add')
94
- class RemoteAddController {
95
- @Cli(':name/:url')
96
- add(@Param('name') name: string, @Param('url') url: string) {
97
- return `Adding remote ${name} -> ${url}`
98
- }
99
- }
100
-
101
- @Controller('remote')
102
- @ImportController(RemoteAddController)
103
- class RemoteController {
104
- @Cli('list')
105
- list() { return 'Listing remotes...' }
106
- }
107
-
108
- @Controller()
109
- @ImportController(RemoteController)
110
- class GitController {
111
- @Cli('status')
112
- status() { return 'On branch main' }
113
- }
114
- ```
115
-
116
- ```bash
117
- my-cli status # GitController
118
- my-cli remote list # RemoteController
119
- my-cli remote add origin git@... # RemoteAddController
120
- ```
121
-
122
- ## Path composition
123
-
124
- The final command path is: **controller prefix** + **child prefix** + **@Cli() path**.
125
-
126
- | Parent prefix | Child prefix | `@Cli()` path | Final command |
127
- |--------------|-------------|---------------|---------------|
128
- | `''` (root) | — | `status` | `status` |
129
- | `remote` | — | `list` | `remote list` |
130
- | `remote` | `add` | `:name/:url` | `remote add :name :url` |
131
-
132
- ## Best Practices
133
-
134
- - Use `@Controller(prefix)` to create command namespaces — `user`, `config`, `remote`
135
- - Only register top-level controllers with `.controllers()` — nested children auto-register
136
- - Keep nesting to 2-3 levels max for usability
137
- - Use `@Controller()` (no prefix) for root-level commands
138
-
139
- ## Gotchas
140
-
141
- - Registering a child controller directly AND via `@ImportController` can cause duplicate command registrations
142
- - The controller prefix uses the same space/slash equivalence as command paths
@@ -1,114 +0,0 @@
1
- # Core concepts & setup — @moostjs/event-cli
2
-
3
- > Installation, project scaffolding, and the mental model for building CLI apps with Moost.
4
-
5
- ## Concepts
6
-
7
- `@moostjs/event-cli` is a Moost adapter that turns decorated TypeScript classes into CLI applications. It wraps `@wooksjs/event-cli` (the Wooks CLI engine) and integrates with Moost's decorator-driven DI, interceptors, and pipes.
8
-
9
- The core mental model:
10
- - **Commands** are methods decorated with `@Cli('path')` inside controller classes
11
- - **Controllers** group commands under shared prefixes (like `git remote add`)
12
- - **Options** are method parameters decorated with `@CliOption('flag')`
13
- - **Arguments** are extracted from `:param` segments in command paths via `@Param('name')`
14
- - **CliApp** is a convenience class that wires everything together with one fluent API
15
-
16
- ## Installation
17
-
18
- ```bash
19
- npm install @moostjs/event-cli moost @wooksjs/event-core wooks
20
- # or
21
- pnpm add @moostjs/event-cli moost @wooksjs/event-core wooks
22
- ```
23
-
24
- Peer dependencies: `moost`, `@wooksjs/event-core`, `wooks`.
25
-
26
- ## Project scaffolding
27
-
28
- ```bash
29
- npm create moost -- --cli
30
- # or with a name:
31
- npm create moost my-cli -- --cli
32
- ```
33
-
34
- Creates:
35
- ```
36
- my-cli/
37
- ├── src/
38
- │ ├── controllers/
39
- │ │ └── app.controller.ts
40
- │ ├── main.ts
41
- │ └── bin.ts
42
- ├── package.json
43
- └── tsconfig.json
44
- ```
45
-
46
- ## CliApp — quick setup
47
-
48
- `CliApp` extends `Moost` and provides a fluent API for the common case:
49
-
50
- ```ts
51
- import { CliApp } from '@moostjs/event-cli'
52
- import { AppController } from './controllers/app.controller'
53
-
54
- new CliApp()
55
- .controllers(AppController) // register controller classes
56
- .useHelp({ name: 'my-cli' }) // enable --help with app name
57
- .useOptions([ // global options shown in all commands
58
- { keys: ['help'], description: 'Display instructions for the command.' },
59
- ])
60
- .start() // wire adapters + interceptors, call init()
61
- ```
62
-
63
- ### `CliApp` methods
64
-
65
- | Method | Description |
66
- |--------|-------------|
67
- | `.controllers(...ctrls)` | Register one or more controller classes (shortcut for `registerControllers`) |
68
- | `.useHelp(opts)` | Configure the help system (name, title, colors, lookupLevel, maxWidth, maxLeft, mark) |
69
- | `.useOptions(opts)` | Set global CLI options that appear in every command's help |
70
- | `.start()` | Creates `MoostCli`, attaches adapter, applies help interceptor, calls `init()` |
71
-
72
- ## Exports
73
-
74
- All imports come from `'@moostjs/event-cli'`:
75
-
76
- | Export | Kind | Purpose |
77
- |--------|------|---------|
78
- | `CliApp` | class | Quick-setup CLI application factory |
79
- | `MoostCli` | class | Low-level adapter (implements `TMoostAdapter`) |
80
- | `Cli` | decorator | Define a CLI command on a method |
81
- | `CliAlias` | decorator | Add an alias for a command |
82
- | `CliExample` | decorator | Add a usage example for help output |
83
- | `CliOption` | decorator | Bind a parameter to a `--flag` |
84
- | `CliGlobalOption` | decorator | Define a global option on a controller class |
85
- | `cliHelpInterceptor` | function | Factory for the help interceptor |
86
- | `CliHelpInterceptor` | decorator | Decorator form of the help interceptor |
87
- | `cliKind` | object | Event type identifier for CLI events |
88
- | `Controller` | decorator | Re-exported from `moost` |
89
- | `Param` | decorator | Re-exported from `moost` |
90
- | `Intercept` | decorator | Re-exported from `moost` |
91
- | `Description` | decorator | Re-exported from `moost` |
92
- | `Optional` | decorator | Re-exported from `moost` |
93
- | `defineBeforeInterceptor` | function | Re-exported from `moost` |
94
- | `defineAfterInterceptor` | function | Re-exported from `moost` |
95
- | `defineInterceptor` | function | Re-exported from `moost` |
96
- | `TInterceptorPriority` | type | Re-exported from `moost` |
97
-
98
- ## Running the app
99
-
100
- ```bash
101
- # Development
102
- npx tsx src/bin.ts greet World
103
-
104
- # Production (after compiling)
105
- node dist/bin.js greet World
106
- ```
107
-
108
- The return value of a command handler is printed to stdout.
109
-
110
- ## Best Practices
111
-
112
- - Use `CliApp` for standard CLI apps; use `MoostCli` only when you need full control or multi-adapter setups
113
- - Keep `tsconfig.json` with `"experimentalDecorators": true` and `"emitDecoratorMetadata": true`
114
- - Register only top-level controllers — nested children are auto-registered via `@ImportController`
@@ -1,160 +0,0 @@
1
- # Help system — @moostjs/event-cli
2
-
3
- > Auto-generated help output, help interceptor configuration, and unknown command handling.
4
-
5
- ## Concepts
6
-
7
- Moost CLI auto-generates `--help` output from decorator metadata: `@Description()` on methods and parameters, `@CliExample()` for usage patterns, `@Value()` for sample values, and `@CliGlobalOption()` for global flags. The help system is powered by `cliHelpInterceptor`, which intercepts `--help` flags and renders formatted help.
8
-
9
- ## API Reference
10
-
11
- ### `cliHelpInterceptor(opts?)`
12
-
13
- Factory function that returns a before-interceptor at `BEFORE_ALL` priority. Intercepts commands with `--help` and prints help output.
14
-
15
- Options:
16
- - `helpOptions?: string[]` — flag names that trigger help (default: `['help']`)
17
- - `colors?: boolean` — enable colored output
18
- - `lookupLevel?: number` — depth for "did you mean?" fuzzy matching on unknown commands
19
-
20
- ```ts
21
- import { cliHelpInterceptor } from '@moostjs/event-cli'
22
- import { Moost } from 'moost'
23
-
24
- const app = new Moost()
25
- app.applyGlobalInterceptors(
26
- cliHelpInterceptor({
27
- helpOptions: ['help', 'h'],
28
- colors: true,
29
- lookupLevel: 3,
30
- }),
31
- )
32
- ```
33
-
34
- ### `@CliHelpInterceptor(opts?)`
35
-
36
- Decorator form of `cliHelpInterceptor`. Apply to a controller or method to scope help behavior:
37
-
38
- ```ts
39
- import { CliHelpInterceptor } from '@moostjs/event-cli'
40
-
41
- @CliHelpInterceptor({ colors: true, lookupLevel: 3 })
42
- @Controller()
43
- export class AppController {
44
- @Cli('deploy')
45
- deploy() { return 'Deploying...' }
46
- }
47
- ```
48
-
49
- ### `useHelp(opts)` on CliApp
50
-
51
- The simplest way to enable help. Calls `cliHelpInterceptor` internally:
52
-
53
- ```ts
54
- new CliApp()
55
- .controllers(AppController)
56
- .useHelp({
57
- name: 'my-cli', // shown in usage: "$ my-cli ..."
58
- title: 'My CLI Tool', // title at top of help
59
- colors: true, // colored output (default: true)
60
- lookupLevel: 3, // fuzzy lookup depth (default: 3)
61
- maxWidth: 80, // max help output width
62
- maxLeft: 30, // max left column width
63
- mark: '>', // prefix marker for sections
64
- })
65
- .start()
66
- ```
67
-
68
- ### Help options reference
69
-
70
- | Option | Default | Description |
71
- |--------|---------|-------------|
72
- | `name` | — | CLI app name shown in usage examples |
73
- | `title` | — | Title displayed at the top of help |
74
- | `colors` | `true` | Enable colored terminal output |
75
- | `lookupLevel` | `3` | Depth for "did you mean?" suggestions |
76
- | `maxWidth` | — | Maximum width of help output |
77
- | `maxLeft` | — | Maximum width of the left column |
78
- | `mark` | — | Prefix marker for help sections |
79
-
80
- ## Common Patterns
81
-
82
- ### Pattern: Full help setup with CliApp
83
-
84
- ```ts
85
- new CliApp()
86
- .controllers(AppController)
87
- .useHelp({ name: 'my-cli' })
88
- .useOptions([
89
- { keys: ['help'], description: 'Display instructions for the command.' },
90
- { keys: ['verbose', 'v'], description: 'Enable verbose output.' },
91
- ])
92
- .start()
93
- ```
94
-
95
- ### Pattern: Documenting a command
96
-
97
- ```ts
98
- @Description('Deploy application to a target environment')
99
- @Cli('deploy/:env')
100
- @CliExample('deploy dev -p my-app', 'Deploy "my-app" to development')
101
- @CliExample('deploy prod -p my-app --verbose', 'Deploy with verbose logging')
102
- deploy(
103
- @Description('Environment')
104
- @Param('env')
105
- env: string,
106
-
107
- @Description('Project name')
108
- @CliOption('project', 'p')
109
- @Value('<my-app>')
110
- project: string,
111
- ) {
112
- return `Deploying ${project} to ${env}`
113
- }
114
- ```
115
-
116
- Running `my-cli deploy --help` produces:
117
- ```
118
- DESCRIPTION
119
- Deploy application to a target environment
120
-
121
- USAGE
122
- $ my-cli deploy <env>
123
-
124
- ARGUMENTS
125
- <env> - Environment
126
-
127
- OPTIONS
128
- --help - Display instructions for the command.
129
- -p, --project <my-app> - Project name
130
-
131
- EXAMPLES
132
- # Deploy "my-app" to development
133
- $ my-cli deploy dev -p my-app
134
- # Deploy with verbose logging
135
- $ my-cli deploy prod -p my-app --verbose
136
- ```
137
-
138
- ### Pattern: Unknown command handling
139
-
140
- When `lookupLevel` is set, typing a wrong command triggers suggestions:
141
-
142
- ```bash
143
- $ my-cli depoly
144
- Did you mean "deploy"?
145
- ```
146
-
147
- ## Integration
148
-
149
- - `@Description()` on methods fills the DESCRIPTION section
150
- - `@Description()` on parameters fills ARGUMENTS and OPTIONS sections
151
- - `@CliExample()` fills the EXAMPLES section
152
- - `@Value()` shows sample values next to options
153
- - `@CliGlobalOption()` adds options to every command's help in that controller
154
-
155
- ## Best Practices
156
-
157
- - Always call `.useHelp({ name: 'my-cli' })` — the name makes usage examples clear
158
- - Add `@Description()` to every command and parameter — it's the primary documentation
159
- - Use `@CliExample()` for non-obvious usage patterns
160
- - Set `lookupLevel: 3` for helpful "did you mean?" suggestions
@@ -1,160 +0,0 @@
1
- # Interceptors — @moostjs/event-cli
2
-
3
- > Guards, error handlers, timing, and cross-cutting CLI logic.
4
-
5
- ## Concepts
6
-
7
- Interceptors wrap command execution with cross-cutting logic. They have three lifecycle hooks: `before`, `after`, and `error`. Each runs at a configurable priority level. They work identically to Moost interceptors but are applied in a CLI context.
8
-
9
- Use the `define*` helpers from `moost` (re-exported by `@moostjs/event-cli`) to create functional interceptors.
10
-
11
- ## API Reference
12
-
13
- ### `defineBeforeInterceptor(fn, priority?)`
14
-
15
- Creates an interceptor that runs before the handler. Call `reply(value)` to short-circuit and skip the handler.
16
-
17
- ```ts
18
- import { defineBeforeInterceptor, TInterceptorPriority } from '@moostjs/event-cli'
19
-
20
- const guard = defineBeforeInterceptor((reply) => {
21
- if (!process.env.CI_TOKEN) {
22
- console.error('Error: CI_TOKEN required')
23
- process.exit(1)
24
- }
25
- }, TInterceptorPriority.GUARD)
26
- ```
27
-
28
- ### `defineAfterInterceptor(fn, priority?)`
29
-
30
- Creates an interceptor that runs after the handler. Receives the response.
31
-
32
- ```ts
33
- import { defineAfterInterceptor } from '@moostjs/event-cli'
34
-
35
- const logger = defineAfterInterceptor((response) => {
36
- console.log('Command returned:', response)
37
- })
38
- ```
39
-
40
- ### `defineErrorInterceptor(fn, priority?)`
41
-
42
- Creates an interceptor that runs on error. Call `reply(value)` to recover.
43
-
44
- ```ts
45
- import { defineErrorInterceptor, TInterceptorPriority } from '@moostjs/event-cli'
46
-
47
- const errorHandler = defineErrorInterceptor((error, reply) => {
48
- console.error(`Error: ${error.message}`)
49
- reply('')
50
- }, TInterceptorPriority.CATCH_ERROR)
51
- ```
52
-
53
- ### `defineInterceptor(hooks, priority?)`
54
-
55
- Combines all hooks in one interceptor:
56
-
57
- ```ts
58
- import { defineInterceptor, TInterceptorPriority } from '@moostjs/event-cli'
59
-
60
- const myInterceptor = defineInterceptor({
61
- before(reply) { /* ... */ },
62
- after(response, reply) { /* ... */ },
63
- error(error, reply) { /* ... */ },
64
- }, TInterceptorPriority.INTERCEPTOR)
65
- ```
66
-
67
- ### Priority levels
68
-
69
- | Priority | Value | Typical use |
70
- |----------|-------|-------------|
71
- | `BEFORE_ALL` | 0 | Help interceptor, early logging |
72
- | `BEFORE_GUARD` | 1 | Setup before guards |
73
- | `GUARD` | 2 | Permission checks, env validation |
74
- | `AFTER_GUARD` | 3 | Post-auth setup |
75
- | `INTERCEPTOR` | 4 | General-purpose (default) |
76
- | `CATCH_ERROR` | 5 | Error formatting |
77
- | `AFTER_ALL` | 6 | Timing, cleanup |
78
-
79
- ## Common Patterns
80
-
81
- ### Pattern: CLI guard
82
-
83
- ```ts
84
- import { Cli, Controller, Intercept } from '@moostjs/event-cli'
85
- import { defineBeforeInterceptor, TInterceptorPriority } from '@moostjs/event-cli'
86
-
87
- const requireEnvGuard = defineBeforeInterceptor(() => {
88
- if (!process.env.CI_TOKEN) {
89
- console.error('Error: CI_TOKEN environment variable is required')
90
- process.exit(1)
91
- }
92
- }, TInterceptorPriority.GUARD)
93
-
94
- @Controller()
95
- export class DeployController {
96
- @Intercept(requireEnvGuard)
97
- @Cli('deploy/:env')
98
- deploy(@Param('env') env: string) {
99
- return `Deploying to ${env}...`
100
- }
101
- }
102
- ```
103
-
104
- ### Pattern: Global error handler
105
-
106
- ```ts
107
- const cliErrorHandler = defineErrorInterceptor((error, reply) => {
108
- console.error(`Error: ${error.message}`)
109
- reply('')
110
- }, TInterceptorPriority.CATCH_ERROR)
111
-
112
- const app = new CliApp()
113
- app.applyGlobalInterceptors(cliErrorHandler)
114
- app.controllers(AppController)
115
- app.useHelp({ name: 'my-cli' })
116
- app.start()
117
- ```
118
-
119
- ### Pattern: Timing interceptor
120
-
121
- Class-based with `FOR_EVENT` scope (fresh instance per command):
122
-
123
- ```ts
124
- import { Interceptor, Before, After, TInterceptorPriority } from 'moost'
125
-
126
- @Interceptor(TInterceptorPriority.BEFORE_ALL, 'FOR_EVENT')
127
- class TimingInterceptor {
128
- private start = 0
129
-
130
- @Before()
131
- recordStart() { this.start = performance.now() }
132
-
133
- @After()
134
- logDuration() {
135
- const ms = (performance.now() - this.start).toFixed(1)
136
- console.log(`Completed in ${ms}ms`)
137
- }
138
- }
139
- ```
140
-
141
- ## Applying interceptors
142
-
143
- | Scope | How |
144
- |-------|-----|
145
- | Single command | `@Intercept(fn)` on the method |
146
- | All commands in a controller | `@Intercept(fn)` on the class |
147
- | Every command in the app | `app.applyGlobalInterceptors(fn)` |
148
-
149
- ## Best Practices
150
-
151
- - Use `GUARD` priority for permission/env checks that should block execution
152
- - Use `CATCH_ERROR` for terminal-friendly error formatting
153
- - Apply error handlers globally so every command benefits
154
- - Use `FOR_EVENT` scope for class-based interceptors that track per-command state (like timing)
155
-
156
- ## Gotchas
157
-
158
- - Interceptors run in priority order (lower numbers first), not registration order
159
- - `reply(value)` in a before-interceptor skips the handler entirely — the value becomes the response
160
- - The help interceptor runs at `BEFORE_ALL` — it runs before guards
@@ -1,180 +0,0 @@
1
- # Options & arguments — @moostjs/event-cli
2
-
3
- > CLI flags, boolean options, optional parameters, descriptions, sample values, and global options.
4
-
5
- ## Concepts
6
-
7
- CLI commands accept two kinds of input:
8
- - **Positional arguments** — extracted from `:param` segments in the command path via `@Param()`
9
- - **Option flags** — bound to `--flag`/`-f` style switches via `@CliOption()`
10
-
11
- Additional decorators control documentation and behavior: `@Description()`, `@Optional()`, `@Value()`, and `@CliGlobalOption()`.
12
-
13
- ## API Reference
14
-
15
- ### `@CliOption(...keys: string[])`
16
-
17
- Parameter decorator. Binds the parameter to one or more CLI flags. Long names (2+ chars) become `--flag`, single chars become `-f`.
18
-
19
- Under the hood, this calls `useCliOption(keys[0])` from `@wooksjs/event-cli` via a `@Resolve()` pipe.
20
-
21
- ```ts
22
- import { Cli, CliOption, Controller, Description, Param } from '@moostjs/event-cli'
23
-
24
- @Controller()
25
- export class DeployController {
26
- @Cli('deploy/:env')
27
- deploy(
28
- @Param('env') env: string,
29
-
30
- @Description('Project name')
31
- @CliOption('project', 'p')
32
- project: string,
33
-
34
- @Description('Enable verbose logging')
35
- @CliOption('verbose', 'v')
36
- verbose: boolean,
37
- ) {
38
- if (verbose) console.log(`Project: ${project}, env: ${env}`)
39
- return `Deploying ${project} to ${env}`
40
- }
41
- }
42
- ```
43
-
44
- ```bash
45
- my-cli deploy production --project my-app --verbose
46
- my-cli deploy production -p my-app -v # same thing
47
- ```
48
-
49
- ### `@CliGlobalOption(option: { keys: string[]; description?: string; value?: string })`
50
-
51
- Class decorator. Declares an option that appears in help output for every command in the controller.
52
-
53
- ```ts
54
- import { Cli, CliGlobalOption, Controller } from '@moostjs/event-cli'
55
-
56
- @Controller('build')
57
- @CliGlobalOption({
58
- keys: ['verbose', 'v'],
59
- description: 'Enable verbose logging',
60
- })
61
- export class BuildController {
62
- @Cli('dev')
63
- buildDev() { return 'Building for dev...' }
64
-
65
- @Cli('prod')
66
- buildProd() { return 'Building for prod...' }
67
- }
68
- ```
69
-
70
- ### `@Description(text: string)`
71
-
72
- Parameter or method decorator (re-exported from `moost`). Documents the parameter or command for help output.
73
-
74
- ```ts
75
- @Description('Deploy the application to a target environment')
76
- @Cli('deploy/:env')
77
- deploy(
78
- @Description('Target environment (production, staging, dev)')
79
- @Param('env')
80
- env: string,
81
- ) {
82
- return `Deploying to ${env}...`
83
- }
84
- ```
85
-
86
- ### `@Optional()`
87
-
88
- Parameter decorator (re-exported from `moost`). Marks a parameter as not required. Integrates with the help system.
89
-
90
- ```ts
91
- @Cli('build')
92
- build(
93
- @Description('Output directory')
94
- @CliOption('out', 'o')
95
- @Optional()
96
- outDir: string,
97
- ) {
98
- return `Output: ${outDir ?? './dist'}`
99
- }
100
- ```
101
-
102
- ### `@Value(sample: string)`
103
-
104
- Parameter decorator (from `moost`). Shows a placeholder in help output. Does NOT set a default value.
105
-
106
- ```ts
107
- import { Value } from 'moost'
108
-
109
- @Cli('test/:config')
110
- test(
111
- @Description('Target environment')
112
- @CliOption('target', 't')
113
- @Value('<staging>')
114
- target: string,
115
- ) {
116
- return `Testing in ${target}`
117
- }
118
- ```
119
-
120
- In help: `--target <staging>`.
121
-
122
- ## Common Patterns
123
-
124
- ### Pattern: Boolean flags
125
-
126
- When a parameter has type `boolean`, Moost automatically registers it as a boolean flag (no value expected — presence means `true`):
127
-
128
- ```ts
129
- @Cli('build')
130
- build(
131
- @CliOption('watch', 'w') watch: boolean,
132
- @CliOption('minify', 'm') minify: boolean,
133
- ) {
134
- return `watch=${watch}, minify=${minify}`
135
- }
136
- ```
137
-
138
- ```bash
139
- my-cli build --watch --minify # watch=true, minify=true
140
- my-cli build -wm # combined short flags
141
- my-cli build # watch=undefined, minify=undefined
142
- ```
143
-
144
- ### Pattern: Global options via CliApp
145
-
146
- ```ts
147
- new CliApp()
148
- .controllers(AppController)
149
- .useHelp({ name: 'my-cli' })
150
- .useOptions([
151
- { keys: ['help'], description: 'Display instructions for the command.' },
152
- { keys: ['verbose', 'v'], description: 'Enable verbose output.' },
153
- ])
154
- .start()
155
- ```
156
-
157
- ## Decorator summary
158
-
159
- | Decorator | Applies to | Purpose |
160
- |-----------|-----------|---------|
161
- | `@Param('name')` | parameter | Extract positional argument from `:name` in path |
162
- | `@CliOption('key', 'k')` | parameter | Bind to `--key` / `-k` flag |
163
- | `@CliGlobalOption({...})` | class | Global option shown in all commands' help |
164
- | `@Description('...')` | parameter, method | Document for help output |
165
- | `@Optional()` | parameter | Mark as not required |
166
- | `@Value('...')` | parameter | Show sample value in help |
167
-
168
- ## Best Practices
169
-
170
- - Always add `@Description()` to options and arguments — it drives `--help` output
171
- - Use short aliases (single char) for frequently-used flags: `@CliOption('verbose', 'v')`
172
- - Type boolean flags as `boolean` so they don't expect a value argument
173
- - Use `@Value()` for non-obvious option formats (e.g., `@Value('<host:port>')`)
174
-
175
- ## Gotchas
176
-
177
- - `@Optional()` does not provide a default value — it only marks the parameter as optional. Use `??` in your code for defaults
178
- - `@Value()` is purely cosmetic for help display — it does NOT set a default value
179
- - Without `@Optional()`, a missing flag results in `undefined` (not an error)
180
- - Boolean flags that are absent yield `undefined`, not `false`