@mudah-cli/mudah 0.8.0 → 0.9.2

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.
Files changed (102) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +70 -0
  3. package/dist/autocomplete.d.ts +8 -0
  4. package/dist/autocomplete.js +38 -0
  5. package/dist/autocomplete.js.map +1 -0
  6. package/dist/built-ins.d.ts +9 -0
  7. package/dist/built-ins.js +144 -0
  8. package/dist/built-ins.js.map +1 -0
  9. package/dist/commands/autocomplete.command.d.ts +11 -0
  10. package/dist/commands/autocomplete.command.js +16 -0
  11. package/dist/commands/autocomplete.command.js.map +1 -0
  12. package/dist/commands/build.command.d.ts +1 -0
  13. package/dist/commands/build.command.js +2 -1
  14. package/dist/commands/build.command.js.map +1 -1
  15. package/dist/commands/cache.command.d.ts +11 -0
  16. package/dist/commands/cache.command.js +47 -0
  17. package/dist/commands/cache.command.js.map +1 -0
  18. package/dist/commands/complete.command.d.ts +13 -0
  19. package/dist/commands/complete.command.js +23 -0
  20. package/dist/commands/complete.command.js.map +1 -0
  21. package/dist/commands/config.diff.command.d.ts +8 -0
  22. package/dist/commands/config.diff.command.js +96 -0
  23. package/dist/commands/config.diff.command.js.map +1 -0
  24. package/dist/commands/config.set.command.d.ts +8 -0
  25. package/dist/commands/config.set.command.js +66 -0
  26. package/dist/commands/config.set.command.js.map +1 -0
  27. package/dist/commands/config.show.command.d.ts +8 -0
  28. package/dist/commands/config.show.command.js +38 -0
  29. package/dist/commands/config.show.command.js.map +1 -0
  30. package/dist/commands/config.source.command.d.ts +8 -0
  31. package/dist/commands/config.source.command.js +42 -0
  32. package/dist/commands/config.source.command.js.map +1 -0
  33. package/dist/commands/config.validate.command.d.ts +14 -0
  34. package/dist/commands/config.validate.command.js +63 -0
  35. package/dist/commands/config.validate.command.js.map +1 -0
  36. package/dist/commands/dev.command.d.ts +1 -0
  37. package/dist/commands/dev.command.js +1 -0
  38. package/dist/commands/dev.command.js.map +1 -1
  39. package/dist/commands/docs.widgets.command.d.ts +11 -0
  40. package/dist/commands/docs.widgets.command.js +17 -0
  41. package/dist/commands/docs.widgets.command.js.map +1 -0
  42. package/dist/commands/doctor.command.d.ts +1 -0
  43. package/dist/commands/doctor.command.js +45 -11
  44. package/dist/commands/doctor.command.js.map +1 -1
  45. package/dist/commands/gallery.command.d.ts +11 -0
  46. package/dist/commands/gallery.command.js +84 -0
  47. package/dist/commands/gallery.command.js.map +1 -0
  48. package/dist/commands/graph.command.d.ts +10 -0
  49. package/dist/commands/graph.command.js +21 -0
  50. package/dist/commands/graph.command.js.map +1 -0
  51. package/dist/commands/help.command.d.ts +1 -0
  52. package/dist/commands/help.command.js +2 -2
  53. package/dist/commands/help.command.js.map +1 -1
  54. package/dist/commands/info.command.d.ts +8 -0
  55. package/dist/commands/info.command.js +61 -0
  56. package/dist/commands/info.command.js.map +1 -0
  57. package/dist/commands/lsp.command.d.ts +12 -0
  58. package/dist/commands/lsp.command.js +57 -0
  59. package/dist/commands/lsp.command.js.map +1 -0
  60. package/dist/commands/make.command.d.ts +5 -0
  61. package/dist/commands/make.command.js +270 -48
  62. package/dist/commands/make.command.js.map +1 -1
  63. package/dist/commands/plugins.list.command.d.ts +12 -0
  64. package/dist/commands/plugins.list.command.js +29 -0
  65. package/dist/commands/plugins.list.command.js.map +1 -0
  66. package/dist/commands/plugins.update.command.d.ts +29 -0
  67. package/dist/commands/plugins.update.command.js +78 -0
  68. package/dist/commands/plugins.update.command.js.map +1 -0
  69. package/dist/commands/plugins.watch.command.d.ts +10 -0
  70. package/dist/commands/plugins.watch.command.js +37 -0
  71. package/dist/commands/plugins.watch.command.js.map +1 -0
  72. package/dist/commands/replay.command.d.ts +10 -0
  73. package/dist/commands/replay.command.js +50 -0
  74. package/dist/commands/replay.command.js.map +1 -0
  75. package/dist/commands/telemetry.command.d.ts +12 -0
  76. package/dist/commands/telemetry.command.js +55 -0
  77. package/dist/commands/telemetry.command.js.map +1 -0
  78. package/dist/commands/test.command.d.ts +13 -0
  79. package/dist/commands/test.command.js +83 -0
  80. package/dist/commands/test.command.js.map +1 -0
  81. package/dist/commands/tutorial.command.d.ts +12 -0
  82. package/dist/commands/tutorial.command.js +57 -0
  83. package/dist/commands/tutorial.command.js.map +1 -0
  84. package/dist/commands/version.command.d.ts +1 -0
  85. package/dist/commands/version.command.js +1 -0
  86. package/dist/commands/version.command.js.map +1 -1
  87. package/dist/commands/watch.command.d.ts +13 -0
  88. package/dist/commands/watch.command.js +62 -0
  89. package/dist/commands/watch.command.js.map +1 -0
  90. package/dist/index.d.ts +8 -5
  91. package/dist/index.js +8 -5
  92. package/dist/index.js.map +1 -1
  93. package/dist/lsp.d.ts +147 -0
  94. package/dist/lsp.js +506 -0
  95. package/dist/lsp.js.map +1 -0
  96. package/dist/run.d.ts +4 -2
  97. package/dist/run.js +355 -38
  98. package/dist/run.js.map +1 -1
  99. package/dist/watcher.d.ts +20 -1
  100. package/dist/watcher.js +41 -4
  101. package/dist/watcher.js.map +1 -1
  102. package/package.json +27 -10
@@ -1,11 +1,11 @@
1
- import { Command } from '@mudah-cli/console';
2
- import { renderCommandHelp, renderCommandList } from '@mudah-cli/console';
1
+ import { Command, renderCommandHelp, renderCommandList } from '@mudah-cli/console';
3
2
  /**
4
3
  * Built-in `help` command: lists all commands, or shows details for one.
5
4
  */
6
5
  export default class HelpCommand extends Command {
7
6
  signature = 'help {command?}';
8
7
  description = 'Show help for the CLI or a specific command';
8
+ descriptionKey = 'cmd.help.description';
9
9
  kernel;
10
10
  constructor(kernel) {
11
11
  super();
@@ -1 +1 @@
1
- {"version":3,"file":"help.command.js","sourceRoot":"","sources":["../../src/commands/help.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAsB,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE1E;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,OAAO;IAC9C,SAAS,GAAG,iBAAiB,CAAC;IAC9B,WAAW,GAAG,6CAA6C,CAAC;IAEpD,MAAM,CAAgB;IAE9B,YAAY,MAAqB;QAC/B,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,MAAM;QACV,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACnC,MAAM,KAAK,GAAa,EAAE,CAAC;QAE3B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACtC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,MAAM,IAAI,CAAC,UAAU,CAAC,oBAAoB,MAAM,IAAI,EAAE,kCAAkC,CAAC,CAAC;YAC5F,CAAC;YACD,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAC1D,CAAC;aAAM,CAAC;YACN,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,KAAK,CAAC,CAAC;QAClG,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACpC,CAAC;CACF"}
1
+ {"version":3,"file":"help.command.js","sourceRoot":"","sources":["../../src/commands/help.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAsB,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAEvG;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,OAAO;IAC9C,SAAS,GAAG,iBAAiB,CAAC;IAC9B,WAAW,GAAG,6CAA6C,CAAC;IAC5D,cAAc,GAAG,sBAAsB,CAAC;IAEhC,MAAM,CAAgB;IAE9B,YAAY,MAAqB;QAC/B,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,MAAM;QACV,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACnC,MAAM,KAAK,GAAa,EAAE,CAAC;QAE3B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACtC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,MAAM,IAAI,CAAC,UAAU,CAAC,oBAAoB,MAAM,IAAI,EAAE,kCAAkC,CAAC,CAAC;YAC5F,CAAC;YACD,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAC1D,CAAC;aAAM,CAAC;YACN,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,KAAK,CAAC,CAAC;QAClG,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACpC,CAAC;CACF"}
@@ -0,0 +1,8 @@
1
+ import { Command } from '@mudah-cli/console';
2
+ /** Built-in `info` command: dump runtime, app, and config info. */
3
+ export default class InfoCommand extends Command {
4
+ signature: string;
5
+ description: string;
6
+ descriptionKey: string;
7
+ handle(): Promise<void>;
8
+ }
@@ -0,0 +1,61 @@
1
+ import { Command } from '@mudah-cli/console';
2
+ /** Built-in `info` command: dump runtime, app, and config info. */
3
+ export default class InfoCommand extends Command {
4
+ signature = 'info [--json]';
5
+ description = 'Show runtime, app, and config information';
6
+ descriptionKey = 'cmd.info.description';
7
+ async handle() {
8
+ const app = this.app;
9
+ const output = this.output;
10
+ const plugins = typeof app.plugins === 'function' ? app.plugins() : [];
11
+ const pluginSummary = plugins.map((plugin) => ({
12
+ name: plugin.name,
13
+ providers: plugin.providers.length,
14
+ }));
15
+ let health;
16
+ if (typeof app.health === 'function') {
17
+ health = await app.health();
18
+ }
19
+ if (output.isMachineReadable || this.option('json') === true) {
20
+ output.emit('data', 'info', {
21
+ name: app.manifest.name,
22
+ version: app.manifest.version,
23
+ node: process.version,
24
+ plugins: pluginSummary,
25
+ ...(health === undefined ? {} : { health }),
26
+ });
27
+ return;
28
+ }
29
+ output.section('Runtime');
30
+ output.keyValue('node', process.version);
31
+ output.keyValue('platform', `${process.platform} ${process.arch}`);
32
+ output.section('Application');
33
+ output.keyValue('name', app.manifest.name);
34
+ output.keyValue('version', app.manifest.version);
35
+ output.keyValue('basePath', app.basePath);
36
+ output.keyValue('bin', app.manifest.bin);
37
+ if (pluginSummary.length > 0) {
38
+ output.section('Plugins');
39
+ for (const plugin of pluginSummary) {
40
+ output.keyValue(plugin.name, `${plugin.providers} provider(s)`);
41
+ }
42
+ }
43
+ if (health !== undefined && health.length > 0) {
44
+ output.section('Health');
45
+ for (const row of health) {
46
+ const detail = row.detail ? ` ${row.detail}` : '';
47
+ output.keyValue(row.provider, `${row.status} (${row.latencyMs}ms)${detail}`);
48
+ }
49
+ }
50
+ const configKeys = Object.keys(app.config().all());
51
+ if (configKeys.length > 0) {
52
+ output.section('Configuration');
53
+ for (const key of configKeys) {
54
+ output.keyValue(key, String(app.config().get(key)));
55
+ }
56
+ }
57
+ output.line();
58
+ output.success('info complete');
59
+ }
60
+ }
61
+ //# sourceMappingURL=info.command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"info.command.js","sourceRoot":"","sources":["../../src/commands/info.command.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { Command } from '@mudah-cli/console';
2
+ /**
3
+ * Built-in `lsp` command: stdio JSON-RPC (Content-Length framed, with a
4
+ * newline-delimited fallback for probes).
5
+ */
6
+ export default class LspCommand extends Command {
7
+ signature: string;
8
+ description: string;
9
+ descriptionKey: string;
10
+ handle(): Promise<number>;
11
+ private serveStdio;
12
+ }
@@ -0,0 +1,57 @@
1
+ import { Command } from '@mudah-cli/console';
2
+ import { decodeLspFrames, encodeLspFrame, handleLspMessage, initializeResult, publishDiagnostics, uriOf, } from '../lsp.js';
3
+ /**
4
+ * Built-in `lsp` command: stdio JSON-RPC (Content-Length framed, with a
5
+ * newline-delimited fallback for probes).
6
+ */
7
+ export default class LspCommand extends Command {
8
+ signature = 'lsp [--stdio] [--probe]';
9
+ description = 'Language server for mudah.json keys and command signatures';
10
+ descriptionKey = 'cmd.lsp.description';
11
+ async handle() {
12
+ if (this.option('probe') === true) {
13
+ const ready = initializeResult();
14
+ this.output.raw(`${JSON.stringify({ jsonrpc: '2.0', id: 1, result: ready })}\n`);
15
+ return 0;
16
+ }
17
+ if (this.option('stdio') === true) {
18
+ return this.serveStdio();
19
+ }
20
+ this.output.success('mudah-lsp ready');
21
+ this.output.muted('Pass --stdio to speak JSON-RPC on stdin/stdout, or --probe to print initialize.');
22
+ return 0;
23
+ }
24
+ serveStdio() {
25
+ return new Promise((resolve) => {
26
+ let buffer = '';
27
+ const write = (msg) => {
28
+ process.stdout.write(encodeLspFrame(msg));
29
+ };
30
+ process.stdin.setEncoding('utf8');
31
+ process.stdin.on('data', (chunk) => {
32
+ buffer += chunk;
33
+ const decoded = decodeLspFrames(buffer);
34
+ buffer = decoded.rest;
35
+ for (const msg of decoded.messages) {
36
+ if (msg.method === 'exit') {
37
+ resolve(0);
38
+ return;
39
+ }
40
+ const reply = handleLspMessage(msg);
41
+ if (reply !== undefined)
42
+ write(reply);
43
+ if (msg.method === 'textDocument/didOpen' || msg.method === 'textDocument/didChange') {
44
+ const notice = publishDiagnostics(uriOf(msg.params));
45
+ if (notice !== undefined)
46
+ write(notice);
47
+ }
48
+ if (msg.method === 'shutdown') {
49
+ resolve(0);
50
+ }
51
+ }
52
+ });
53
+ process.stdin.on('end', () => resolve(0));
54
+ });
55
+ }
56
+ }
57
+ //# sourceMappingURL=lsp.command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lsp.command.js","sourceRoot":"","sources":["../../src/commands/lsp.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EACL,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAEhB,kBAAkB,EAClB,KAAK,GACN,MAAM,WAAW,CAAC;AAEnB;;;GAGG;AACH,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,OAAO;IAC7C,SAAS,GAAG,yBAAyB,CAAC;IACtC,WAAW,GAAG,4DAA4D,CAAC;IAC3E,cAAc,GAAG,qBAAqB,CAAC;IAEvC,KAAK,CAAC,MAAM;QACV,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;YAClC,MAAM,KAAK,GAAG,gBAAgB,EAAE,CAAC;YACjC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC;YACjF,OAAO,CAAC,CAAC;QACX,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;YAClC,OAAO,IAAI,CAAC,UAAU,EAAE,CAAC;QAC3B,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;QACvC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,iFAAiF,CAAC,CAAC;QACrG,OAAO,CAAC,CAAC;IACX,CAAC;IAEO,UAAU;QAChB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC7B,IAAI,MAAM,GAAG,EAAE,CAAC;YAChB,MAAM,KAAK,GAAG,CAAC,GAAe,EAAQ,EAAE;gBACtC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;YAC5C,CAAC,CAAC;YACF,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YAClC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;gBACzC,MAAM,IAAI,KAAK,CAAC;gBAChB,MAAM,OAAO,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;gBACxC,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;gBACtB,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;oBACnC,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;wBAC1B,OAAO,CAAC,CAAC,CAAC,CAAC;wBACX,OAAO;oBACT,CAAC;oBACD,MAAM,KAAK,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;oBACpC,IAAI,KAAK,KAAK,SAAS;wBAAE,KAAK,CAAC,KAAK,CAAC,CAAC;oBACtC,IAAI,GAAG,CAAC,MAAM,KAAK,sBAAsB,IAAI,GAAG,CAAC,MAAM,KAAK,wBAAwB,EAAE,CAAC;wBACrF,MAAM,MAAM,GAAG,kBAAkB,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;wBACrD,IAAI,MAAM,KAAK,SAAS;4BAAE,KAAK,CAAC,MAAM,CAAC,CAAC;oBAC1C,CAAC;oBACD,IAAI,GAAG,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;wBAC9B,OAAO,CAAC,CAAC,CAAC,CAAC;oBACb,CAAC;gBACH,CAAC;YACH,CAAC,CAAC,CAAC;YACH,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -5,5 +5,10 @@ import { Command } from '@mudah-cli/console';
5
5
  export default class MakeCommand extends Command {
6
6
  signature: string;
7
7
  description: string;
8
+ descriptionKey: string;
8
9
  handle(): Promise<void>;
10
+ private writeTui;
11
+ private hintNext;
12
+ private writeNew;
13
+ private writePlugin;
9
14
  }
@@ -1,6 +1,13 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
1
2
  import { mkdir, writeFile } from 'node:fs/promises';
2
3
  import { isAbsolute, join, relative } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
3
5
  import { Command } from '@mudah-cli/console';
6
+ /** Peer range for a generated plugin, matching the installed framework version. */
7
+ function mudahRange() {
8
+ const pkg = JSON.parse(readFileSync(fileURLToPath(new URL('../../package.json', import.meta.url)), 'utf8'));
9
+ return `^${pkg.version}`;
10
+ }
4
11
  function kebab(name) {
5
12
  return name.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
6
13
  }
@@ -10,6 +17,8 @@ function pascal(name) {
10
17
  .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
11
18
  .join('');
12
19
  }
20
+ const TUI_RECIPES = ['picker', 'wizard', 'dashboard', 'form'];
21
+ const HINT = 'Use: make command {name} | make tui {picker|wizard|dashboard|form} {name?} | make provider {name} | make config {name} | make plugin {name}';
13
22
  const COMMAND_TEMPLATE = (command, className) => `import { Command } from '@mudah-cli/mudah';
14
23
 
15
24
  export default class ${className} extends Command {
@@ -17,14 +26,30 @@ export default class ${className} extends Command {
17
26
  description = 'Describe ${command} in one line.';
18
27
 
19
28
  async handle() {
20
- this.output.success('Hello from ${command}!');
29
+ this.output.success(\`Hello from ${command}!\`);
21
30
  }
22
31
  }
23
32
  `;
33
+ const COMMAND_TEST = (command) => `import { fileURLToPath } from 'node:url';
34
+ import { describe, it } from 'vitest';
35
+ import { TestApp } from '@mudah-cli/mudah/testing';
36
+
37
+ const appDir = fileURLToPath(new URL('..', import.meta.url));
38
+
39
+ describe('${command}', () => {
40
+ it('runs', async () => {
41
+ const app = await TestApp.create({ cwd: appDir });
42
+ const result = await app.dispatch(['${command}']);
43
+ result.exit(0).outContains('Hello from ${command}!');
44
+ });
45
+ });
46
+ `;
24
47
  const PROVIDER_TEMPLATE = (className) => `import { ServiceProvider } from '@mudah-cli/mudah';
25
48
 
26
49
  export default class ${className} extends ServiceProvider {
27
- register(): void {}
50
+ register(): void {
51
+ // this.app.singleton('db', () => new Db());
52
+ }
28
53
 
29
54
  boot(): void {}
30
55
  }
@@ -35,23 +60,65 @@ export default defineConfig({
35
60
  // Add configuration for this component.
36
61
  });
37
62
  `;
38
- const TUI_HINT = 'Use: make {command|provider|config|tui} {name}';
63
+ const PLUGIN_PACKAGE = (name) => `${JSON.stringify({
64
+ name,
65
+ version: '0.1.0',
66
+ private: true,
67
+ description: `${name} — a Mudah plugin`,
68
+ type: 'module',
69
+ license: 'MIT',
70
+ keywords: ['mudah-plugin', 'cli'],
71
+ exports: { '.': './src/index.ts' },
72
+ dependencies: { '@mudah-cli/mudah': mudahRange() },
73
+ }, null, 2)}\n`;
74
+ const PLUGIN_INDEX = (name, className) => `import { Command, ServiceProvider } from '@mudah-cli/mudah';
75
+
76
+ /** Binds services this plugin contributes. */
77
+ export class ${className}Provider extends ServiceProvider {
78
+ register(): void {}
79
+
80
+ boot(): void {}
81
+ }
82
+
83
+ export class ${className}HelloCommand extends Command {
84
+ signature = '${name}:hello';
85
+ description = 'Hello from the ${name} plugin';
86
+ groupDescription = '${className} plugin';
87
+
88
+ async handle(): Promise<number> {
89
+ this.output.success('Hello from ${name}');
90
+ return 0;
91
+ }
92
+ }
93
+
94
+ export const providers = [${className}Provider];
95
+ export const commands = [${className}HelloCommand];
96
+ `;
39
97
  const TUI_PICKER = (command, className) => `import { Command } from '@mudah-cli/mudah';
40
- import { Program, Screen } from '@mudah-cli/mudah/tui';
98
+ import { Screen } from '@mudah-cli/mudah/tui';
99
+
100
+ const ITEMS = ['one', 'two', 'three'];
41
101
 
42
102
  export default class ${className} extends Command {
43
- signature = '${command}';
103
+ signature = '${command} {item?}';
44
104
  description = 'Pick one item';
45
105
 
46
106
  async handle(): Promise<number> {
107
+ const flagged = this.arg('item');
108
+ if (flagged) {
109
+ if (!ITEMS.includes(flagged)) {
110
+ throw this.usageError(\`Unknown item "\${flagged}".\`, \`Known: \${ITEMS.join(', ')}\`);
111
+ }
112
+ this.output.success(\`Picked \${flagged}.\`);
113
+ return 0;
114
+ }
47
115
  if (process.stdout.isTTY !== true) {
48
116
  this.output.error('This command needs an interactive terminal.');
117
+ this.output.hint('Pass an item: ${command} one');
49
118
  return 2;
50
119
  }
51
- const screen = Screen.picker({ title: '${command}', items: ['one', 'two', 'three'] });
52
- const program = new Program();
53
- screen.attach(program);
54
- const code = await program.run();
120
+ const screen = Screen.picker({ title: '${command}', items: ITEMS });
121
+ const code = await screen.run();
55
122
  const picked = screen.result();
56
123
  if (picked) this.output.success(\`Picked \${picked}.\`);
57
124
  return code;
@@ -59,16 +126,20 @@ export default class ${className} extends Command {
59
126
  }
60
127
  `;
61
128
  const TUI_WIZARD = (command, className) => `import { Command } from '@mudah-cli/mudah';
62
- import { Program, Screen } from '@mudah-cli/mudah/tui';
129
+ import { Screen } from '@mudah-cli/mudah/tui';
63
130
 
64
131
  export default class ${className} extends Command {
65
- signature = '${command}';
132
+ signature = '${command} [--env=] [--note=]';
66
133
  description = 'Multi-step wizard';
67
134
 
68
135
  async handle(): Promise<number> {
69
- if (process.stdout.isTTY !== true) {
70
- this.output.error('This command needs an interactive terminal.');
71
- return 2;
136
+ const envFlag = this.option('env');
137
+ const noteFlag = this.option('note');
138
+ if (typeof envFlag === 'string' || process.stdout.isTTY !== true) {
139
+ const env = typeof envFlag === 'string' && envFlag.length > 0 ? envFlag : 'staging';
140
+ const note = typeof noteFlag === 'string' ? noteFlag : '';
141
+ this.output.success(JSON.stringify({ env, note }));
142
+ return 0;
72
143
  }
73
144
  const screen = Screen.wizard({
74
145
  title: '${command}',
@@ -77,9 +148,7 @@ export default class ${className} extends Command {
77
148
  { name: 'note', kind: 'text', label: 'Note' },
78
149
  ],
79
150
  });
80
- const program = new Program();
81
- screen.attach(program);
82
- const code = await program.run();
151
+ const code = await screen.run();
83
152
  const result = screen.result();
84
153
  if (result) this.output.success(JSON.stringify(result));
85
154
  return code;
@@ -87,81 +156,234 @@ export default class ${className} extends Command {
87
156
  }
88
157
  `;
89
158
  const TUI_DASHBOARD = (command, className) => `import { Command } from '@mudah-cli/mudah';
90
- import { Program, Screen } from '@mudah-cli/mudah/tui';
159
+ import { Screen } from '@mudah-cli/mudah/tui';
160
+
161
+ const ROWS = [
162
+ ['api', 'ok'],
163
+ ['worker', 'ok'],
164
+ ];
91
165
 
92
166
  export default class ${className} extends Command {
93
- signature = '${command}';
167
+ signature = '${command} [--dump]';
94
168
  description = 'Full-screen dashboard';
95
169
 
96
170
  async handle(): Promise<number> {
97
- if (process.stdout.isTTY !== true) {
98
- this.output.error('This command needs an interactive terminal.');
99
- return 2;
171
+ if (this.option('dump') === true || process.stdout.isTTY !== true) {
172
+ this.output.section('${command}');
173
+ for (const [name, status] of ROWS) this.output.keyValue(name ?? '', status ?? '');
174
+ return 0;
100
175
  }
101
176
  const screen = Screen.dashboard({
102
177
  title: '${command}',
103
178
  sidebar: ['esc to quit', 'drag the split'],
104
179
  columns: [{ header: 'name' }, { header: 'status' }],
105
- rows: [['api', 'ok'], ['worker', 'ok']],
180
+ rows: ROWS,
106
181
  });
107
- const program = new Program({ mouse: true });
108
- screen.attach(program);
109
- return program.run();
182
+ return screen.run({ mouse: true });
183
+ }
184
+ }
185
+ `;
186
+ const TUI_FORM = (command, className) => `import { Command, s } from '@mudah-cli/mudah';
187
+ import { Screen } from '@mudah-cli/mudah/tui';
188
+
189
+ const schema = s.object({
190
+ name: s.string(),
191
+ live: s.boolean(),
192
+ });
193
+
194
+ export default class ${className} extends Command {
195
+ signature = '${command} [--name=] [--live]';
196
+ description = 'Schema-backed form';
197
+
198
+ async handle(): Promise<number> {
199
+ const named = this.option('name');
200
+ if (typeof named === 'string' || this.option('live') === true || process.stdout.isTTY !== true) {
201
+ this.output.success(
202
+ JSON.stringify({ name: typeof named === 'string' ? named : '', live: this.option('live') === true }),
203
+ );
204
+ return 0;
205
+ }
206
+ const screen = Screen.form({ title: '${command}', schema });
207
+ const code = await screen.run();
208
+ const values = screen.result();
209
+ if (values) this.output.success(JSON.stringify(values));
210
+ return code;
110
211
  }
111
212
  }
112
213
  `;
214
+ const TUI_PICKER_TEST = (command) => `import { fileURLToPath } from 'node:url';
215
+ import { describe, expect, it } from 'vitest';
216
+ import { Screen } from '@mudah-cli/mudah/tui';
217
+ import { TestApp, TestTui } from '@mudah-cli/mudah/testing';
218
+
219
+ const appDir = fileURLToPath(new URL('..', import.meta.url));
220
+
221
+ describe('${command}', () => {
222
+ it('picks from an argument without a TTY', async () => {
223
+ const app = await TestApp.create({ cwd: appDir });
224
+ const result = await app.dispatch(['${command}', 'two']);
225
+ result.exit(0).outContains('Picked two.');
226
+ });
227
+
228
+ it('renders the picker', () => {
229
+ const screen = Screen.picker({ title: '${command}', items: ['one', 'two', 'three'] });
230
+ const tui = TestTui.mount(screen.root, { cols: 40, rows: 10 });
231
+ expect(tui.snapshot()).toContain('${command}');
232
+ tui.send('down').send('enter');
233
+ expect(screen.result()).toBe('two');
234
+ });
235
+ });
236
+ `;
237
+ const TUI_SCREEN_TEST = (command, recipe) => {
238
+ if (recipe === 'picker')
239
+ return TUI_PICKER_TEST(command);
240
+ const factory = recipe === 'wizard'
241
+ ? `Screen.wizard({ title: '${command}', steps: [{ name: 'env', kind: 'pick', items: ['staging', 'production'] }] })`
242
+ : recipe === 'form'
243
+ ? `Screen.form({ title: '${command}', schema: s.object({ name: s.string() }) })`
244
+ : `Screen.dashboard({ title: '${command}', sidebar: ['esc'], columns: [{ header: 'name' }], rows: [['api']] })`;
245
+ const extraImport = recipe === 'form' ? `\nimport { s } from '@mudah-cli/mudah';` : '';
246
+ const cli = recipe === 'wizard'
247
+ ? `['${command}', '--env=production']`
248
+ : recipe === 'form'
249
+ ? `['${command}', '--name=Ada']`
250
+ : `['${command}', '--dump']`;
251
+ const expectOut = recipe === 'wizard' ? 'production' : recipe === 'form' ? 'Ada' : 'api';
252
+ return `import { fileURLToPath } from 'node:url';
253
+ import { describe, expect, it } from 'vitest';
254
+ import { Screen } from '@mudah-cli/mudah/tui';
255
+ import { TestApp, TestTui } from '@mudah-cli/mudah/testing';${extraImport}
256
+
257
+ const appDir = fileURLToPath(new URL('..', import.meta.url));
258
+
259
+ describe('${command}', () => {
260
+ it('runs without a TTY', async () => {
261
+ const app = await TestApp.create({ cwd: appDir });
262
+ const result = await app.dispatch(${cli});
263
+ result.exit(0).outContains('${expectOut}');
264
+ });
265
+
266
+ it('renders the ${recipe}', () => {
267
+ const screen = ${factory};
268
+ const tui = TestTui.mount(screen.root, { cols: 60, rows: 16 });
269
+ expect(tui.snapshot()).toContain('${command}');
270
+ expect(tui.tree().role).toBeTruthy();
271
+ });
272
+ });
273
+ `;
274
+ };
275
+ function tuiSource(recipe, command, className) {
276
+ if (recipe === 'wizard')
277
+ return TUI_WIZARD(command, className);
278
+ if (recipe === 'dashboard')
279
+ return TUI_DASHBOARD(command, className);
280
+ if (recipe === 'form')
281
+ return TUI_FORM(command, className);
282
+ return TUI_PICKER(command, className);
283
+ }
113
284
  /**
114
285
  * Built-in `make` command: scaffold commands, providers, config files, and TUI recipes.
115
286
  */
116
287
  export default class MakeCommand extends Command {
117
- signature = 'make {type} {name}';
118
- description = 'Scaffold a command, provider, config file, or TUI screen';
288
+ signature = 'make {type} {name} {extra?}';
289
+ description = 'Scaffold a command, provider, config file, TUI screen, or plugin';
290
+ descriptionKey = 'cmd.make.description';
119
291
  async handle() {
120
292
  const type = this.arg('type');
121
293
  const name = this.arg('name');
122
- if (!['command', 'provider', 'config', 'tui'].includes(type)) {
123
- throw this.usageError(`Unknown make type "${type}".`, TUI_HINT);
294
+ const extra = this.arg('extra');
295
+ if (!['command', 'provider', 'config', 'tui', 'plugin'].includes(type)) {
296
+ throw this.usageError(`Unknown make type "${type}".`, HINT);
297
+ }
298
+ for (const part of [name, extra]) {
299
+ if (part !== undefined && !/^[a-zA-Z][a-zA-Z0-9-]*$/.test(part)) {
300
+ throw this.usageError(`Invalid name "${part}".`, 'Use letters, numbers, and dashes (e.g. "deploy-site").');
301
+ }
124
302
  }
125
- if (!/^[a-zA-Z][a-zA-Z0-9-]*$/.test(name)) {
126
- throw this.usageError(`Invalid name "${name}".`, 'Use letters, numbers, and dashes (e.g. "deploy-site").');
303
+ if (extra && type !== 'tui') {
304
+ throw this.usageError(`"${type}" takes one name.`, HINT);
127
305
  }
128
306
  const k = kebab(name);
129
307
  const className = pascal(name);
308
+ if (type === 'plugin') {
309
+ await this.writePlugin(k, className);
310
+ return;
311
+ }
312
+ if (type === 'tui') {
313
+ await this.writeTui(k, extra);
314
+ return;
315
+ }
130
316
  let filePath;
131
317
  let content;
318
+ let testPath;
319
+ let testContent;
132
320
  if (type === 'command') {
133
321
  filePath = join(this.app.basePath, 'src', 'commands', `${k}.command.ts`);
134
322
  content = COMMAND_TEMPLATE(k, className);
323
+ testPath = join(this.app.basePath, 'test', `${k}.test.ts`);
324
+ testContent = COMMAND_TEST(k);
135
325
  }
136
326
  else if (type === 'provider') {
137
327
  filePath = join(this.app.basePath, 'src', 'providers', `${className}Provider.ts`);
138
328
  content = PROVIDER_TEMPLATE(`${className}Provider`);
139
329
  }
140
- else if (type === 'tui') {
141
- if (!['picker', 'wizard', 'dashboard'].includes(k)) {
142
- throw this.usageError(`Unknown TUI recipe "${name}".`, 'Use: make tui {picker|wizard|dashboard}');
143
- }
144
- filePath = join(this.app.basePath, 'src', 'commands', `${k}.command.ts`);
145
- content = k === 'wizard' ? TUI_WIZARD(k, className) : k === 'dashboard' ? TUI_DASHBOARD(k, className) : TUI_PICKER(k, className);
146
- }
147
330
  else {
148
331
  filePath = join(this.app.basePath, 'config', `${k}.ts`);
149
332
  content = CONFIG_TEMPLATE;
150
333
  }
334
+ await this.writeNew(filePath, content);
335
+ this.output.success(`Created ${relative(this.app.basePath, filePath)}`);
336
+ if (testPath && testContent) {
337
+ await this.writeNew(testPath, testContent);
338
+ this.output.success(`Created ${relative(this.app.basePath, testPath)}`);
339
+ }
340
+ this.hintNext(type === 'command' ? k : undefined);
341
+ }
342
+ async writeTui(recipe, extra) {
343
+ if (!TUI_RECIPES.includes(recipe)) {
344
+ throw this.usageError(`Unknown TUI recipe "${recipe}".`, 'Use: make tui {picker|wizard|dashboard|form} {name?}');
345
+ }
346
+ const command = extra ? kebab(extra) : recipe;
347
+ const className = pascal(command);
348
+ const filePath = join(this.app.basePath, 'src', 'commands', `${command}.command.ts`);
349
+ const testPath = join(this.app.basePath, 'test', `${command}.test.ts`);
350
+ await this.writeNew(filePath, tuiSource(recipe, command, className));
351
+ await this.writeNew(testPath, TUI_SCREEN_TEST(command, recipe));
352
+ this.output.success(`Created ${relative(this.app.basePath, filePath)}`);
353
+ this.output.success(`Created ${relative(this.app.basePath, testPath)}`);
354
+ this.hintNext(command);
355
+ }
356
+ hintNext(command) {
357
+ const bin = this.app.manifest.bin;
358
+ if (command) {
359
+ this.output.hint(`Next: node bin/${bin}.js ${command} then npm test`);
360
+ }
361
+ }
362
+ async writeNew(filePath, content) {
151
363
  if (isAbsolute(filePath) === false || !filePath.startsWith(this.app.basePath)) {
152
364
  throw this.usageError('Refusing to write outside the app directory.');
153
365
  }
154
- try {
155
- await mkdir(join(filePath, '..'), { recursive: true });
156
- await writeFile(filePath, content, { flag: 'wx' });
366
+ if (existsSync(filePath)) {
367
+ throw this.usageError(`${relative(this.app.basePath, filePath)} already exists.`);
157
368
  }
158
- catch (error) {
159
- if (error.code === 'EEXIST') {
160
- throw this.usageError(`${relative(this.app.basePath, filePath)} already exists.`);
161
- }
162
- throw error;
369
+ await mkdir(join(filePath, '..'), { recursive: true });
370
+ await writeFile(filePath, content, { flag: 'wx' });
371
+ }
372
+ async writePlugin(k, className) {
373
+ const dir = join(this.app.basePath, `${k}-plugin`);
374
+ if (isAbsolute(dir) === false || !dir.startsWith(this.app.basePath)) {
375
+ throw this.usageError('Refusing to write outside the app directory.');
163
376
  }
164
- this.output.success(`Created ${relative(this.app.basePath, filePath)}`);
377
+ const pkgPath = join(dir, 'package.json');
378
+ const srcPath = join(dir, 'src', 'index.ts');
379
+ if (existsSync(pkgPath) || existsSync(srcPath)) {
380
+ throw this.usageError(`${relative(this.app.basePath, dir)} already exists.`);
381
+ }
382
+ await mkdir(join(dir, 'src'), { recursive: true });
383
+ await writeFile(pkgPath, PLUGIN_PACKAGE(k), { flag: 'wx' });
384
+ await writeFile(srcPath, PLUGIN_INDEX(k, className), { flag: 'wx' });
385
+ this.output.success(`Created ${relative(this.app.basePath, srcPath)}`);
386
+ this.output.hint(`Next: npm install ./${k}-plugin`);
165
387
  }
166
388
  }
167
389
  //# sourceMappingURL=make.command.js.map