@memberjunction/cli-core 0.0.1 → 5.42.0
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/README.md +136 -43
- package/dist/base-cli-plugin.d.ts +56 -0
- package/dist/base-cli-plugin.d.ts.map +1 -0
- package/dist/base-cli-plugin.js +78 -0
- package/dist/base-cli-plugin.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin-registry.d.ts +72 -0
- package/dist/plugin-registry.d.ts.map +1 -0
- package/dist/plugin-registry.js +152 -0
- package/dist/plugin-registry.js.map +1 -0
- package/dist/runtime-host.d.ts +48 -0
- package/dist/runtime-host.d.ts.map +1 -0
- package/dist/runtime-host.js +167 -0
- package/dist/runtime-host.js.map +1 -0
- package/dist/serialize.d.ts +10 -0
- package/dist/serialize.d.ts.map +1 -0
- package/dist/serialize.js +15 -0
- package/dist/serialize.js.map +1 -0
- package/dist/types.d.ts +105 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/package.json +39 -6
package/README.md
CHANGED
|
@@ -1,45 +1,138 @@
|
|
|
1
1
|
# @memberjunction/cli-core
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
**
|
|
3
|
+
Pluggable core for the MemberJunction `mj` CLI. Provides the primitives that let
|
|
4
|
+
commands be **machine-readable**, **discoverable**, and **pluggable** — so AI
|
|
5
|
+
coding agents (Claude Code, OpenCode, …) can drive `mj` reliably, and third
|
|
6
|
+
parties can add commands without modifying MJCLI.
|
|
7
|
+
|
|
8
|
+
## Why
|
|
9
|
+
|
|
10
|
+
`mj codegen`, `mj sync push/pull`, etc. are run constantly by humans *and* AI
|
|
11
|
+
agents. Humans get clean spinners and a summary box. Agents get four problems:
|
|
12
|
+
|
|
13
|
+
1. **No machine-readable output** — parsing chalk + box-drawing is fragile.
|
|
14
|
+
2. **Errors interleaved mid-run** — no collected error list.
|
|
15
|
+
3. **The banner burns context** — hundreds of captured chars before content.
|
|
16
|
+
4. **No runtime signal** — agents pick a timeout and kill healthy long-running
|
|
17
|
+
commands (`codegen`, `migrate`) midway.
|
|
18
|
+
|
|
19
|
+
This package fixes all four with one architecture, and makes the CLI open to
|
|
20
|
+
third-party plugins as a side effect.
|
|
21
|
+
|
|
22
|
+
## Architecture
|
|
23
|
+
|
|
24
|
+
> **Principle:** plugins return *data* (`MJCLIResult`); the host *renders* it.
|
|
25
|
+
> No `ora`, `chalk`, or `console` inside a plugin's `Execute()`.
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
mj (oclif root)
|
|
29
|
+
├─ loads mj-cli-plugins.json → @RegisterClass populates ClassFactory
|
|
30
|
+
├─ composes `mj usage` / `mj <domain> usage` from each plugin's static Usage
|
|
31
|
+
└─ routes a command → BaseCLIPlugin subclass
|
|
32
|
+
│ injects MJCLIRuntimeHost (per --format)
|
|
33
|
+
▼
|
|
34
|
+
BaseCLIPlugin MJCLIRuntimeHost
|
|
35
|
+
- abstract Execute(): MJCLIResult - text: ora spinner + chalk
|
|
36
|
+
- this.Host.StartStep / Log / … - json: events→stderr, result→stdout
|
|
37
|
+
- declares static Usage - md: fenced ```json block
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Output formats (`--format`)
|
|
41
|
+
|
|
42
|
+
| Format | Result destination | Decorative output | Use |
|
|
43
|
+
|---|---|---|---|
|
|
44
|
+
| `text` (default) | — (plugin renders) | stdout (spinners/chalk) | humans |
|
|
45
|
+
| `json` | **stdout** (clean JSON) | **stderr** (`{event:…}`) | agents, `\| jq` |
|
|
46
|
+
| `md` | stdout (fenced block) | stderr | AI chat UIs |
|
|
47
|
+
|
|
48
|
+
`--verbose` / `--no-banner` are inherited by every plugin too.
|
|
49
|
+
|
|
50
|
+
## Progressive-disclosure usage (for agents)
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
mj usage --format=json | jq '.data.domains[].domain' # tier 1 (~200 tokens)
|
|
54
|
+
mj sync usage --format=json | jq '.data.commands[].runtime' # tier 2 (one domain)
|
|
55
|
+
mj sync push --help # tier 3 (full oclif help)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Tier 1 returns a domain map; tier 2 returns one domain's commands, flags,
|
|
59
|
+
examples, and **runtime hints**. The guidance string tells the agent to check
|
|
60
|
+
usage rather than guess flags. An agent discovers the surface in ~500–700 tokens
|
|
61
|
+
instead of pulling the whole ~13k command tree.
|
|
62
|
+
|
|
63
|
+
## Runtime hints / timeouts
|
|
64
|
+
|
|
65
|
+
Every plugin declares a `RuntimeHint` (`fast` <5s · `moderate` 5–60s · `slow`
|
|
66
|
+
>60s · `variable`). It's surfaced two ways so agents budget timeouts:
|
|
67
|
+
|
|
68
|
+
1. In usage output (read before invoking).
|
|
69
|
+
2. As a pre-execution advisory — `{event:'start', command, runtime}` on stderr
|
|
70
|
+
(JSON mode) or a dim note (text). Suppressed for `fast` commands.
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
timeout_s=$(mj codegen usage --format=json | jq '.data.commands[0].runtime.typicalSeconds')
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Authoring a plugin
|
|
77
|
+
|
|
78
|
+
1. Create a package that depends on `@memberjunction/cli-core`.
|
|
79
|
+
2. Subclass `BaseCLIPlugin`, register it, declare flags + `static Usage`, and
|
|
80
|
+
implement `Execute()`:
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
import { Flags } from '@oclif/core';
|
|
84
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
85
|
+
import { BaseCLIPlugin, type MJCLIResult, type PluginUsage } from '@memberjunction/cli-core';
|
|
86
|
+
|
|
87
|
+
@RegisterClass(BaseCLIPlugin, 'widgets:build')
|
|
88
|
+
export class WidgetsBuildPlugin extends BaseCLIPlugin {
|
|
89
|
+
static description = 'Build widgets';
|
|
90
|
+
static flags = { dir: Flags.string({ description: 'Source directory' }) };
|
|
91
|
+
|
|
92
|
+
static Usage: PluginUsage = {
|
|
93
|
+
domain: 'widgets',
|
|
94
|
+
command: 'widgets:build',
|
|
95
|
+
summary: 'Compile widgets from source.',
|
|
96
|
+
flags: [{ name: '--dir', type: 'string', description: 'Source directory' }],
|
|
97
|
+
examples: ['mj widgets build --dir=src', 'mj widgets build --format=json'],
|
|
98
|
+
runtime: { class: 'moderate', typicalSeconds: 20, note: 'scales with widget count' },
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
protected async Execute(): Promise<MJCLIResult> {
|
|
102
|
+
const start = Date.now();
|
|
103
|
+
const { flags } = await this.parse(WidgetsBuildPlugin);
|
|
104
|
+
this.Host.StartStep('Building widgets');
|
|
105
|
+
// … do work; collect errors instead of throwing …
|
|
106
|
+
this.Host.SucceedStep('Built widgets');
|
|
107
|
+
if (this.Host.Format === 'text') this.Host.Log('Done.'); // human-only rich text
|
|
108
|
+
return { success: true, command: 'widgets:build', durationSeconds: (Date.now() - start) / 1000, data: { /* … */ } };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Optional: release resources / force-exit to kill lingering handles.
|
|
112
|
+
protected async Cleanup(result: MJCLIResult): Promise<void> { /* close pools */ }
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
3. Ship the plugin behind a **light subpath** (e.g. `@my-org/pkg/plugins`) that
|
|
117
|
+
static-imports only `cli-core` + light deps and dynamic-imports any heavy
|
|
118
|
+
engine inside `Execute()`. This keeps oclif manifest generation and `mj usage`
|
|
119
|
+
enumeration cheap.
|
|
120
|
+
|
|
121
|
+
4. Register it so `mj` loads it:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
mj plugin add @my-org/pkg/plugins
|
|
125
|
+
# appends to mj-cli-plugins.json — no MJCLI change needed
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
In the MemberJunction monorepo, MJCLI also keeps a one-line oclif shim under
|
|
129
|
+
`src/commands/` (`export { WidgetsBuildPlugin as default } from '…'`) so oclif
|
|
130
|
+
discovers and routes the command. The plugin's logic stays in its home package.
|
|
131
|
+
|
|
132
|
+
## Exports
|
|
133
|
+
|
|
134
|
+
- `BaseCLIPlugin` — abstract base (extends oclif `Command`).
|
|
135
|
+
- `MJCLIRuntimeHost` / `IMJCLIRuntimeHost` — the stdio host.
|
|
136
|
+
- `CLIPluginRegistry` — loads `mj-cli-plugins.json`, composes tier-1/tier-2 usage.
|
|
137
|
+
- Types: `MJCLIResult`, `OutputFormat`, `PluginUsage`, `RuntimeHint`, …
|
|
138
|
+
- `PLUGIN_CONFIG_FILENAME` — `"mj-cli-plugins.json"`.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import type { IMJCLIRuntimeHost, MJCLIResult, PluginUsage } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Abstract base for every pluggable `mj` command (plan D1/D2).
|
|
5
|
+
*
|
|
6
|
+
* Extends oclif's {@link Command}, so oclif still owns flag parsing, help
|
|
7
|
+
* generation, and routing. We wrap only the *execution* layer: subclasses
|
|
8
|
+
* implement {@link BaseCLIPlugin.Execute} (pure logic, returns data) and the
|
|
9
|
+
* shared {@link BaseCLIPlugin.run} wires up the {@link IMJCLIRuntimeHost}, emits
|
|
10
|
+
* the runtime advisory, renders the result per `--format`, and sets the exit code.
|
|
11
|
+
*
|
|
12
|
+
* The global flags `--format`, `--verbose`, and `--no-banner` are declared on
|
|
13
|
+
* {@link BaseCLIPlugin.baseFlags} and inherited by every subclass via oclif's
|
|
14
|
+
* native `baseFlags` merging — no per-command duplication (plan D3).
|
|
15
|
+
*/
|
|
16
|
+
export declare abstract class BaseCLIPlugin extends Command {
|
|
17
|
+
/** Inherited by every subclass through oclif's static `baseFlags` mechanism. */
|
|
18
|
+
static baseFlags: {
|
|
19
|
+
format: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
20
|
+
verbose: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
21
|
+
'no-banner': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Every plugin declares its own usage + runtime metadata. The CLI root reads
|
|
25
|
+
* this off the registered classes to assemble the progressive-disclosure
|
|
26
|
+
* `mj usage` / `mj <domain> usage` surface and the timeout advisory.
|
|
27
|
+
* Subclasses MUST override.
|
|
28
|
+
*/
|
|
29
|
+
static Usage: PluginUsage;
|
|
30
|
+
protected Host: IMJCLIRuntimeHost;
|
|
31
|
+
/** Parsed flags, captured once in {@link run}; read via {@link GetFlags}. */
|
|
32
|
+
private parsedFlags;
|
|
33
|
+
/**
|
|
34
|
+
* The flags parsed for this command. Subclasses call this in {@link Execute}
|
|
35
|
+
* instead of re-parsing — the parse happens once, in {@link run}.
|
|
36
|
+
*
|
|
37
|
+
* The `as unknown as T` is the ONE place the cross-package `@oclif/core` copy
|
|
38
|
+
* split is bridged: cli-core nests its own oclif, so the inferred parse type
|
|
39
|
+
* isn't nameable from a strict consumer package (TS2742). Confining the cast
|
|
40
|
+
* here keeps every plugin's `Execute()` cast-free. Pass
|
|
41
|
+
* `Interfaces.InferredFlags<typeof YourPlugin.flags>` as `T`.
|
|
42
|
+
*/
|
|
43
|
+
protected GetFlags<T>(): T;
|
|
44
|
+
/**
|
|
45
|
+
* oclif entry point — do NOT override in subclasses. Override {@link Execute}.
|
|
46
|
+
*/
|
|
47
|
+
run(): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Optional post-Emit hook. Override to release resources (DB pools, singletons)
|
|
50
|
+
* and, when necessary, `process.exit()` to terminate lingering background work.
|
|
51
|
+
*/
|
|
52
|
+
protected Cleanup(_result: MJCLIResult): Promise<void>;
|
|
53
|
+
/** Subclasses implement this — pure logic, no direct stdio. */
|
|
54
|
+
protected abstract Execute(): Promise<MJCLIResult>;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=base-cli-plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-cli-plugin.d.ts","sourceRoot":"","sources":["../src/base-cli-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAS,MAAM,aAAa,CAAC;AAE7C,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAgB,WAAW,EAAE,MAAM,SAAS,CAAC;AAEzF;;;;;;;;;;;;GAYG;AACH,8BAAsB,aAAc,SAAQ,OAAO;IACjD,gFAAgF;IAChF,OAAgB,SAAS;;;;MAQvB;IAEF;;;;;OAKG;IACH,MAAM,CAAC,KAAK,EAAE,WAAW,CAAC;IAE1B,SAAS,CAAC,IAAI,EAAG,iBAAiB,CAAC;IAEnC,6EAA6E;IAC7E,OAAO,CAAC,WAAW,CAAU;IAE7B;;;;;;;;;OASG;IACH,SAAS,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC;IAI1B;;OAEG;IACG,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IAkC1B;;;OAGG;cACa,OAAO,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAI5D,+DAA+D;IAC/D,SAAS,CAAC,QAAQ,CAAC,OAAO,IAAI,OAAO,CAAC,WAAW,CAAC;CACnD"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import { MJCLIRuntimeHost } from './runtime-host.js';
|
|
3
|
+
/**
|
|
4
|
+
* Abstract base for every pluggable `mj` command (plan D1/D2).
|
|
5
|
+
*
|
|
6
|
+
* Extends oclif's {@link Command}, so oclif still owns flag parsing, help
|
|
7
|
+
* generation, and routing. We wrap only the *execution* layer: subclasses
|
|
8
|
+
* implement {@link BaseCLIPlugin.Execute} (pure logic, returns data) and the
|
|
9
|
+
* shared {@link BaseCLIPlugin.run} wires up the {@link IMJCLIRuntimeHost}, emits
|
|
10
|
+
* the runtime advisory, renders the result per `--format`, and sets the exit code.
|
|
11
|
+
*
|
|
12
|
+
* The global flags `--format`, `--verbose`, and `--no-banner` are declared on
|
|
13
|
+
* {@link BaseCLIPlugin.baseFlags} and inherited by every subclass via oclif's
|
|
14
|
+
* native `baseFlags` merging — no per-command duplication (plan D3).
|
|
15
|
+
*/
|
|
16
|
+
export class BaseCLIPlugin extends Command {
|
|
17
|
+
/** Inherited by every subclass through oclif's static `baseFlags` mechanism. */
|
|
18
|
+
static { this.baseFlags = {
|
|
19
|
+
format: Flags.string({
|
|
20
|
+
options: ['text', 'json', 'md'],
|
|
21
|
+
default: 'text',
|
|
22
|
+
description: 'Output format: text (human), json (machine-readable), md (Markdown-fenced)',
|
|
23
|
+
}),
|
|
24
|
+
verbose: Flags.boolean({ char: 'v', default: false, description: 'Show detailed output' }),
|
|
25
|
+
'no-banner': Flags.boolean({ default: false, description: 'Suppress the startup banner and runtime advisory' }),
|
|
26
|
+
}; }
|
|
27
|
+
/**
|
|
28
|
+
* The flags parsed for this command. Subclasses call this in {@link Execute}
|
|
29
|
+
* instead of re-parsing — the parse happens once, in {@link run}.
|
|
30
|
+
*
|
|
31
|
+
* The `as unknown as T` is the ONE place the cross-package `@oclif/core` copy
|
|
32
|
+
* split is bridged: cli-core nests its own oclif, so the inferred parse type
|
|
33
|
+
* isn't nameable from a strict consumer package (TS2742). Confining the cast
|
|
34
|
+
* here keeps every plugin's `Execute()` cast-free. Pass
|
|
35
|
+
* `Interfaces.InferredFlags<typeof YourPlugin.flags>` as `T`.
|
|
36
|
+
*/
|
|
37
|
+
GetFlags() {
|
|
38
|
+
return this.parsedFlags;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* oclif entry point — do NOT override in subclasses. Override {@link Execute}.
|
|
42
|
+
*/
|
|
43
|
+
async run() {
|
|
44
|
+
// Parse against the concrete subclass so `baseFlags` + the subclass `flags`
|
|
45
|
+
// both resolve. `this.constructor` is the concrete command class at runtime.
|
|
46
|
+
const ctor = this.constructor;
|
|
47
|
+
const { flags } = await this.parse(ctor);
|
|
48
|
+
this.parsedFlags = flags;
|
|
49
|
+
const f = flags;
|
|
50
|
+
const format = f.format ?? 'text';
|
|
51
|
+
const verbose = !!f.verbose;
|
|
52
|
+
const noBanner = !!f['no-banner'];
|
|
53
|
+
this.Host = new MJCLIRuntimeHost(format, verbose, noBanner);
|
|
54
|
+
// Announce runtime expectation up front (stderr in JSON mode) so an agent
|
|
55
|
+
// reading the stream can budget its timeout — see plan §5/§6.
|
|
56
|
+
if (ctor.Usage) {
|
|
57
|
+
this.Host.AnnounceRuntime(ctor.Usage);
|
|
58
|
+
}
|
|
59
|
+
const result = await this.Execute();
|
|
60
|
+
this.Host.Emit(result);
|
|
61
|
+
// Optional cleanup hook (e.g. close DB pools, reset singletons). Runs after
|
|
62
|
+
// Emit so the result is always rendered even when cleanup hard-exits.
|
|
63
|
+
await this.Cleanup(result);
|
|
64
|
+
// Default exit handling. Plugins that must force-exit to kill lingering
|
|
65
|
+
// handles (e.g. embedding workers) do so inside Cleanup().
|
|
66
|
+
if (!result.success) {
|
|
67
|
+
this.exit(1);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Optional post-Emit hook. Override to release resources (DB pools, singletons)
|
|
72
|
+
* and, when necessary, `process.exit()` to terminate lingering background work.
|
|
73
|
+
*/
|
|
74
|
+
async Cleanup(_result) {
|
|
75
|
+
// no-op by default
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=base-cli-plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-cli-plugin.js","sourceRoot":"","sources":["../src/base-cli-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGlD;;;;;;;;;;;;GAYG;AACH,MAAM,OAAgB,aAAc,SAAQ,OAAO;IACjD,gFAAgF;aAChE,cAAS,GAAG;QAC1B,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC;YACnB,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC;YAC/B,OAAO,EAAE,MAAM;YACf,WAAW,EAAE,4EAA4E;SAC1F,CAAC;QACF,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,sBAAsB,EAAE,CAAC;QAC1F,WAAW,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,kDAAkD,EAAE,CAAC;KAChH,CAAC;IAeF;;;;;;;;;OASG;IACO,QAAQ;QAChB,OAAO,IAAI,CAAC,WAA2B,CAAC;IAC1C,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,GAAG;QACP,4EAA4E;QAC5E,6EAA6E;QAC7E,MAAM,IAAI,GAAG,IAAI,CAAC,WAAmC,CAAC;QACtD,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QAEzB,MAAM,CAAC,GAAG,KAAsE,CAAC;QACjF,MAAM,MAAM,GAAI,CAAC,CAAC,MAAuB,IAAI,MAAM,CAAC;QACpD,MAAM,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;QAC5B,MAAM,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;QAElC,IAAI,CAAC,IAAI,GAAG,IAAI,gBAAgB,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QAE5D,0EAA0E;QAC1E,8DAA8D;QAC9D,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxC,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACpC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAEvB,4EAA4E;QAC5E,sEAAsE;QACtE,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAE3B,wEAAwE;QACxE,2DAA2D;QAC3D,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACf,CAAC;IACH,CAAC;IAED;;;OAGG;IACO,KAAK,CAAC,OAAO,CAAC,OAAoB;QAC1C,mBAAmB;IACrB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from './types.js';
|
|
2
|
+
export { MJCLIRuntimeHost } from './runtime-host.js';
|
|
3
|
+
export { BaseCLIPlugin } from './base-cli-plugin.js';
|
|
4
|
+
export { SerializeResult } from './serialize.js';
|
|
5
|
+
export { CLIPluginRegistry, PLUGIN_CONFIG_FILENAME, } from './plugin-registry.js';
|
|
6
|
+
export type { UsageDomainSummary, UsageDomainMap, UsageDomainDetail, PluginLoadResult, } from './plugin-registry.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EACL,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from './types.js';
|
|
2
|
+
export { MJCLIRuntimeHost } from './runtime-host.js';
|
|
3
|
+
export { BaseCLIPlugin } from './base-cli-plugin.js';
|
|
4
|
+
export { SerializeResult } from './serialize.js';
|
|
5
|
+
export { CLIPluginRegistry, PLUGIN_CONFIG_FILENAME, } from './plugin-registry.js';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EACL,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { MJCLIResult, PluginUsage, RuntimeHint } from './types.js';
|
|
2
|
+
/** Filename enumerating active plugin entry points (plan §4 / D8). */
|
|
3
|
+
export declare const PLUGIN_CONFIG_FILENAME = "mj-cli-plugins.json";
|
|
4
|
+
/** Outcome of {@link CLIPluginRegistry.LoadPluginsFromConfig}. */
|
|
5
|
+
export interface PluginLoadResult {
|
|
6
|
+
/** Specifiers that imported successfully. */
|
|
7
|
+
loaded: string[];
|
|
8
|
+
/** Specifiers that failed to import, with the error, so callers can log them. */
|
|
9
|
+
failed: Array<{
|
|
10
|
+
specifier: string;
|
|
11
|
+
error: string;
|
|
12
|
+
}>;
|
|
13
|
+
}
|
|
14
|
+
/** Tier-1 domain map entry. */
|
|
15
|
+
export interface UsageDomainSummary {
|
|
16
|
+
domain: string;
|
|
17
|
+
summary: string;
|
|
18
|
+
runtime: RuntimeHint['class'];
|
|
19
|
+
}
|
|
20
|
+
/** Tier-1 result payload. */
|
|
21
|
+
export interface UsageDomainMap {
|
|
22
|
+
guidance: string;
|
|
23
|
+
domains: UsageDomainSummary[];
|
|
24
|
+
}
|
|
25
|
+
/** Tier-2 result payload (one domain's commands). */
|
|
26
|
+
export interface UsageDomainDetail {
|
|
27
|
+
domain: string;
|
|
28
|
+
commands: PluginUsage[];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Loads CLI plugin packages and composes the progressive-disclosure usage surface
|
|
32
|
+
* (`mj usage` → `mj <domain> usage`) dynamically from each registered plugin's
|
|
33
|
+
* `static Usage` (plan §5, the linearis model). There is no central hardcoded
|
|
34
|
+
* help file — usage is whatever plugins are loaded, including third-party ones.
|
|
35
|
+
*/
|
|
36
|
+
export declare class CLIPluginRegistry {
|
|
37
|
+
/**
|
|
38
|
+
* Walks up from {@link startDir} looking for `mj-cli-plugins.json`, then
|
|
39
|
+
* dynamic-imports each listed entry point so its `@RegisterClass(BaseCLIPlugin, …)`
|
|
40
|
+
* decorators populate the ClassFactory. Safe to call repeatedly — imports are
|
|
41
|
+
* cached by the module loader. Returns the list of specifiers it loaded.
|
|
42
|
+
*/
|
|
43
|
+
static LoadPluginsFromConfig(startDir?: string): Promise<PluginLoadResult>;
|
|
44
|
+
/**
|
|
45
|
+
* Resolves a plugin entry point. Package specifiers (`@scope/pkg`,
|
|
46
|
+
* `@scope/pkg/plugins`) import as-is; relative paths resolve against the config
|
|
47
|
+
* file's directory and convert to a file URL for ESM import.
|
|
48
|
+
*/
|
|
49
|
+
private static importSpecifier;
|
|
50
|
+
/** First `mj-cli-plugins.json` found walking up from {@link startDir}. */
|
|
51
|
+
private static findConfig;
|
|
52
|
+
/** Every registered plugin's usage metadata, de-duplicated by command key. */
|
|
53
|
+
static GetAllUsage(): PluginUsage[];
|
|
54
|
+
/** Tier-1: the domain map — each domain + one-line summary + runtime class. */
|
|
55
|
+
static BuildDomainMap(): UsageDomainMap;
|
|
56
|
+
/** Tier-2: every command in {@link domain} with summary, flags, examples, runtime. */
|
|
57
|
+
static BuildDomainDetail(domain: string): UsageDomainDetail;
|
|
58
|
+
/** Wraps a tier payload in the universal {@link MJCLIResult} shape. */
|
|
59
|
+
static AsResult(command: string, data: Record<string, unknown>): MJCLIResult;
|
|
60
|
+
/**
|
|
61
|
+
* A domain summary: if a single command's summary best represents the domain
|
|
62
|
+
* use it; otherwise join the command summaries compactly.
|
|
63
|
+
*/
|
|
64
|
+
private static domainSummary;
|
|
65
|
+
/**
|
|
66
|
+
* The "loosest" runtime class across a domain's commands, so an agent setting a
|
|
67
|
+
* single domain-wide timeout errs on the generous side.
|
|
68
|
+
* Ordering: variable > slow > moderate > fast.
|
|
69
|
+
*/
|
|
70
|
+
private static domainRuntimeClass;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=plugin-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-registry.d.ts","sourceRoot":"","sources":["../src/plugin-registry.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAErE,sEAAsE;AACtE,eAAO,MAAM,sBAAsB,wBAAwB,CAAC;AAM5D,kEAAkE;AAClE,MAAM,WAAW,gBAAgB;IAC/B,6CAA6C;IAC7C,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,iFAAiF;IACjF,MAAM,EAAE,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACrD;AAED,+BAA+B;AAC/B,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;CAC/B;AAED,6BAA6B;AAC7B,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,kBAAkB,EAAE,CAAC;CAC/B;AAED,qDAAqD;AACrD,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,WAAW,EAAE,CAAC;CACzB;AAID;;;;;GAKG;AACH,qBAAa,iBAAiB;IAC5B;;;;;OAKG;WACU,qBAAqB,CAAC,QAAQ,GAAE,MAAsB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA+B/F;;;;OAIG;mBACkB,eAAe;IASpC,0EAA0E;IAC1E,OAAO,CAAC,MAAM,CAAC,UAAU;IAYzB,8EAA8E;IAC9E,MAAM,CAAC,WAAW,IAAI,WAAW,EAAE;IAYnC,+EAA+E;IAC/E,MAAM,CAAC,cAAc,IAAI,cAAc;IAoBvC,sFAAsF;IACtF,MAAM,CAAC,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB;IAQ3D,uEAAuE;IACvE,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,WAAW;IAI5E;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,aAAa;IAU5B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,kBAAkB;CAQlC"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'fs';
|
|
2
|
+
import { dirname, isAbsolute, resolve } from 'path';
|
|
3
|
+
import { pathToFileURL } from 'url';
|
|
4
|
+
import { MJGlobal } from '@memberjunction/global';
|
|
5
|
+
import { BaseCLIPlugin } from './base-cli-plugin.js';
|
|
6
|
+
/** Filename enumerating active plugin entry points (plan §4 / D8). */
|
|
7
|
+
export const PLUGIN_CONFIG_FILENAME = 'mj-cli-plugins.json';
|
|
8
|
+
const GUIDANCE = 'Run `mj <domain> usage` before invoking. Do NOT guess flags or subcommands.';
|
|
9
|
+
/**
|
|
10
|
+
* Loads CLI plugin packages and composes the progressive-disclosure usage surface
|
|
11
|
+
* (`mj usage` → `mj <domain> usage`) dynamically from each registered plugin's
|
|
12
|
+
* `static Usage` (plan §5, the linearis model). There is no central hardcoded
|
|
13
|
+
* help file — usage is whatever plugins are loaded, including third-party ones.
|
|
14
|
+
*/
|
|
15
|
+
export class CLIPluginRegistry {
|
|
16
|
+
/**
|
|
17
|
+
* Walks up from {@link startDir} looking for `mj-cli-plugins.json`, then
|
|
18
|
+
* dynamic-imports each listed entry point so its `@RegisterClass(BaseCLIPlugin, …)`
|
|
19
|
+
* decorators populate the ClassFactory. Safe to call repeatedly — imports are
|
|
20
|
+
* cached by the module loader. Returns the list of specifiers it loaded.
|
|
21
|
+
*/
|
|
22
|
+
static async LoadPluginsFromConfig(startDir = process.cwd()) {
|
|
23
|
+
const result = { loaded: [], failed: [] };
|
|
24
|
+
const configPath = this.findConfig(startDir);
|
|
25
|
+
if (!configPath)
|
|
26
|
+
return result;
|
|
27
|
+
let parsed;
|
|
28
|
+
try {
|
|
29
|
+
parsed = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
result.failed.push({ specifier: configPath, error: `Invalid ${PLUGIN_CONFIG_FILENAME}: ${e instanceof Error ? e.message : String(e)}` });
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
const specifiers = Array.isArray(parsed.plugins) ? parsed.plugins : [];
|
|
36
|
+
const configDir = dirname(configPath);
|
|
37
|
+
for (const specifier of specifiers) {
|
|
38
|
+
try {
|
|
39
|
+
await this.importSpecifier(specifier, configDir);
|
|
40
|
+
result.loaded.push(specifier);
|
|
41
|
+
}
|
|
42
|
+
catch (e) {
|
|
43
|
+
// A broken/optional plugin must not take down the whole CLI; record the
|
|
44
|
+
// failure so the caller can surface it under --verbose rather than
|
|
45
|
+
// swallowing it silently.
|
|
46
|
+
result.failed.push({ specifier, error: e instanceof Error ? e.message : String(e) });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Resolves a plugin entry point. Package specifiers (`@scope/pkg`,
|
|
53
|
+
* `@scope/pkg/plugins`) import as-is; relative paths resolve against the config
|
|
54
|
+
* file's directory and convert to a file URL for ESM import.
|
|
55
|
+
*/
|
|
56
|
+
static async importSpecifier(specifier, configDir) {
|
|
57
|
+
if (specifier.startsWith('.') || isAbsolute(specifier)) {
|
|
58
|
+
const abs = isAbsolute(specifier) ? specifier : resolve(configDir, specifier);
|
|
59
|
+
await import(pathToFileURL(abs).href);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
await import(specifier);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/** First `mj-cli-plugins.json` found walking up from {@link startDir}. */
|
|
66
|
+
static findConfig(startDir) {
|
|
67
|
+
let dir = resolve(startDir);
|
|
68
|
+
// eslint-disable-next-line no-constant-condition
|
|
69
|
+
while (true) {
|
|
70
|
+
const candidate = resolve(dir, PLUGIN_CONFIG_FILENAME);
|
|
71
|
+
if (existsSync(candidate))
|
|
72
|
+
return candidate;
|
|
73
|
+
const parent = dirname(dir);
|
|
74
|
+
if (parent === dir)
|
|
75
|
+
return null;
|
|
76
|
+
dir = parent;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/** Every registered plugin's usage metadata, de-duplicated by command key. */
|
|
80
|
+
static GetAllUsage() {
|
|
81
|
+
const regs = MJGlobal.Instance.ClassFactory.GetAllRegistrations(BaseCLIPlugin);
|
|
82
|
+
const byCommand = new Map();
|
|
83
|
+
for (const reg of regs) {
|
|
84
|
+
const usage = reg.SubClass?.Usage;
|
|
85
|
+
if (usage?.domain && usage?.command && !byCommand.has(usage.command)) {
|
|
86
|
+
byCommand.set(usage.command, usage);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return [...byCommand.values()];
|
|
90
|
+
}
|
|
91
|
+
/** Tier-1: the domain map — each domain + one-line summary + runtime class. */
|
|
92
|
+
static BuildDomainMap() {
|
|
93
|
+
const usages = this.GetAllUsage();
|
|
94
|
+
const byDomain = new Map();
|
|
95
|
+
for (const u of usages) {
|
|
96
|
+
const list = byDomain.get(u.domain) ?? [];
|
|
97
|
+
list.push(u);
|
|
98
|
+
byDomain.set(u.domain, list);
|
|
99
|
+
}
|
|
100
|
+
const domains = [...byDomain.entries()]
|
|
101
|
+
.map(([domain, cmds]) => ({
|
|
102
|
+
domain,
|
|
103
|
+
summary: this.domainSummary(domain, cmds),
|
|
104
|
+
runtime: this.domainRuntimeClass(cmds),
|
|
105
|
+
}))
|
|
106
|
+
.sort((a, b) => a.domain.localeCompare(b.domain));
|
|
107
|
+
return { guidance: GUIDANCE, domains };
|
|
108
|
+
}
|
|
109
|
+
/** Tier-2: every command in {@link domain} with summary, flags, examples, runtime. */
|
|
110
|
+
static BuildDomainDetail(domain) {
|
|
111
|
+
const target = domain.trim().toLowerCase();
|
|
112
|
+
const commands = this.GetAllUsage()
|
|
113
|
+
.filter((u) => u.domain.toLowerCase() === target)
|
|
114
|
+
.sort((a, b) => a.command.localeCompare(b.command));
|
|
115
|
+
return { domain, commands };
|
|
116
|
+
}
|
|
117
|
+
/** Wraps a tier payload in the universal {@link MJCLIResult} shape. */
|
|
118
|
+
static AsResult(command, data) {
|
|
119
|
+
return { success: true, command, durationSeconds: 0, data };
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* A domain summary: if a single command's summary best represents the domain
|
|
123
|
+
* use it; otherwise join the command summaries compactly.
|
|
124
|
+
*/
|
|
125
|
+
static domainSummary(domain, cmds) {
|
|
126
|
+
if (cmds.length === 1)
|
|
127
|
+
return cmds[0].summary;
|
|
128
|
+
// Prefer a command whose key equals the domain (e.g. 'codegen').
|
|
129
|
+
const headline = cmds.find((c) => c.command === domain);
|
|
130
|
+
if (headline)
|
|
131
|
+
return headline.summary;
|
|
132
|
+
// Avoid a run-on line for 3+ commands: lead with the first summary + a count.
|
|
133
|
+
if (cmds.length >= 3)
|
|
134
|
+
return `${cmds[0].summary} (+${cmds.length - 1} more commands)`;
|
|
135
|
+
return cmds.map((c) => c.summary).join(' ');
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* The "loosest" runtime class across a domain's commands, so an agent setting a
|
|
139
|
+
* single domain-wide timeout errs on the generous side.
|
|
140
|
+
* Ordering: variable > slow > moderate > fast.
|
|
141
|
+
*/
|
|
142
|
+
static domainRuntimeClass(cmds) {
|
|
143
|
+
const order = ['fast', 'moderate', 'slow', 'variable'];
|
|
144
|
+
let worst = 'fast';
|
|
145
|
+
for (const c of cmds) {
|
|
146
|
+
if (order.indexOf(c.runtime.class) > order.indexOf(worst))
|
|
147
|
+
worst = c.runtime.class;
|
|
148
|
+
}
|
|
149
|
+
return worst;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=plugin-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-registry.js","sourceRoot":"","sources":["../src/plugin-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { IMJCLIRuntimeHost, LogLevel, MJCLIResult, OutputFormat, PluginUsage } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Default implementation of {@link IMJCLIRuntimeHost}.
|
|
4
|
+
*
|
|
5
|
+
* - **Text mode** (default): an `ora` spinner with a live elapsed timer for steps,
|
|
6
|
+
* chalk-colored logs, and a generic human summary on {@link MJCLIRuntimeHost.Emit}
|
|
7
|
+
* when the plugin hasn't already rendered its own. Plugins that want rich,
|
|
8
|
+
* command-specific text (e.g. a push summary box) build the string themselves
|
|
9
|
+
* and pass it through {@link MJCLIRuntimeHost.Log} — the host never hard-codes
|
|
10
|
+
* per-command formatting.
|
|
11
|
+
* - **JSON mode**: every decorative call (steps, logs, runtime advisory) goes to
|
|
12
|
+
* **stderr** so the {@link MJCLIResult} JSON on **stdout** stays clean and
|
|
13
|
+
* pipeable (plan D4: `mj sync push --format=json | jq .errors`).
|
|
14
|
+
* - **MD mode**: the result is emitted as a fenced ```json block on stdout
|
|
15
|
+
* (forward-looking slot for AI chat UIs, plan D10).
|
|
16
|
+
*/
|
|
17
|
+
export declare class MJCLIRuntimeHost implements IMJCLIRuntimeHost {
|
|
18
|
+
readonly Format: OutputFormat;
|
|
19
|
+
readonly Verbose: boolean;
|
|
20
|
+
private readonly noBanner;
|
|
21
|
+
/** Process-start, used for the "· N total" running clock in text mode. */
|
|
22
|
+
private readonly startTime;
|
|
23
|
+
private spinner;
|
|
24
|
+
private stepBaseMessage;
|
|
25
|
+
private stepStart;
|
|
26
|
+
private ticker;
|
|
27
|
+
constructor(format?: OutputFormat, verbose?: boolean, noBanner?: boolean);
|
|
28
|
+
/** Spinners/colors are only appropriate in text mode on a real TTY. */
|
|
29
|
+
private get textMode();
|
|
30
|
+
private fmtMs;
|
|
31
|
+
private stopTicker;
|
|
32
|
+
/**
|
|
33
|
+
* Begin the live elapsed timer for the current step. Each call resets the
|
|
34
|
+
* clock so the timer reflects the CURRENT step. Unref'd so it never holds the
|
|
35
|
+
* event loop open on its own.
|
|
36
|
+
*/
|
|
37
|
+
private startTicker;
|
|
38
|
+
/** Structured progress event on stderr (JSON mode) so stdout stays result-only. */
|
|
39
|
+
private emitStderrEvent;
|
|
40
|
+
StartStep(label: string): void;
|
|
41
|
+
UpdateStep(label: string): void;
|
|
42
|
+
SucceedStep(label: string, detail?: string): void;
|
|
43
|
+
FailStep(label: string, detail?: string): void;
|
|
44
|
+
Log(message: string, level?: LogLevel): void;
|
|
45
|
+
AnnounceRuntime(usage: PluginUsage): void;
|
|
46
|
+
Emit(result: MJCLIResult): void;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=runtime-host.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-host.d.ts","sourceRoot":"","sources":["../src/runtime-host.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAGnG;;;;;;;;;;;;;;GAcG;AACH,qBAAa,gBAAiB,YAAW,iBAAiB;IACxD,SAAgB,MAAM,EAAE,YAAY,CAAC;IACrC,SAAgB,OAAO,EAAE,OAAO,CAAC;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAU;IAEnC,0EAA0E;IAC1E,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IAExC,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,eAAe,CAAM;IAC7B,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,MAAM,CAA+B;gBAEjC,MAAM,GAAE,YAAqB,EAAE,OAAO,UAAQ,EAAE,QAAQ,UAAQ;IAS5E,uEAAuE;IACvE,OAAO,KAAK,QAAQ,GAEnB;IAED,OAAO,CAAC,KAAK;IAIb,OAAO,CAAC,UAAU;IAOlB;;;;OAIG;IACH,OAAO,CAAC,WAAW;IAenB,mFAAmF;IACnF,OAAO,CAAC,eAAe;IAIhB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAW9B,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAU/B,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAejD,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAc9C,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,QAAiB,GAAG,IAAI;IAapD,eAAe,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAezC,IAAI,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;CAWvC"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import ora from 'ora-classic';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { SerializeResult } from './serialize.js';
|
|
4
|
+
/**
|
|
5
|
+
* Default implementation of {@link IMJCLIRuntimeHost}.
|
|
6
|
+
*
|
|
7
|
+
* - **Text mode** (default): an `ora` spinner with a live elapsed timer for steps,
|
|
8
|
+
* chalk-colored logs, and a generic human summary on {@link MJCLIRuntimeHost.Emit}
|
|
9
|
+
* when the plugin hasn't already rendered its own. Plugins that want rich,
|
|
10
|
+
* command-specific text (e.g. a push summary box) build the string themselves
|
|
11
|
+
* and pass it through {@link MJCLIRuntimeHost.Log} — the host never hard-codes
|
|
12
|
+
* per-command formatting.
|
|
13
|
+
* - **JSON mode**: every decorative call (steps, logs, runtime advisory) goes to
|
|
14
|
+
* **stderr** so the {@link MJCLIResult} JSON on **stdout** stays clean and
|
|
15
|
+
* pipeable (plan D4: `mj sync push --format=json | jq .errors`).
|
|
16
|
+
* - **MD mode**: the result is emitted as a fenced ```json block on stdout
|
|
17
|
+
* (forward-looking slot for AI chat UIs, plan D10).
|
|
18
|
+
*/
|
|
19
|
+
export class MJCLIRuntimeHost {
|
|
20
|
+
constructor(format = 'text', verbose = false, noBanner = false) {
|
|
21
|
+
/** Process-start, used for the "· N total" running clock in text mode. */
|
|
22
|
+
this.startTime = Date.now();
|
|
23
|
+
this.spinner = null;
|
|
24
|
+
this.stepBaseMessage = '';
|
|
25
|
+
this.stepStart = 0;
|
|
26
|
+
this.ticker = null;
|
|
27
|
+
this.Format = format;
|
|
28
|
+
this.Verbose = verbose;
|
|
29
|
+
// `--no-banner` is handled globally by the CLI prerun hook (it strips the flag
|
|
30
|
+
// from argv so not-yet-migrated commands don't fail oclif's strict parser) and
|
|
31
|
+
// signalled here via env, so honor either source.
|
|
32
|
+
this.noBanner = noBanner || process.env.MJ_CLI_NO_BANNER === '1';
|
|
33
|
+
}
|
|
34
|
+
/** Spinners/colors are only appropriate in text mode on a real TTY. */
|
|
35
|
+
get textMode() {
|
|
36
|
+
return this.Format === 'text';
|
|
37
|
+
}
|
|
38
|
+
fmtMs(ms) {
|
|
39
|
+
return ms < 1000 ? `${ms}ms` : `${(ms / 1000).toFixed(1)}s`;
|
|
40
|
+
}
|
|
41
|
+
stopTicker() {
|
|
42
|
+
if (this.ticker) {
|
|
43
|
+
clearInterval(this.ticker);
|
|
44
|
+
this.ticker = null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Begin the live elapsed timer for the current step. Each call resets the
|
|
49
|
+
* clock so the timer reflects the CURRENT step. Unref'd so it never holds the
|
|
50
|
+
* event loop open on its own.
|
|
51
|
+
*/
|
|
52
|
+
startTicker(message) {
|
|
53
|
+
this.stopTicker();
|
|
54
|
+
this.stepBaseMessage = message;
|
|
55
|
+
this.stepStart = Date.now();
|
|
56
|
+
if (this.spinner) {
|
|
57
|
+
this.ticker = setInterval(() => {
|
|
58
|
+
if (!this.spinner)
|
|
59
|
+
return;
|
|
60
|
+
const step = this.fmtMs(Date.now() - this.stepStart);
|
|
61
|
+
const total = this.fmtMs(Date.now() - this.startTime);
|
|
62
|
+
this.spinner.text = `${this.stepBaseMessage} ${chalk.gray(`· ${step} · ${total} total`)}`;
|
|
63
|
+
}, 100);
|
|
64
|
+
this.ticker.unref?.();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** Structured progress event on stderr (JSON mode) so stdout stays result-only. */
|
|
68
|
+
emitStderrEvent(event) {
|
|
69
|
+
process.stderr.write(JSON.stringify(event) + '\n');
|
|
70
|
+
}
|
|
71
|
+
StartStep(label) {
|
|
72
|
+
if (this.textMode) {
|
|
73
|
+
if (!this.spinner)
|
|
74
|
+
this.spinner = ora();
|
|
75
|
+
this.spinner.start(label);
|
|
76
|
+
this.startTicker(label);
|
|
77
|
+
}
|
|
78
|
+
else if (this.Format === 'json') {
|
|
79
|
+
this.emitStderrEvent({ event: 'step', label });
|
|
80
|
+
}
|
|
81
|
+
// md mode: steps are noise in a fenced block — suppress.
|
|
82
|
+
}
|
|
83
|
+
UpdateStep(label) {
|
|
84
|
+
if (this.textMode) {
|
|
85
|
+
if (!this.spinner)
|
|
86
|
+
this.spinner = ora();
|
|
87
|
+
this.spinner.text = label;
|
|
88
|
+
this.startTicker(label);
|
|
89
|
+
}
|
|
90
|
+
else if (this.Format === 'json') {
|
|
91
|
+
this.emitStderrEvent({ event: 'step', label });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
SucceedStep(label, detail) {
|
|
95
|
+
this.stopTicker();
|
|
96
|
+
if (this.textMode) {
|
|
97
|
+
const elapsed = this.stepStart ? chalk.gray(` (${this.fmtMs(Date.now() - this.stepStart)})`) : '';
|
|
98
|
+
const text = detail ? `${label} ${chalk.gray(detail)}${elapsed}` : `${label}${elapsed}`;
|
|
99
|
+
if (this.spinner) {
|
|
100
|
+
this.spinner.stopAndPersist({ symbol: chalk.green('✓'), text });
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
process.stdout.write(`${chalk.green('✓')} ${text}\n`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else if (this.Format === 'json') {
|
|
107
|
+
this.emitStderrEvent({ event: 'step-done', label, detail });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
FailStep(label, detail) {
|
|
111
|
+
this.stopTicker();
|
|
112
|
+
if (this.textMode) {
|
|
113
|
+
const text = detail ? `${label} ${chalk.gray(detail)}` : label;
|
|
114
|
+
if (this.spinner) {
|
|
115
|
+
this.spinner.fail(text);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
process.stderr.write(`${chalk.red('✗')} ${text}\n`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
else if (this.Format === 'json') {
|
|
122
|
+
this.emitStderrEvent({ event: 'step-failed', label, detail });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
Log(message, level = 'info') {
|
|
126
|
+
if (this.textMode) {
|
|
127
|
+
// A spinner mid-render would garble a raw write; stop it first.
|
|
128
|
+
if (this.spinner?.isSpinning)
|
|
129
|
+
this.spinner.stop();
|
|
130
|
+
const painted = level === 'error' ? chalk.red(message) : level === 'warn' ? chalk.yellow(message) : message;
|
|
131
|
+
// eslint-disable-next-line no-console
|
|
132
|
+
(level === 'error' ? console.error : console.log)(painted);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
// Keep stdout clean for the JSON/MD result — all human logging → stderr.
|
|
136
|
+
process.stderr.write(message + '\n');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
AnnounceRuntime(usage) {
|
|
140
|
+
// Fast commands don't warrant an advisory — it would just be noise.
|
|
141
|
+
if (!usage?.runtime || usage.runtime.class === 'fast')
|
|
142
|
+
return;
|
|
143
|
+
if (this.noBanner)
|
|
144
|
+
return;
|
|
145
|
+
if (this.Format === 'json') {
|
|
146
|
+
this.emitStderrEvent({ event: 'start', command: usage.command, runtime: usage.runtime });
|
|
147
|
+
}
|
|
148
|
+
else if (this.textMode) {
|
|
149
|
+
const r = usage.runtime;
|
|
150
|
+
const secs = r.typicalSeconds ? `~${r.typicalSeconds}s` : r.class;
|
|
151
|
+
const note = r.note ? ` — ${r.note}` : '';
|
|
152
|
+
process.stderr.write(chalk.gray(`⏱ ${usage.command}: typically ${secs} (${r.class})${note}\n`));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
Emit(result) {
|
|
156
|
+
this.stopTicker();
|
|
157
|
+
if (this.spinner?.isSpinning)
|
|
158
|
+
this.spinner.stop();
|
|
159
|
+
if (this.Format === 'json' || this.Format === 'md') {
|
|
160
|
+
process.stdout.write(SerializeResult(result, this.Format) + '\n');
|
|
161
|
+
}
|
|
162
|
+
// Text mode: the plugin is responsible for its own rich human output (via
|
|
163
|
+
// Log/StartStep/SucceedStep). Emit deliberately prints nothing extra so we
|
|
164
|
+
// never double-render a summary.
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=runtime-host.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-host.js","sourceRoot":"","sources":["../src/runtime-host.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { MJCLIResult, OutputFormat } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Single source of truth for serializing an {@link MJCLIResult} per format.
|
|
4
|
+
* Both the runtime host's `Emit` and the usage commands call this so JSON/MD
|
|
5
|
+
* envelopes are always rendered identically. Returns the empty string for
|
|
6
|
+
* `text` — in text mode the plugin renders its own human output, not a result
|
|
7
|
+
* blob. No trailing newline; callers add one.
|
|
8
|
+
*/
|
|
9
|
+
export declare function SerializeResult(result: MJCLIResult, format: OutputFormat): string;
|
|
10
|
+
//# sourceMappingURL=serialize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../src/serialize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEzD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,GAAG,MAAM,CAIjF"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for serializing an {@link MJCLIResult} per format.
|
|
3
|
+
* Both the runtime host's `Emit` and the usage commands call this so JSON/MD
|
|
4
|
+
* envelopes are always rendered identically. Returns the empty string for
|
|
5
|
+
* `text` — in text mode the plugin renders its own human output, not a result
|
|
6
|
+
* blob. No trailing newline; callers add one.
|
|
7
|
+
*/
|
|
8
|
+
export function SerializeResult(result, format) {
|
|
9
|
+
if (format === 'json')
|
|
10
|
+
return JSON.stringify(result, null, 2);
|
|
11
|
+
if (format === 'md')
|
|
12
|
+
return '```json\n' + JSON.stringify(result, null, 2) + '\n```';
|
|
13
|
+
return '';
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=serialize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serialize.js","sourceRoot":"","sources":["../src/serialize.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,MAAmB,EAAE,MAAoB;IACvE,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC9D,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC;IACpF,OAAO,EAAE,CAAC;AACZ,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core type definitions for the pluggable MJ CLI.
|
|
3
|
+
*
|
|
4
|
+
* The guiding principle (plan D2): plugins return *data* — a {@link MJCLIResult} —
|
|
5
|
+
* and the {@link IMJCLIRuntimeHost} renders it per the active {@link OutputFormat}.
|
|
6
|
+
* No `ora`, `chalk`, or `console` calls live inside a plugin's business logic.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Output format selected via the global `--format` flag.
|
|
10
|
+
* - `text`: human-readable (the default; spinners + chalk).
|
|
11
|
+
* - `json`: machine-readable result on stdout, decorative output on stderr.
|
|
12
|
+
* - `md`: Markdown-fenced result block, for AI chat UIs (forward-looking, plan D10).
|
|
13
|
+
*/
|
|
14
|
+
export type OutputFormat = 'text' | 'json' | 'md';
|
|
15
|
+
/** Severity for {@link IMJCLIRuntimeHost.Log}. */
|
|
16
|
+
export type LogLevel = 'info' | 'warn' | 'error';
|
|
17
|
+
/**
|
|
18
|
+
* How long a command typically runs, so an AI agent wrapping `mj` in a shell
|
|
19
|
+
* timeout can budget correctly rather than killing a healthy long-running
|
|
20
|
+
* command midway (plan §1d / D12).
|
|
21
|
+
*/
|
|
22
|
+
export interface RuntimeHint {
|
|
23
|
+
/**
|
|
24
|
+
* - `fast`: <5s (no start advisory emitted).
|
|
25
|
+
* - `moderate`: 5–60s.
|
|
26
|
+
* - `slow`: >60s.
|
|
27
|
+
* - `variable`: depends on scope — see {@link RuntimeHint.note}.
|
|
28
|
+
*/
|
|
29
|
+
class: 'fast' | 'moderate' | 'slow' | 'variable';
|
|
30
|
+
/** Best-guess midpoint (seconds) an agent can use to set a timeout. */
|
|
31
|
+
typicalSeconds?: number;
|
|
32
|
+
/** e.g. 'scales with entity count', 'full migration ≫ incremental'. */
|
|
33
|
+
note?: string;
|
|
34
|
+
}
|
|
35
|
+
/** One flag described in a plugin's usage metadata. */
|
|
36
|
+
export interface PluginUsageFlag {
|
|
37
|
+
name: string;
|
|
38
|
+
type: string;
|
|
39
|
+
description: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Per-plugin usage + runtime metadata. Drives the progressive-disclosure
|
|
43
|
+
* `mj usage` (tier 1) and `mj <domain> usage` (tier 2) surface and the timeout
|
|
44
|
+
* advisory (plan §5). Co-located with the plugin so it can never drift from the
|
|
45
|
+
* actual flags.
|
|
46
|
+
*/
|
|
47
|
+
export interface PluginUsage {
|
|
48
|
+
/** Groups commands in `mj usage` — e.g. 'sync', 'codegen', 'migrate'. */
|
|
49
|
+
domain: string;
|
|
50
|
+
/** The invocation key — e.g. 'sync:push', 'codegen', 'migrate'. */
|
|
51
|
+
command: string;
|
|
52
|
+
/** One line, shown in the `mj usage` domain map. Keep it terse. */
|
|
53
|
+
summary: string;
|
|
54
|
+
/** Fuller prose, shown only in `mj <domain> usage`. */
|
|
55
|
+
description?: string;
|
|
56
|
+
flags?: PluginUsageFlag[];
|
|
57
|
+
/** Copy-pasteable invocations. */
|
|
58
|
+
examples?: string[];
|
|
59
|
+
runtime: RuntimeHint;
|
|
60
|
+
}
|
|
61
|
+
/** A single failure, collected (not interleaved) so an agent can read them as a list. */
|
|
62
|
+
export interface MJCLIResultError {
|
|
63
|
+
/** Entity name, file path, phase — whatever is relevant. */
|
|
64
|
+
context?: string;
|
|
65
|
+
message: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Universal result shape every plugin returns (plan D6). Command-specific detail
|
|
69
|
+
* goes in the typed {@link MJCLIResult.data} field; failures always go in
|
|
70
|
+
* {@link MJCLIResult.errors} with full detail (not just a count).
|
|
71
|
+
*/
|
|
72
|
+
export interface MJCLIResult {
|
|
73
|
+
success: boolean;
|
|
74
|
+
/** 'sync:push', 'codegen', 'migrate', etc. */
|
|
75
|
+
command: string;
|
|
76
|
+
durationSeconds: number;
|
|
77
|
+
data?: Record<string, unknown>;
|
|
78
|
+
errors?: MJCLIResultError[];
|
|
79
|
+
warnings?: string[];
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* The runtime host abstracts all stdio. A plugin talks to the host through this
|
|
83
|
+
* interface; the host decides whether a call becomes a spinner line, a JSON event
|
|
84
|
+
* on stderr, or nothing — based on the active {@link OutputFormat}.
|
|
85
|
+
*/
|
|
86
|
+
export interface IMJCLIRuntimeHost {
|
|
87
|
+
/** The active output format. Plugins gate human-only text rendering on this. */
|
|
88
|
+
readonly Format: OutputFormat;
|
|
89
|
+
/** Whether `--verbose` was set. */
|
|
90
|
+
readonly Verbose: boolean;
|
|
91
|
+
StartStep(label: string): void;
|
|
92
|
+
UpdateStep(label: string): void;
|
|
93
|
+
SucceedStep(label: string, detail?: string): void;
|
|
94
|
+
FailStep(label: string, detail?: string): void;
|
|
95
|
+
Log(message: string, level?: LogLevel): void;
|
|
96
|
+
/**
|
|
97
|
+
* Runtime advisory, emitted before work starts so an agent can budget its
|
|
98
|
+
* timeout (plan §5/§6). Text mode: a dim one-liner on stderr. JSON mode: a
|
|
99
|
+
* `{event:'start', ...}` line on stderr. Suppressed for `fast` commands.
|
|
100
|
+
*/
|
|
101
|
+
AnnounceRuntime(usage: PluginUsage): void;
|
|
102
|
+
/** Final result — host serializes per the active `--format`. */
|
|
103
|
+
Emit(result: MJCLIResult): void;
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;AAElD,kDAAkD;AAClD,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAEjD;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;;OAKG;IACH,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,UAAU,CAAC;IACjD,uEAAuE;IACvE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,uEAAuE;IACvE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,uDAAuD;AACvD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAC;IACf,mEAAmE;IACnE,OAAO,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,OAAO,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,eAAe,EAAE,CAAC;IAC1B,kCAAkC;IAClC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,yFAAyF;AACzF,MAAM,WAAW,gBAAgB;IAC/B,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,MAAM,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,gFAAgF;IAChF,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,mCAAmC;IACnC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAG1B,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClD,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAG/C,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IAE7C;;;;OAIG;IACH,eAAe,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAAC;IAE1C,gEAAgE;IAChE,IAAI,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC;CACjC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core type definitions for the pluggable MJ CLI.
|
|
3
|
+
*
|
|
4
|
+
* The guiding principle (plan D2): plugins return *data* — a {@link MJCLIResult} —
|
|
5
|
+
* and the {@link IMJCLIRuntimeHost} renders it per the active {@link OutputFormat}.
|
|
6
|
+
* No `ora`, `chalk`, or `console` calls live inside a plugin's business logic.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
package/package.json
CHANGED
|
@@ -1,10 +1,43 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/cli-core",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "5.42.0",
|
|
5
|
+
"description": "Pluggable MJ CLI core — BaseCLIPlugin, runtime host, and progressive-disclosure usage primitives shared by mj CLI plugins",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc && tsc-alias -f",
|
|
10
|
+
"watch": "tsc --watch",
|
|
11
|
+
"clean": "rimraf dist",
|
|
12
|
+
"test": "vitest run",
|
|
13
|
+
"test:watch": "vitest"
|
|
14
|
+
},
|
|
5
15
|
"keywords": [
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
|
|
16
|
+
"memberjunction",
|
|
17
|
+
"cli",
|
|
18
|
+
"oclif",
|
|
19
|
+
"plugin"
|
|
20
|
+
],
|
|
21
|
+
"author": "MemberJunction",
|
|
22
|
+
"license": "ISC",
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@memberjunction/global": "5.42.0",
|
|
25
|
+
"@oclif/core": "^3.27.0",
|
|
26
|
+
"chalk": "^5.6.2",
|
|
27
|
+
"ora-classic": "^5.4.2"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^24.10.11",
|
|
31
|
+
"rimraf": "6.1.2",
|
|
32
|
+
"tsc-alias": "^1.8.10",
|
|
33
|
+
"typescript": "^5.9.3",
|
|
34
|
+
"vitest": "^4.0.18"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist"
|
|
38
|
+
],
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "https://github.com/MemberJunction/MJ"
|
|
42
|
+
}
|
|
10
43
|
}
|