@maltty/cli 1.0.0-rc.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.
Files changed (109) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +88 -0
  3. package/bin/maltty.js +3 -0
  4. package/dist/_format-CeQhQTl5.js +23 -0
  5. package/dist/_format-CeQhQTl5.js.map +1 -0
  6. package/dist/attemptAsync-BdXzRSIT.js +42 -0
  7. package/dist/attemptAsync-BdXzRSIT.js.map +1 -0
  8. package/dist/chunk-DsIazq42.js +26 -0
  9. package/dist/commands/add/command.d.ts +7 -0
  10. package/dist/commands/add/command.d.ts.map +1 -0
  11. package/dist/commands/add/command.js +123 -0
  12. package/dist/commands/add/command.js.map +1 -0
  13. package/dist/commands/add/config.d.ts +7 -0
  14. package/dist/commands/add/config.d.ts.map +1 -0
  15. package/dist/commands/add/config.js +57 -0
  16. package/dist/commands/add/config.js.map +1 -0
  17. package/dist/commands/add/index.d.ts +7 -0
  18. package/dist/commands/add/index.d.ts.map +1 -0
  19. package/dist/commands/add/index.js +7 -0
  20. package/dist/commands/add/index.js.map +1 -0
  21. package/dist/commands/add/middleware.d.ts +7 -0
  22. package/dist/commands/add/middleware.d.ts.map +1 -0
  23. package/dist/commands/add/middleware.js +88 -0
  24. package/dist/commands/add/middleware.js.map +1 -0
  25. package/dist/commands/build.d.ts +15 -0
  26. package/dist/commands/build.d.ts.map +1 -0
  27. package/dist/commands/build.js +246 -0
  28. package/dist/commands/build.js.map +1 -0
  29. package/dist/commands/commands.d.ts +14 -0
  30. package/dist/commands/commands.d.ts.map +1 -0
  31. package/dist/commands/commands.js +189 -0
  32. package/dist/commands/commands.js.map +1 -0
  33. package/dist/commands/dev.d.ts +13 -0
  34. package/dist/commands/dev.d.ts.map +1 -0
  35. package/dist/commands/dev.js +58 -0
  36. package/dist/commands/dev.js.map +1 -0
  37. package/dist/commands/doctor.d.ts +13 -0
  38. package/dist/commands/doctor.d.ts.map +1 -0
  39. package/dist/commands/doctor.js +658 -0
  40. package/dist/commands/doctor.js.map +1 -0
  41. package/dist/commands/init.d.ts +7 -0
  42. package/dist/commands/init.d.ts.map +1 -0
  43. package/dist/commands/init.js +305 -0
  44. package/dist/commands/init.js.map +1 -0
  45. package/dist/commands/run.d.ts +19 -0
  46. package/dist/commands/run.d.ts.map +1 -0
  47. package/dist/commands/run.js +478 -0
  48. package/dist/commands/run.js.map +1 -0
  49. package/dist/commands/stories.d.ts +11 -0
  50. package/dist/commands/stories.d.ts.map +1 -0
  51. package/dist/commands/stories.js +22 -0
  52. package/dist/commands/stories.js.map +1 -0
  53. package/dist/config-helpers-pv_2Spux.js +15 -0
  54. package/dist/config-helpers-pv_2Spux.js.map +1 -0
  55. package/dist/defu-BBwssjW3.js +192 -0
  56. package/dist/defu-BBwssjW3.js.map +1 -0
  57. package/dist/detect-AOq1ehTE.js +61 -0
  58. package/dist/detect-AOq1ehTE.js.map +1 -0
  59. package/dist/dist-DW_gRP7_.js +1119 -0
  60. package/dist/dist-DW_gRP7_.js.map +1 -0
  61. package/dist/dist-Xa07jNxS.js +15617 -0
  62. package/dist/dist-Xa07jNxS.js.map +1 -0
  63. package/dist/index.d.ts +1 -0
  64. package/dist/index.js +46 -0
  65. package/dist/index.js.map +1 -0
  66. package/dist/jiti-CxWlVmZ_.js +5458 -0
  67. package/dist/jiti-CxWlVmZ_.js.map +1 -0
  68. package/dist/json-D7WbuVK1.js +43 -0
  69. package/dist/json-D7WbuVK1.js.map +1 -0
  70. package/dist/json5-z-PGDMyy.js +777 -0
  71. package/dist/json5-z-PGDMyy.js.map +1 -0
  72. package/dist/jsonc-DqZkfC3S.js +401 -0
  73. package/dist/jsonc-DqZkfC3S.js.map +1 -0
  74. package/dist/jsonc-KADMdSjU.js +2 -0
  75. package/dist/lib/templates/command/command.ts.liquid +12 -0
  76. package/dist/lib/templates/config/config.ts.liquid +16 -0
  77. package/dist/lib/templates/middleware/middleware.ts.liquid +9 -0
  78. package/dist/lib/templates/project/gitignore.liquid +3 -0
  79. package/dist/lib/templates/project/maltty.config.ts.liquid +6 -0
  80. package/dist/lib/templates/project/package.json.liquid +30 -0
  81. package/dist/lib/templates/project/src/commands/hello.ts.liquid +12 -0
  82. package/dist/lib/templates/project/src/config.ts.liquid +16 -0
  83. package/dist/lib/templates/project/src/index.ts.liquid +13 -0
  84. package/dist/lib/templates/project/tsconfig.json.liquid +19 -0
  85. package/dist/lib/templates/project/vitest.config.ts.liquid +8 -0
  86. package/dist/main-C_8x2aB3.js +274 -0
  87. package/dist/main-C_8x2aB3.js.map +1 -0
  88. package/dist/manifest-BIKVDJML.js +112 -0
  89. package/dist/manifest-BIKVDJML.js.map +1 -0
  90. package/dist/multipart-parser-MNkxNO83.js +174 -0
  91. package/dist/multipart-parser-MNkxNO83.js.map +1 -0
  92. package/dist/node-C91xGGqn.js +644 -0
  93. package/dist/node-C91xGGqn.js.map +1 -0
  94. package/dist/node-Drmc03fL.js +3973 -0
  95. package/dist/node-Drmc03fL.js.map +1 -0
  96. package/dist/ohash-BPoV4zBH.js +185 -0
  97. package/dist/ohash-BPoV4zBH.js.map +1 -0
  98. package/dist/pnpm-workspace.yaml +26 -0
  99. package/dist/toml-LuHsHQtV.js +646 -0
  100. package/dist/toml-LuHsHQtV.js.map +1 -0
  101. package/dist/utils-DlIJNGLQ.js +1905 -0
  102. package/dist/utils-DlIJNGLQ.js.map +1 -0
  103. package/dist/validate-C_SXBoLq.js +18 -0
  104. package/dist/validate-C_SXBoLq.js.map +1 -0
  105. package/dist/write-CSwG4Stj.js +152 -0
  106. package/dist/write-CSwG4Stj.js.map +1 -0
  107. package/dist/yaml-BS9RDuUr.js +1067 -0
  108. package/dist/yaml-BS9RDuUr.js.map +1 -0
  109. package/package.json +65 -0
@@ -0,0 +1,246 @@
1
+ import { i as normalizeCompileOptions, r as createBundler } from "../dist-DW_gRP7_.js";
2
+ import { n as loadConfig } from "../utils-DlIJNGLQ.js";
3
+ import { t as extractConfig } from "../config-helpers-pv_2Spux.js";
4
+ import { command } from "maltty";
5
+ import { relative } from "node:path";
6
+ import { z } from "zod";
7
+ import pc from "picocolors";
8
+ //#region src/commands/build.ts
9
+ /**
10
+ * Build a maltty CLI project for production.
11
+ *
12
+ * Loads the project's `maltty.config.ts`, invokes the bundler, and reports
13
+ * the output entry file and directory on success. When `--compile` or
14
+ * `--targets` is provided (or `compile` is set in config), also compiles
15
+ * to standalone binaries via Bun.
16
+ */
17
+ const buildCommand = command({
18
+ options: z.object({
19
+ clean: z.boolean().optional().describe("Clean build artifacts before bundling (default: true)"),
20
+ compile: z.boolean().optional().describe("Compile to standalone binaries after bundling"),
21
+ targets: z.array(z.string()).optional().describe("Compile targets (implies --compile)"),
22
+ verbose: z.boolean().optional().describe("Show detailed error output on bundle or compile failure")
23
+ }),
24
+ description: "Build a maltty CLI project for production",
25
+ handler: async (ctx) => {
26
+ const cwd = process.cwd();
27
+ const startTime = Date.now();
28
+ const [, configResult] = await loadConfig({ cwd });
29
+ const config = mergeCleanOption({
30
+ config: extractConfig(configResult),
31
+ clean: ctx.args.clean
32
+ });
33
+ const shouldCompile = resolveCompileIntent({
34
+ compileFlag: ctx.args.compile,
35
+ configCompile: config.compile,
36
+ targets: ctx.args.targets
37
+ });
38
+ const bundler = await createBundler({
39
+ config: resolveMergedConfig({
40
+ config,
41
+ shouldCompile,
42
+ targets: ctx.args.targets
43
+ }),
44
+ cwd,
45
+ onStepStart: ({ meta }) => ctx.status.spinner.message(`Compiling ${String(meta.label ?? "target")}...`),
46
+ onStepFinish: ({ meta }) => ctx.status.spinner.message(`Compiled ${String(meta.label ?? "target")}`)
47
+ });
48
+ ctx.status.spinner.start("Bundling...");
49
+ const [buildError, buildOutput] = await bundler.build({ verbose: ctx.args.verbose });
50
+ if (buildError) {
51
+ ctx.status.spinner.stop("Bundle failed");
52
+ return ctx.fail(buildError.message);
53
+ }
54
+ if (!shouldCompile) {
55
+ ctx.status.spinner.stop("Build complete");
56
+ ctx.log.note(formatBuildNote({
57
+ cwd,
58
+ define: buildOutput.define,
59
+ entryFile: buildOutput.entryFile,
60
+ outDir: buildOutput.outDir,
61
+ version: buildOutput.version
62
+ }), "Bundle");
63
+ ctx.log.outro(formatOutroSummary({
64
+ binaries: 0,
65
+ duration: Date.now() - startTime
66
+ }));
67
+ return;
68
+ }
69
+ ctx.status.spinner.message("Bundled, compiling binaries...");
70
+ const [compileError, compileOutput] = await bundler.compile({ verbose: ctx.args.verbose });
71
+ if (compileError) {
72
+ ctx.status.spinner.stop("Compile failed");
73
+ return ctx.fail(compileError.message);
74
+ }
75
+ ctx.status.spinner.stop("Build complete");
76
+ ctx.log.note(formatBuildNote({
77
+ cwd,
78
+ define: buildOutput.define,
79
+ entryFile: buildOutput.entryFile,
80
+ outDir: buildOutput.outDir,
81
+ version: buildOutput.version
82
+ }), "Bundle");
83
+ ctx.log.note(formatBinariesNote({
84
+ binaries: compileOutput.binaries,
85
+ cwd
86
+ }), "Binaries");
87
+ ctx.log.outro(formatOutroSummary({
88
+ binaries: compileOutput.binaries.length,
89
+ duration: Date.now() - startTime
90
+ }));
91
+ }
92
+ });
93
+ /**
94
+ * Apply compile-target overrides to the config when compilation will run.
95
+ *
96
+ * @private
97
+ * @param params - The base config, compile decision, and CLI target overrides.
98
+ * @returns The config to hand to the bundler.
99
+ */
100
+ function resolveMergedConfig(params) {
101
+ if (params.shouldCompile) return mergeCompileTargets({
102
+ config: params.config,
103
+ targets: params.targets
104
+ });
105
+ return params.config;
106
+ }
107
+ /**
108
+ * Determine whether compilation should run based on CLI flags and config.
109
+ *
110
+ * Resolution order:
111
+ * 1. `--targets` provided → compile (implied)
112
+ * 2. `--compile` flag → use its boolean value
113
+ * 3. Fall back to config `compile` field (truthy = compile)
114
+ *
115
+ * @private
116
+ * @param params - The CLI flags and config compile field.
117
+ * @returns Whether to run the compile step.
118
+ */
119
+ function resolveCompileIntent(params) {
120
+ if (params.targets && params.targets.length > 0) return true;
121
+ if (params.compileFlag !== void 0) return params.compileFlag;
122
+ if (params.configCompile === true) return true;
123
+ if (typeof params.configCompile === "object") return true;
124
+ return false;
125
+ }
126
+ /**
127
+ * Merge CLI `--targets` into the config's compile options.
128
+ *
129
+ * @private
130
+ * @param params - The config and optional CLI targets.
131
+ * @returns A config with compile targets merged in.
132
+ */
133
+ function mergeCompileTargets(params) {
134
+ if (!params.targets || params.targets.length === 0) return params.config;
135
+ const existingCompile = normalizeCompileOptions(params.config.compile);
136
+ return {
137
+ ...params.config,
138
+ compile: {
139
+ ...existingCompile,
140
+ targets: params.targets
141
+ }
142
+ };
143
+ }
144
+ /**
145
+ * Merge the CLI `--clean` / `--no-clean` flag into the loaded config.
146
+ *
147
+ * @private
148
+ * @param params - The loaded config and optional CLI clean flag.
149
+ * @returns A config with the clean option merged in.
150
+ */
151
+ function mergeCleanOption(params) {
152
+ if (params.clean === void 0) return params.config;
153
+ return {
154
+ ...params.config,
155
+ build: {
156
+ ...params.config.build,
157
+ clean: params.clean
158
+ }
159
+ };
160
+ }
161
+ /**
162
+ * Format bundle output into a multi-line string for display.
163
+ *
164
+ * @private
165
+ * @param params - The build output paths and working directory.
166
+ * @returns A formatted string with entry and output directory.
167
+ */
168
+ function formatBuildNote(params) {
169
+ return [
170
+ `entry ${relative(params.cwd, params.entryFile)}`,
171
+ `output ${relative(params.cwd, params.outDir)}`,
172
+ ...formatVersionLine(params.version),
173
+ ...formatDefineLines(params.define)
174
+ ].join("\n");
175
+ }
176
+ /**
177
+ * Format a version line for the build note, if a version is available.
178
+ *
179
+ * @private
180
+ * @param version - The version string, or undefined.
181
+ * @returns A single-element array with the formatted line, or an empty array.
182
+ */
183
+ function formatVersionLine(version) {
184
+ if (!version) return [];
185
+ return [`version ${version}`];
186
+ }
187
+ /**
188
+ * Format define constants into display lines.
189
+ *
190
+ * Omits `__MALTTY_VERSION__` (already shown as `version`) and returns
191
+ * remaining entries as `define key = value` lines.
192
+ *
193
+ * @private
194
+ * @param define - The resolved define map.
195
+ * @returns An array of formatted lines (empty when no user-defined constants).
196
+ */
197
+ /**
198
+ * Format the outro summary line with build stats.
199
+ *
200
+ * @private
201
+ * @param params - Build stats for the summary.
202
+ * @returns A formatted inline summary string.
203
+ */
204
+ function formatOutroSummary(params) {
205
+ return [...formatBinariesSegment(params.binaries), `finished in ${formatDuration(params.duration)}`].join(pc.gray(" · "));
206
+ }
207
+ /**
208
+ * Build the optional "binaries compiled" segment of the outro summary.
209
+ *
210
+ * @private
211
+ * @param count - The number of compiled binaries.
212
+ * @returns A single-element array describing the count, or an empty array.
213
+ */
214
+ function formatBinariesSegment(count) {
215
+ if (count > 0) return [`${count} binaries compiled`];
216
+ return [];
217
+ }
218
+ /**
219
+ * Format a millisecond duration into a human-readable string.
220
+ *
221
+ * @private
222
+ * @param ms - Duration in milliseconds.
223
+ * @returns A formatted duration string (e.g. "1.2s", "350ms").
224
+ */
225
+ function formatDuration(ms) {
226
+ if (ms >= 1e3) return `${(ms / 1e3).toFixed(1)}s`;
227
+ return `${ms}ms`;
228
+ }
229
+ function formatDefineLines(define) {
230
+ return Object.entries(define).map(([key, value]) => `build_var ${key} = ${value}`);
231
+ }
232
+ /**
233
+ * Format compiled binaries into an aligned, multi-line string for display.
234
+ *
235
+ * @private
236
+ * @param params - The binaries and working directory for relative path resolution.
237
+ * @returns A formatted string with one line per binary.
238
+ */
239
+ function formatBinariesNote(params) {
240
+ const maxLen = Math.max(...params.binaries.map((b) => b.label.length));
241
+ return params.binaries.map((binary) => `${binary.label.padEnd(maxLen)} ${relative(params.cwd, binary.path)}`).join("\n");
242
+ }
243
+ //#endregion
244
+ export { buildCommand as default };
245
+
246
+ //# sourceMappingURL=build.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.js","names":[],"sources":["../../src/commands/build.ts"],"sourcesContent":["import { relative } from 'node:path'\n\nimport { createBundler, normalizeCompileOptions } from '@maltty/bundler'\nimport type { CompiledBinary } from '@maltty/bundler'\nimport type { CompileTarget, MalttyConfig } from '@maltty/config'\nimport { loadConfig } from '@maltty/config/utils'\nimport { command } from 'maltty'\nimport type { Command, CommandContext } from 'maltty'\nimport pc from 'picocolors'\nimport { z } from 'zod'\n\nimport { extractConfig } from '../lib/config-helpers.js'\n\nconst options = z.object({\n clean: z.boolean().optional().describe('Clean build artifacts before bundling (default: true)'),\n compile: z.boolean().optional().describe('Compile to standalone binaries after bundling'),\n targets: z.array(z.string()).optional().describe('Compile targets (implies --compile)'),\n verbose: z\n .boolean()\n .optional()\n .describe('Show detailed error output on bundle or compile failure'),\n})\n\ntype BuildArgs = z.infer<typeof options>\n\n/**\n * Build a maltty CLI project for production.\n *\n * Loads the project's `maltty.config.ts`, invokes the bundler, and reports\n * the output entry file and directory on success. When `--compile` or\n * `--targets` is provided (or `compile` is set in config), also compiles\n * to standalone binaries via Bun.\n */\nconst buildCommand: Command = command({\n options,\n description: 'Build a maltty CLI project for production',\n handler: async (ctx: CommandContext<BuildArgs>) => {\n const cwd = process.cwd()\n const startTime = Date.now()\n\n const [, configResult] = await loadConfig({ cwd })\n const config = mergeCleanOption({ config: extractConfig(configResult), clean: ctx.args.clean })\n\n const shouldCompile = resolveCompileIntent({\n compileFlag: ctx.args.compile,\n configCompile: config.compile,\n targets: ctx.args.targets,\n })\n\n const mergedConfig = resolveMergedConfig({\n config,\n shouldCompile,\n targets: ctx.args.targets,\n })\n\n const bundler = await createBundler({\n config: mergedConfig,\n cwd,\n onStepStart: ({ meta }) =>\n ctx.status.spinner.message(`Compiling ${String(meta.label ?? 'target')}...`),\n onStepFinish: ({ meta }) =>\n ctx.status.spinner.message(`Compiled ${String(meta.label ?? 'target')}`),\n })\n\n ctx.status.spinner.start('Bundling...')\n\n const [buildError, buildOutput] = await bundler.build({ verbose: ctx.args.verbose })\n\n if (buildError) {\n ctx.status.spinner.stop('Bundle failed')\n return ctx.fail(buildError.message)\n }\n\n if (!shouldCompile) {\n ctx.status.spinner.stop('Build complete')\n ctx.log.note(\n formatBuildNote({\n cwd,\n define: buildOutput.define,\n entryFile: buildOutput.entryFile,\n outDir: buildOutput.outDir,\n version: buildOutput.version,\n }),\n 'Bundle'\n )\n ctx.log.outro(formatOutroSummary({ binaries: 0, duration: Date.now() - startTime }))\n return\n }\n\n ctx.status.spinner.message('Bundled, compiling binaries...')\n\n const [compileError, compileOutput] = await bundler.compile({\n verbose: ctx.args.verbose,\n })\n\n if (compileError) {\n ctx.status.spinner.stop('Compile failed')\n return ctx.fail(compileError.message)\n }\n\n ctx.status.spinner.stop('Build complete')\n ctx.log.note(\n formatBuildNote({\n cwd,\n define: buildOutput.define,\n entryFile: buildOutput.entryFile,\n outDir: buildOutput.outDir,\n version: buildOutput.version,\n }),\n 'Bundle'\n )\n ctx.log.note(formatBinariesNote({ binaries: compileOutput.binaries, cwd }), 'Binaries')\n ctx.log.outro(\n formatOutroSummary({\n binaries: compileOutput.binaries.length,\n duration: Date.now() - startTime,\n })\n )\n },\n})\n\nexport default buildCommand\n\n// ---------------------------------------------------------------------------\n\n/**\n * Apply compile-target overrides to the config when compilation will run.\n *\n * @private\n * @param params - The base config, compile decision, and CLI target overrides.\n * @returns The config to hand to the bundler.\n */\nfunction resolveMergedConfig(params: {\n readonly config: MalttyConfig\n readonly shouldCompile: boolean\n readonly targets: readonly string[] | undefined\n}): MalttyConfig {\n if (params.shouldCompile) {\n return mergeCompileTargets({ config: params.config, targets: params.targets })\n }\n return params.config\n}\n\n/**\n * Determine whether compilation should run based on CLI flags and config.\n *\n * Resolution order:\n * 1. `--targets` provided → compile (implied)\n * 2. `--compile` flag → use its boolean value\n * 3. Fall back to config `compile` field (truthy = compile)\n *\n * @private\n * @param params - The CLI flags and config compile field.\n * @returns Whether to run the compile step.\n */\nfunction resolveCompileIntent(params: {\n readonly targets: readonly string[] | undefined\n readonly compileFlag: boolean | undefined\n readonly configCompile: boolean | MalttyConfig['compile']\n}): boolean {\n if (params.targets && params.targets.length > 0) {\n return true\n }\n\n if (params.compileFlag !== undefined) {\n return params.compileFlag\n }\n\n if (params.configCompile === true) {\n return true\n }\n\n if (typeof params.configCompile === 'object') {\n return true\n }\n\n return false\n}\n\n/**\n * Merge CLI `--targets` into the config's compile options.\n *\n * @private\n * @param params - The config and optional CLI targets.\n * @returns A config with compile targets merged in.\n */\nfunction mergeCompileTargets(params: {\n readonly config: MalttyConfig\n readonly targets: readonly string[] | undefined\n}): MalttyConfig {\n if (!params.targets || params.targets.length === 0) {\n return params.config\n }\n\n const existingCompile = normalizeCompileOptions(params.config.compile)\n\n return {\n ...params.config,\n compile: {\n ...existingCompile,\n targets: params.targets as CompileTarget[],\n },\n }\n}\n\n/**\n * Merge the CLI `--clean` / `--no-clean` flag into the loaded config.\n *\n * @private\n * @param params - The loaded config and optional CLI clean flag.\n * @returns A config with the clean option merged in.\n */\nfunction mergeCleanOption(params: {\n readonly config: MalttyConfig\n readonly clean: boolean | undefined\n}): MalttyConfig {\n if (params.clean === undefined) {\n return params.config\n }\n\n return {\n ...params.config,\n build: {\n ...params.config.build,\n clean: params.clean,\n },\n }\n}\n\n/**\n * Format bundle output into a multi-line string for display.\n *\n * @private\n * @param params - The build output paths and working directory.\n * @returns A formatted string with entry and output directory.\n */\nfunction formatBuildNote(params: {\n readonly entryFile: string\n readonly outDir: string\n readonly cwd: string\n readonly version: string | undefined\n readonly define: Readonly<Record<string, string>>\n}): string {\n return [\n `entry ${relative(params.cwd, params.entryFile)}`,\n `output ${relative(params.cwd, params.outDir)}`,\n ...formatVersionLine(params.version),\n ...formatDefineLines(params.define),\n ].join('\\n')\n}\n\n/**\n * Format a version line for the build note, if a version is available.\n *\n * @private\n * @param version - The version string, or undefined.\n * @returns A single-element array with the formatted line, or an empty array.\n */\nfunction formatVersionLine(version: string | undefined): string[] {\n if (!version) {\n return []\n }\n\n return [`version ${version}`]\n}\n\n/**\n * Format define constants into display lines.\n *\n * Omits `__MALTTY_VERSION__` (already shown as `version`) and returns\n * remaining entries as `define key = value` lines.\n *\n * @private\n * @param define - The resolved define map.\n * @returns An array of formatted lines (empty when no user-defined constants).\n */\n/**\n * Format the outro summary line with build stats.\n *\n * @private\n * @param params - Build stats for the summary.\n * @returns A formatted inline summary string.\n */\nfunction formatOutroSummary(params: {\n readonly binaries: number\n readonly duration: number\n}): string {\n const stats = [\n ...formatBinariesSegment(params.binaries),\n `finished in ${formatDuration(params.duration)}`,\n ]\n\n return stats.join(pc.gray(' · '))\n}\n\n/**\n * Build the optional \"binaries compiled\" segment of the outro summary.\n *\n * @private\n * @param count - The number of compiled binaries.\n * @returns A single-element array describing the count, or an empty array.\n */\nfunction formatBinariesSegment(count: number): readonly string[] {\n if (count > 0) {\n return [`${count} binaries compiled`]\n }\n return []\n}\n\n/**\n * Format a millisecond duration into a human-readable string.\n *\n * @private\n * @param ms - Duration in milliseconds.\n * @returns A formatted duration string (e.g. \"1.2s\", \"350ms\").\n */\nfunction formatDuration(ms: number): string {\n if (ms >= 1000) {\n return `${(ms / 1000).toFixed(1)}s`\n }\n return `${ms}ms`\n}\n\nfunction formatDefineLines(define: Readonly<Record<string, string>>): string[] {\n return Object.entries(define).map(([key, value]) => `build_var ${key} = ${value}`)\n}\n\n/**\n * Format compiled binaries into an aligned, multi-line string for display.\n *\n * @private\n * @param params - The binaries and working directory for relative path resolution.\n * @returns A formatted string with one line per binary.\n */\nfunction formatBinariesNote(params: {\n readonly binaries: readonly CompiledBinary[]\n readonly cwd: string\n}): string {\n const maxLen = Math.max(...params.binaries.map((b) => b.label.length))\n\n return params.binaries\n .map((binary) => `${binary.label.padEnd(maxLen)} ${relative(params.cwd, binary.path)}`)\n .join('\\n')\n}\n"],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { Command } from "maltty";
2
+
3
+ //#region src/commands/commands.d.ts
4
+ /**
5
+ * Display the command tree for a maltty CLI project.
6
+ *
7
+ * Loads the project's `maltty.config.ts` to locate the commands directory,
8
+ * scans it with the autoloader, and prints an ASCII tree of all discovered
9
+ * commands and subcommands.
10
+ */
11
+ declare const commandsCommand: Command;
12
+ //#endregion
13
+ export { commandsCommand as default };
14
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.d.ts","names":[],"sources":["../../src/commands/commands.ts"],"mappings":";;;;;AAK8D;;;;;cAoBxD,eAAA,EAAiB,OAAA"}
@@ -0,0 +1,189 @@
1
+ import { r as fs_exports } from "../node-C91xGGqn.js";
2
+ import { n as loadConfig } from "../utils-DlIJNGLQ.js";
3
+ import { t as extractConfig } from "../config-helpers-pv_2Spux.js";
4
+ import { autoload, command } from "maltty";
5
+ import { join } from "node:path";
6
+ //#region src/commands/commands.ts
7
+ /**
8
+ * Display the command tree for a maltty CLI project.
9
+ *
10
+ * Loads the project's `maltty.config.ts` to locate the commands directory,
11
+ * scans it with the autoloader, and prints an ASCII tree of all discovered
12
+ * commands and subcommands.
13
+ */
14
+ const commandsCommand = command({
15
+ description: "Display the command tree for a maltty CLI project",
16
+ handler: async (ctx) => {
17
+ const cwd = process.cwd();
18
+ const [, configResult] = await loadConfig({ cwd });
19
+ const commandsDir = join(cwd, extractConfig(configResult).commands ?? "commands");
20
+ if (!await fs_exports.exists(commandsDir)) return ctx.fail(`Commands directory not found: ${commandsDir}`);
21
+ ctx.status.spinner.start("Scanning commands...");
22
+ const tree = await buildTree(await autoload({ dir: commandsDir }));
23
+ ctx.status.spinner.stop("Commands");
24
+ if (tree.length === 0) {
25
+ ctx.log.raw("No commands found");
26
+ return;
27
+ }
28
+ ctx.log.raw(renderTree(tree));
29
+ }
30
+ });
31
+ /**
32
+ * Resolve a command's subcommands field, which may be a Promise, a map, or undefined.
33
+ *
34
+ * @private
35
+ * @param commands - The raw subcommands value from a Command object.
36
+ * @returns The resolved CommandMap, or an empty object when none exist.
37
+ */
38
+ async function resolveSubcommands(commands) {
39
+ if (!commands) return {};
40
+ if (commands instanceof Promise) return commands;
41
+ return commands;
42
+ }
43
+ /**
44
+ * Recursively build a sorted tree of entries from a CommandMap.
45
+ *
46
+ * Commands listed in the order array appear first in the specified order;
47
+ * omitted commands fall back to alphabetical sort.
48
+ *
49
+ * @private
50
+ * @param commandMap - The map of command names to Command objects.
51
+ * @param order - Optional array of command names defining display order.
52
+ * @returns A sorted array of TreeEntry nodes.
53
+ */
54
+ async function buildTree(commandMap, order) {
55
+ const entries = sortEntries({
56
+ entries: Object.entries(commandMap),
57
+ order
58
+ });
59
+ return Promise.all(entries.map(async ([name, cmd]) => {
60
+ return {
61
+ children: await buildTree(await resolveSubcommands(cmd.commands), cmd.help?.order),
62
+ description: cmd.description ?? "",
63
+ name
64
+ };
65
+ }));
66
+ }
67
+ /**
68
+ * Sort command entries with ordered names first (in specified order),
69
+ * remaining names alphabetically.
70
+ *
71
+ * Validates the order array against available command names and logs a
72
+ * warning for unknown or duplicate entries rather than failing silently.
73
+ *
74
+ * @private
75
+ * @param params - The command entries and optional order array.
76
+ * @returns Sorted array of entries.
77
+ */
78
+ function sortEntries(params) {
79
+ const { entries, order } = params;
80
+ if (!order || order.length === 0) return entries.toSorted(([a], [b]) => a.localeCompare(b));
81
+ const validOrder = validateOrder({
82
+ commandNames: entries.map(([name]) => name),
83
+ order
84
+ });
85
+ const entryMap = new Map(entries);
86
+ const orderedSet = new Set(validOrder);
87
+ const ordered = validOrder.filter((name) => entryMap.has(name)).map((name) => [name, entryMap.get(name)]);
88
+ const remaining = entries.filter(([name]) => !orderedSet.has(name)).toSorted(([a], [b]) => a.localeCompare(b));
89
+ return [...ordered, ...remaining];
90
+ }
91
+ /**
92
+ * Validate the order array by filtering out unknown and duplicate names.
93
+ *
94
+ * Logs warnings for invalid entries so developers see the mismatch
95
+ * during `maltty commands` introspection, matching the runtime behaviour.
96
+ *
97
+ * @private
98
+ * @param params - The order array and available command names.
99
+ * @returns A deduplicated array of valid order names.
100
+ */
101
+ function validateOrder(params) {
102
+ const { commandNames, order } = params;
103
+ const nameSet = new Set(commandNames);
104
+ const { valid } = order.reduce((acc, name) => {
105
+ if (acc.seen.has(name)) {
106
+ console.warn(`Warning: duplicate command name "${name}" in order array`);
107
+ return acc;
108
+ }
109
+ if (!nameSet.has(name)) {
110
+ console.warn(`Warning: unknown command "${name}" in order array`);
111
+ return {
112
+ seen: new Set([...acc.seen, name]),
113
+ valid: acc.valid
114
+ };
115
+ }
116
+ return {
117
+ seen: new Set([...acc.seen, name]),
118
+ valid: [...acc.valid, name]
119
+ };
120
+ }, {
121
+ seen: /* @__PURE__ */ new Set(),
122
+ valid: []
123
+ });
124
+ return valid;
125
+ }
126
+ /**
127
+ * Render a tree of entries into an ASCII tree string.
128
+ *
129
+ * @private
130
+ * @param entries - The top-level tree entries to render.
131
+ * @returns The formatted tree string.
132
+ */
133
+ function renderTree(entries) {
134
+ return renderEntries(entries, "").join("\n");
135
+ }
136
+ /**
137
+ * Recursively render tree entries with proper box-drawing connectors.
138
+ *
139
+ * @private
140
+ * @param entries - The entries at this level.
141
+ * @param prefix - The prefix string for indentation.
142
+ * @returns An array of formatted lines.
143
+ */
144
+ function renderEntries(entries, prefix) {
145
+ return entries.flatMap((entry, index) => {
146
+ const isLast = index === entries.length - 1;
147
+ const connector = resolveConnector(isLast);
148
+ const childPrefix = resolveChildPrefix(isLast);
149
+ return [`${prefix}${connector}${formatLabel(entry.name, entry.description)}`, ...renderEntries(entry.children, `${prefix}${childPrefix}`)];
150
+ });
151
+ }
152
+ /**
153
+ * Get the box-drawing connector for a tree entry.
154
+ *
155
+ * @private
156
+ * @param isLast - Whether this is the last entry at its level.
157
+ * @returns The connector string.
158
+ */
159
+ function resolveConnector(isLast) {
160
+ if (isLast) return "└── ";
161
+ return "├── ";
162
+ }
163
+ /**
164
+ * Get the indentation prefix for children of a tree entry.
165
+ *
166
+ * @private
167
+ * @param isLast - Whether the parent is the last entry at its level.
168
+ * @returns The child prefix string.
169
+ */
170
+ function resolveChildPrefix(isLast) {
171
+ if (isLast) return " ";
172
+ return "│ ";
173
+ }
174
+ /**
175
+ * Format a command name and description into a tree label.
176
+ *
177
+ * @private
178
+ * @param name - The command name.
179
+ * @param description - The command description (may be empty).
180
+ * @returns The formatted label string.
181
+ */
182
+ function formatLabel(name, description) {
183
+ if (description) return `${name} — ${description}`;
184
+ return name;
185
+ }
186
+ //#endregion
187
+ export { commandsCommand as default };
188
+
189
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","names":["fs"],"sources":["../../src/commands/commands.ts"],"sourcesContent":["import { join } from 'node:path'\n\nimport { loadConfig } from '@maltty/config/utils'\nimport { fs } from '@maltty/utils/node'\nimport { autoload, command } from 'maltty'\nimport type { Command as MalttyCommand, CommandContext } from 'maltty'\n\nimport { extractConfig } from '../lib/config-helpers.js'\n\n/**\n * A single node in the rendered command tree.\n */\ninterface TreeEntry {\n readonly name: string\n readonly description: string\n readonly children: readonly TreeEntry[]\n}\n\n/**\n * Display the command tree for a maltty CLI project.\n *\n * Loads the project's `maltty.config.ts` to locate the commands directory,\n * scans it with the autoloader, and prints an ASCII tree of all discovered\n * commands and subcommands.\n */\nconst commandsCommand: MalttyCommand = command({\n description: 'Display the command tree for a maltty CLI project',\n handler: async (ctx: CommandContext) => {\n const cwd = process.cwd()\n\n const [, configResult] = await loadConfig({ cwd })\n const config = extractConfig(configResult)\n\n const commandsDir = join(cwd, config.commands ?? 'commands')\n\n if (!(await fs.exists(commandsDir))) {\n return ctx.fail(`Commands directory not found: ${commandsDir}`)\n }\n\n ctx.status.spinner.start('Scanning commands...')\n\n const commandMap = await autoload({ dir: commandsDir })\n const tree = await buildTree(commandMap)\n\n ctx.status.spinner.stop('Commands')\n\n if (tree.length === 0) {\n ctx.log.raw('No commands found')\n return\n }\n\n ctx.log.raw(renderTree(tree))\n },\n})\n\nexport default commandsCommand\n\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve a command's subcommands field, which may be a Promise, a map, or undefined.\n *\n * @private\n * @param commands - The raw subcommands value from a Command object.\n * @returns The resolved CommandMap, or an empty object when none exist.\n */\nasync function resolveSubcommands(\n commands: Record<string, MalttyCommand> | Promise<Record<string, MalttyCommand>> | undefined\n): Promise<Record<string, MalttyCommand>> {\n if (!commands) {\n return {}\n }\n\n if (commands instanceof Promise) {\n return commands\n }\n\n return commands\n}\n\n/**\n * Recursively build a sorted tree of entries from a CommandMap.\n *\n * Commands listed in the order array appear first in the specified order;\n * omitted commands fall back to alphabetical sort.\n *\n * @private\n * @param commandMap - The map of command names to Command objects.\n * @param order - Optional array of command names defining display order.\n * @returns A sorted array of TreeEntry nodes.\n */\nasync function buildTree(\n commandMap: Record<string, MalttyCommand>,\n order?: readonly string[]\n): Promise<readonly TreeEntry[]> {\n const entries = sortEntries({ entries: Object.entries(commandMap), order })\n\n return Promise.all(\n entries.map(async ([name, cmd]): Promise<TreeEntry> => {\n const subMap = await resolveSubcommands(cmd.commands)\n const children = await buildTree(subMap, cmd.help?.order)\n\n return {\n children,\n description: cmd.description ?? '',\n name,\n }\n })\n )\n}\n\n/**\n * Sort command entries with ordered names first (in specified order),\n * remaining names alphabetically.\n *\n * Validates the order array against available command names and logs a\n * warning for unknown or duplicate entries rather than failing silently.\n *\n * @private\n * @param params - The command entries and optional order array.\n * @returns Sorted array of entries.\n */\nfunction sortEntries(params: {\n readonly entries: [string, MalttyCommand][]\n readonly order?: readonly string[]\n}): [string, MalttyCommand][] {\n const { entries, order } = params\n\n if (!order || order.length === 0) {\n return entries.toSorted(([a], [b]) => a.localeCompare(b))\n }\n\n const commandNames = entries.map(([name]) => name)\n const validOrder = validateOrder({ commandNames, order })\n\n const entryMap = new Map(entries)\n const orderedSet = new Set(validOrder)\n\n const ordered = validOrder\n .filter((name) => entryMap.has(name))\n .map((name): [string, MalttyCommand] => [name, entryMap.get(name) as MalttyCommand])\n\n const remaining = entries\n .filter(([name]) => !orderedSet.has(name))\n .toSorted(([a], [b]) => a.localeCompare(b))\n\n return [...ordered, ...remaining]\n}\n\n/**\n * Validate the order array by filtering out unknown and duplicate names.\n *\n * Logs warnings for invalid entries so developers see the mismatch\n * during `maltty commands` introspection, matching the runtime behaviour.\n *\n * @private\n * @param params - The order array and available command names.\n * @returns A deduplicated array of valid order names.\n */\nfunction validateOrder(params: {\n readonly commandNames: readonly string[]\n readonly order: readonly string[]\n}): readonly string[] {\n const { commandNames, order } = params\n const nameSet = new Set(commandNames)\n\n const { valid } = order.reduce<{\n readonly seen: ReadonlySet<string>\n readonly valid: readonly string[]\n }>(\n (acc, name) => {\n if (acc.seen.has(name)) {\n console.warn(`Warning: duplicate command name \"${name}\" in order array`)\n return acc\n }\n\n if (!nameSet.has(name)) {\n console.warn(`Warning: unknown command \"${name}\" in order array`)\n return { seen: new Set([...acc.seen, name]), valid: acc.valid }\n }\n\n return { seen: new Set([...acc.seen, name]), valid: [...acc.valid, name] }\n },\n { seen: new Set<string>(), valid: [] }\n )\n\n return valid\n}\n\n/**\n * Render a tree of entries into an ASCII tree string.\n *\n * @private\n * @param entries - The top-level tree entries to render.\n * @returns The formatted tree string.\n */\nfunction renderTree(entries: readonly TreeEntry[]): string {\n return renderEntries(entries, '').join('\\n')\n}\n\n/**\n * Recursively render tree entries with proper box-drawing connectors.\n *\n * @private\n * @param entries - The entries at this level.\n * @param prefix - The prefix string for indentation.\n * @returns An array of formatted lines.\n */\nfunction renderEntries(entries: readonly TreeEntry[], prefix: string): readonly string[] {\n return entries.flatMap((entry, index) => {\n const isLast = index === entries.length - 1\n const connector = resolveConnector(isLast)\n const childPrefix = resolveChildPrefix(isLast)\n const label = formatLabel(entry.name, entry.description)\n const line = `${prefix}${connector}${label}`\n const childLines = renderEntries(entry.children, `${prefix}${childPrefix}`)\n\n return [line, ...childLines]\n })\n}\n\n/**\n * Get the box-drawing connector for a tree entry.\n *\n * @private\n * @param isLast - Whether this is the last entry at its level.\n * @returns The connector string.\n */\nfunction resolveConnector(isLast: boolean): string {\n if (isLast) {\n return '└── '\n }\n\n return '├── '\n}\n\n/**\n * Get the indentation prefix for children of a tree entry.\n *\n * @private\n * @param isLast - Whether the parent is the last entry at its level.\n * @returns The child prefix string.\n */\nfunction resolveChildPrefix(isLast: boolean): string {\n if (isLast) {\n return ' '\n }\n\n return '│ '\n}\n\n/**\n * Format a command name and description into a tree label.\n *\n * @private\n * @param name - The command name.\n * @param description - The command description (may be empty).\n * @returns The formatted label string.\n */\nfunction formatLabel(name: string, description: string): string {\n if (description) {\n return `${name} — ${description}`\n }\n\n return name\n}\n"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { Command } from "maltty";
2
+
3
+ //#region src/commands/dev.d.ts
4
+ /**
5
+ * Start a maltty CLI project in development mode with file watching.
6
+ *
7
+ * Loads the project's `maltty.config.ts`, starts the bundler in watch mode, and
8
+ * logs rebuild status on each successful build.
9
+ */
10
+ declare const devCommand: Command;
11
+ //#endregion
12
+ export { devCommand as default };
13
+ //# sourceMappingURL=dev.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev.d.ts","names":[],"sources":["../../src/commands/dev.ts"],"mappings":";;;;;AAG6C;;;;cAUvC,UAAA,EAAY,OAAA"}
@@ -0,0 +1,58 @@
1
+ import { r as createBundler } from "../dist-DW_gRP7_.js";
2
+ import { n as loadConfig } from "../utils-DlIJNGLQ.js";
3
+ import { t as extractConfig } from "../config-helpers-pv_2Spux.js";
4
+ import { command } from "maltty";
5
+ //#region src/commands/dev.ts
6
+ /**
7
+ * Start a maltty CLI project in development mode with file watching.
8
+ *
9
+ * Loads the project's `maltty.config.ts`, starts the bundler in watch mode, and
10
+ * logs rebuild status on each successful build.
11
+ */
12
+ const devCommand = command({
13
+ description: "Start a maltty CLI project in development mode",
14
+ handler: async (ctx) => {
15
+ const cwd = process.cwd();
16
+ const [, configResult] = await loadConfig({ cwd });
17
+ const config = extractConfig(configResult);
18
+ ctx.status.spinner.start("Starting dev server...");
19
+ const bundler = await createBundler({
20
+ config,
21
+ cwd
22
+ });
23
+ const onSuccess = createOnSuccess(ctx);
24
+ const [watchError] = await bundler.watch({ onSuccess });
25
+ if (watchError) {
26
+ ctx.status.spinner.stop("Watch failed");
27
+ return ctx.fail(watchError.message);
28
+ }
29
+ }
30
+ });
31
+ /**
32
+ * Create an onSuccess callback that tracks first-build state.
33
+ *
34
+ * On the first invocation the spinner is stopped and a "watching" message is
35
+ * logged. Subsequent invocations log a "rebuilt" message instead.
36
+ *
37
+ * Uses a mutable container object inside the closure to track build count
38
+ * without `let` reassignment.
39
+ *
40
+ * @private
41
+ * @param ctx - The command context for logging and spinner access.
42
+ * @returns A callback suitable for the watch `onSuccess` parameter.
43
+ */
44
+ function createOnSuccess(ctx) {
45
+ const state = { buildCount: 0 };
46
+ return () => {
47
+ if (state.buildCount === 0) {
48
+ state.buildCount = 1;
49
+ ctx.status.spinner.stop("Watching for changes...");
50
+ return;
51
+ }
52
+ ctx.log.success("Rebuilt successfully");
53
+ };
54
+ }
55
+ //#endregion
56
+ export { devCommand as default };
57
+
58
+ //# sourceMappingURL=dev.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev.js","names":[],"sources":["../../src/commands/dev.ts"],"sourcesContent":["import { createBundler } from '@maltty/bundler'\nimport { loadConfig } from '@maltty/config/utils'\nimport { command } from 'maltty'\nimport type { Command, CommandContext } from 'maltty'\n\nimport { extractConfig } from '../lib/config-helpers.js'\n\n/**\n * Start a maltty CLI project in development mode with file watching.\n *\n * Loads the project's `maltty.config.ts`, starts the bundler in watch mode, and\n * logs rebuild status on each successful build.\n */\nconst devCommand: Command = command({\n description: 'Start a maltty CLI project in development mode',\n handler: async (ctx: CommandContext) => {\n const cwd = process.cwd()\n\n const [, configResult] = await loadConfig({ cwd })\n const config = extractConfig(configResult)\n\n ctx.status.spinner.start('Starting dev server...')\n\n const bundler = await createBundler({ config, cwd })\n const onSuccess = createOnSuccess(ctx)\n\n const [watchError] = await bundler.watch({ onSuccess })\n\n if (watchError) {\n ctx.status.spinner.stop('Watch failed')\n return ctx.fail(watchError.message)\n }\n },\n})\n\nexport default devCommand\n\n// ---------------------------------------------------------------------------\n\n/**\n * Create an onSuccess callback that tracks first-build state.\n *\n * On the first invocation the spinner is stopped and a \"watching\" message is\n * logged. Subsequent invocations log a \"rebuilt\" message instead.\n *\n * Uses a mutable container object inside the closure to track build count\n * without `let` reassignment.\n *\n * @private\n * @param ctx - The command context for logging and spinner access.\n * @returns A callback suitable for the watch `onSuccess` parameter.\n */\nfunction createOnSuccess(ctx: CommandContext): () => void {\n const state = { buildCount: 0 }\n\n return () => {\n if (state.buildCount === 0) {\n state.buildCount = 1\n ctx.status.spinner.stop('Watching for changes...')\n return\n }\n\n ctx.log.success('Rebuilt successfully')\n }\n}\n"],"mappings":";;;;;;;;;;;AAaA,MAAM,aAAsB,QAAQ;CAClC,aAAa;CACb,SAAS,OAAO,QAAwB;EACtC,MAAM,MAAM,QAAQ,KAAK;EAEzB,MAAM,GAAG,gBAAgB,MAAM,WAAW,EAAE,KAAK,CAAC;EAClD,MAAM,SAAS,cAAc,aAAa;AAE1C,MAAI,OAAO,QAAQ,MAAM,yBAAyB;EAElD,MAAM,UAAU,MAAM,cAAc;GAAE;GAAQ;GAAK,CAAC;EACpD,MAAM,YAAY,gBAAgB,IAAI;EAEtC,MAAM,CAAC,cAAc,MAAM,QAAQ,MAAM,EAAE,WAAW,CAAC;AAEvD,MAAI,YAAY;AACd,OAAI,OAAO,QAAQ,KAAK,eAAe;AACvC,UAAO,IAAI,KAAK,WAAW,QAAQ;;;CAGxC,CAAC;;;;;;;;;;;;;;AAmBF,SAAS,gBAAgB,KAAiC;CACxD,MAAM,QAAQ,EAAE,YAAY,GAAG;AAE/B,cAAa;AACX,MAAI,MAAM,eAAe,GAAG;AAC1B,SAAM,aAAa;AACnB,OAAI,OAAO,QAAQ,KAAK,0BAA0B;AAClD;;AAGF,MAAI,IAAI,QAAQ,uBAAuB"}
@@ -0,0 +1,13 @@
1
+ import { Command } from "maltty";
2
+
3
+ //#region src/commands/doctor.d.ts
4
+ /**
5
+ * Diagnose common maltty project issues.
6
+ *
7
+ * Validates config, checks package.json setup, verifies entry points exist,
8
+ * and catches anything that could cause build or runtime failures.
9
+ */
10
+ declare const doctorCommand: Command;
11
+ //#endregion
12
+ export { doctorCommand as default };
13
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.d.ts","names":[],"sources":["../../src/commands/doctor.ts"],"mappings":";;;;;AAI6C;;;;cAmBvC,aAAA,EAAe,OAAA"}