@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 +7 -13
- package/scripts/setup-skills.js +0 -78
- package/skills/moostjs-event-cli/SKILL.md +0 -38
- package/skills/moostjs-event-cli/advanced.md +0 -202
- package/skills/moostjs-event-cli/commands.md +0 -152
- package/skills/moostjs-event-cli/controllers.md +0 -142
- package/skills/moostjs-event-cli/core.md +0 -114
- package/skills/moostjs-event-cli/help.md +0 -160
- package/skills/moostjs-event-cli/interceptors.md +0 -160
- package/skills/moostjs-event-cli/options.md +0 -180
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moostjs/event-cli",
|
|
3
|
-
"version": "0.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.
|
|
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.
|
|
53
|
-
"wooks": "^0.7.
|
|
54
|
-
"moost": "^0.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
|
}
|
package/scripts/setup-skills.js
DELETED
|
@@ -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`
|