@linxiraos/pi-utils 1.0.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 (244) hide show
  1. package/CHANGELOG.md +449 -0
  2. package/README.md +36 -0
  3. package/dist/types/abortable.d.ts +32 -0
  4. package/dist/types/acp/connection.d.ts +118 -0
  5. package/dist/types/acp/protocol.d.ts +526 -0
  6. package/dist/types/acp/schema.d.ts +41 -0
  7. package/dist/types/acp/stream.d.ts +8 -0
  8. package/dist/types/acp/transport.d.ts +81 -0
  9. package/dist/types/acp.d.ts +6 -0
  10. package/dist/types/async.d.ts +20 -0
  11. package/dist/types/binary.d.ts +17 -0
  12. package/dist/types/browsers.d.ts +68 -0
  13. package/dist/types/chalk.d.ts +125 -0
  14. package/dist/types/cli.d.ts +134 -0
  15. package/dist/types/color.d.ts +102 -0
  16. package/dist/types/dates.d.ts +7 -0
  17. package/dist/types/dirs.d.ts +282 -0
  18. package/dist/types/docx/converter.d.ts +46 -0
  19. package/dist/types/docx/xml.d.ts +26 -0
  20. package/dist/types/docx/zip.d.ts +6 -0
  21. package/dist/types/docx.d.ts +11 -0
  22. package/dist/types/dom/core.d.ts +431 -0
  23. package/dist/types/dom/parser.d.ts +7 -0
  24. package/dist/types/dom/selector.d.ts +5 -0
  25. package/dist/types/dom.d.ts +5 -0
  26. package/dist/types/env.d.ts +124 -0
  27. package/dist/types/fetch-retry.d.ts +96 -0
  28. package/dist/types/file-lock.d.ts +23 -0
  29. package/dist/types/format.d.ts +37 -0
  30. package/dist/types/frontmatter.d.ts +46 -0
  31. package/dist/types/fs-error.d.ts +31 -0
  32. package/dist/types/glob.d.ts +28 -0
  33. package/dist/types/headers.d.ts +34 -0
  34. package/dist/types/index.d.ts +39 -0
  35. package/dist/types/json-parse.d.ts +74 -0
  36. package/dist/types/json.d.ts +14 -0
  37. package/dist/types/logger/rotating-file.d.ts +18 -0
  38. package/dist/types/logger.d.ts +96 -0
  39. package/dist/types/loop-phase.d.ts +10 -0
  40. package/dist/types/lru.d.ts +46 -0
  41. package/dist/types/marked/core.d.ts +445 -0
  42. package/dist/types/marked.d.ts +2 -0
  43. package/dist/types/mermaid-ascii.d.ts +11 -0
  44. package/dist/types/mime.d.ts +29 -0
  45. package/dist/types/module-timer.d.ts +1 -0
  46. package/dist/types/path-tree.d.ts +76 -0
  47. package/dist/types/path.d.ts +2 -0
  48. package/dist/types/peek-file.d.ts +29 -0
  49. package/dist/types/postmortem.d.ts +104 -0
  50. package/dist/types/process-name.d.ts +7 -0
  51. package/dist/types/procmgr.d.ts +74 -0
  52. package/dist/types/prompt.d.ts +18 -0
  53. package/dist/types/ptree.d.ts +110 -0
  54. package/dist/types/readability/readability.d.ts +9 -0
  55. package/dist/types/readability/readerable.d.ts +10 -0
  56. package/dist/types/readability/types.d.ts +70 -0
  57. package/dist/types/readability.d.ts +4 -0
  58. package/dist/types/ring.d.ts +93 -0
  59. package/dist/types/runtime-install.d.ts +78 -0
  60. package/dist/types/sanitize-text.d.ts +29 -0
  61. package/dist/types/snowflake.d.ts +25 -0
  62. package/dist/types/stderr-guard.d.ts +22 -0
  63. package/dist/types/stream.d.ts +67 -0
  64. package/dist/types/tab-spacing.d.ts +24 -0
  65. package/dist/types/temp.d.ts +17 -0
  66. package/dist/types/template.d.ts +62 -0
  67. package/dist/types/timing-buffer.d.ts +22 -0
  68. package/dist/types/tls-fetch.d.ts +37 -0
  69. package/dist/types/turndown/gfm.d.ts +11 -0
  70. package/dist/types/turndown/html.d.ts +5 -0
  71. package/dist/types/turndown/service.d.ts +21 -0
  72. package/dist/types/turndown/types.d.ts +70 -0
  73. package/dist/types/turndown.d.ts +4 -0
  74. package/dist/types/type-guards.d.ts +5 -0
  75. package/dist/types/vendor/mermaid-ascii/ascii/ansi.d.ts +41 -0
  76. package/dist/types/vendor/mermaid-ascii/ascii/canvas.d.ts +89 -0
  77. package/dist/types/vendor/mermaid-ascii/ascii/class-diagram.d.ts +7 -0
  78. package/dist/types/vendor/mermaid-ascii/ascii/converter.d.ts +12 -0
  79. package/dist/types/vendor/mermaid-ascii/ascii/draw.d.ts +66 -0
  80. package/dist/types/vendor/mermaid-ascii/ascii/edge-bundling.d.ts +48 -0
  81. package/dist/types/vendor/mermaid-ascii/ascii/edge-routing.d.ts +43 -0
  82. package/dist/types/vendor/mermaid-ascii/ascii/er-diagram.d.ts +7 -0
  83. package/dist/types/vendor/mermaid-ascii/ascii/grid.d.ts +56 -0
  84. package/dist/types/vendor/mermaid-ascii/ascii/index.d.ts +65 -0
  85. package/dist/types/vendor/mermaid-ascii/ascii/multiline-utils.d.ts +27 -0
  86. package/dist/types/vendor/mermaid-ascii/ascii/pathfinder.d.ts +17 -0
  87. package/dist/types/vendor/mermaid-ascii/ascii/sequence.d.ts +7 -0
  88. package/dist/types/vendor/mermaid-ascii/ascii/shapes/circle.d.ts +11 -0
  89. package/dist/types/vendor/mermaid-ascii/ascii/shapes/corners.d.ts +34 -0
  90. package/dist/types/vendor/mermaid-ascii/ascii/shapes/diamond.d.ts +11 -0
  91. package/dist/types/vendor/mermaid-ascii/ascii/shapes/hexagon.d.ts +11 -0
  92. package/dist/types/vendor/mermaid-ascii/ascii/shapes/index.d.ts +26 -0
  93. package/dist/types/vendor/mermaid-ascii/ascii/shapes/rectangle.d.ts +31 -0
  94. package/dist/types/vendor/mermaid-ascii/ascii/shapes/rounded.d.ts +11 -0
  95. package/dist/types/vendor/mermaid-ascii/ascii/shapes/special.d.ts +59 -0
  96. package/dist/types/vendor/mermaid-ascii/ascii/shapes/stadium.d.ts +17 -0
  97. package/dist/types/vendor/mermaid-ascii/ascii/shapes/state.d.ts +30 -0
  98. package/dist/types/vendor/mermaid-ascii/ascii/shapes/types.d.ts +55 -0
  99. package/dist/types/vendor/mermaid-ascii/ascii/types.d.ts +206 -0
  100. package/dist/types/vendor/mermaid-ascii/ascii/validate.d.ts +51 -0
  101. package/dist/types/vendor/mermaid-ascii/ascii/xychart.d.ts +2 -0
  102. package/dist/types/vendor/mermaid-ascii/class/parser.d.ts +6 -0
  103. package/dist/types/vendor/mermaid-ascii/class/types.d.ts +102 -0
  104. package/dist/types/vendor/mermaid-ascii/er/parser.d.ts +6 -0
  105. package/dist/types/vendor/mermaid-ascii/er/types.d.ts +76 -0
  106. package/dist/types/vendor/mermaid-ascii/index.d.ts +1 -0
  107. package/dist/types/vendor/mermaid-ascii/multiline-utils.d.ts +9 -0
  108. package/dist/types/vendor/mermaid-ascii/parser.d.ts +7 -0
  109. package/dist/types/vendor/mermaid-ascii/sequence/parser.d.ts +6 -0
  110. package/dist/types/vendor/mermaid-ascii/sequence/types.d.ts +130 -0
  111. package/dist/types/vendor/mermaid-ascii/text-metrics.d.ts +21 -0
  112. package/dist/types/vendor/mermaid-ascii/types.d.ts +114 -0
  113. package/dist/types/vendor/mermaid-ascii/xychart/colors.d.ts +25 -0
  114. package/dist/types/vendor/mermaid-ascii/xychart/parser.d.ts +6 -0
  115. package/dist/types/vendor/mermaid-ascii/xychart/types.d.ts +145 -0
  116. package/dist/types/version.d.ts +18 -0
  117. package/dist/types/vterm/buffer.d.ts +99 -0
  118. package/dist/types/vterm/terminal.d.ts +44 -0
  119. package/dist/types/vterm.d.ts +8 -0
  120. package/dist/types/which.d.ts +37 -0
  121. package/dist/types/worker-host.d.ts +51 -0
  122. package/dist/types/xml.d.ts +31 -0
  123. package/package.json +59 -0
  124. package/src/abortable.ts +109 -0
  125. package/src/acp/connection.ts +344 -0
  126. package/src/acp/protocol.ts +466 -0
  127. package/src/acp/schema.ts +160 -0
  128. package/src/acp/stream.ts +82 -0
  129. package/src/acp/transport.ts +213 -0
  130. package/src/acp.ts +6 -0
  131. package/src/async.ts +91 -0
  132. package/src/binary.ts +50 -0
  133. package/src/browsers.ts +501 -0
  134. package/src/chalk.ts +312 -0
  135. package/src/cli.ts +529 -0
  136. package/src/color.ts +302 -0
  137. package/src/dates.ts +194 -0
  138. package/src/dirs.ts +1026 -0
  139. package/src/docx/converter.ts +681 -0
  140. package/src/docx/xml.ts +166 -0
  141. package/src/docx/zip.ts +87 -0
  142. package/src/docx.ts +20 -0
  143. package/src/dom/core.ts +1254 -0
  144. package/src/dom/parser.ts +370 -0
  145. package/src/dom/selector.ts +290 -0
  146. package/src/dom.ts +33 -0
  147. package/src/env.ts +400 -0
  148. package/src/fetch-retry.ts +379 -0
  149. package/src/file-lock.ts +69 -0
  150. package/src/format.ts +113 -0
  151. package/src/frontmatter.ts +205 -0
  152. package/src/fs-error.ts +56 -0
  153. package/src/glob.ts +189 -0
  154. package/src/headers.ts +167 -0
  155. package/src/index.ts +59 -0
  156. package/src/json-parse.ts +821 -0
  157. package/src/json.ts +23 -0
  158. package/src/logger/rotating-file.ts +149 -0
  159. package/src/logger.ts +798 -0
  160. package/src/loop-phase.ts +49 -0
  161. package/src/lru.ts +185 -0
  162. package/src/marked/core.ts +1576 -0
  163. package/src/marked.ts +2 -0
  164. package/src/mermaid-ascii.ts +31 -0
  165. package/src/mime.ts +159 -0
  166. package/src/module-timer.ts +148 -0
  167. package/src/path-tree.ts +147 -0
  168. package/src/path.ts +28 -0
  169. package/src/peek-file.ts +188 -0
  170. package/src/postmortem.ts +433 -0
  171. package/src/process-name.ts +57 -0
  172. package/src/procmgr.ts +247 -0
  173. package/src/prompt.ts +545 -0
  174. package/src/ptree.ts +412 -0
  175. package/src/readability/readability.ts +533 -0
  176. package/src/readability/readerable.ts +51 -0
  177. package/src/readability/types.ts +72 -0
  178. package/src/readability.ts +11 -0
  179. package/src/ring.ts +169 -0
  180. package/src/runtime-install.ts +393 -0
  181. package/src/sanitize-text.ts +96 -0
  182. package/src/snowflake.ts +121 -0
  183. package/src/stderr-guard.ts +155 -0
  184. package/src/stream.ts +454 -0
  185. package/src/tab-spacing.ts +336 -0
  186. package/src/temp.ts +134 -0
  187. package/src/template.ts +586 -0
  188. package/src/timing-buffer.ts +47 -0
  189. package/src/tls-fetch.ts +178 -0
  190. package/src/turndown/gfm.ts +106 -0
  191. package/src/turndown/html.ts +257 -0
  192. package/src/turndown/service.ts +334 -0
  193. package/src/turndown/types.ts +81 -0
  194. package/src/turndown.ts +5 -0
  195. package/src/type-guards.ts +17 -0
  196. package/src/vendor/mermaid-ascii/NOTICE +33 -0
  197. package/src/vendor/mermaid-ascii/ascii/ansi.ts +409 -0
  198. package/src/vendor/mermaid-ascii/ascii/canvas.ts +476 -0
  199. package/src/vendor/mermaid-ascii/ascii/class-diagram.ts +699 -0
  200. package/src/vendor/mermaid-ascii/ascii/converter.ts +271 -0
  201. package/src/vendor/mermaid-ascii/ascii/draw.ts +1382 -0
  202. package/src/vendor/mermaid-ascii/ascii/edge-bundling.ts +328 -0
  203. package/src/vendor/mermaid-ascii/ascii/edge-routing.ts +297 -0
  204. package/src/vendor/mermaid-ascii/ascii/er-diagram.ts +441 -0
  205. package/src/vendor/mermaid-ascii/ascii/grid.ts +578 -0
  206. package/src/vendor/mermaid-ascii/ascii/index.ts +187 -0
  207. package/src/vendor/mermaid-ascii/ascii/multiline-utils.ts +78 -0
  208. package/src/vendor/mermaid-ascii/ascii/pathfinder.ts +277 -0
  209. package/src/vendor/mermaid-ascii/ascii/sequence.ts +460 -0
  210. package/src/vendor/mermaid-ascii/ascii/shapes/circle.ts +27 -0
  211. package/src/vendor/mermaid-ascii/ascii/shapes/corners.ts +127 -0
  212. package/src/vendor/mermaid-ascii/ascii/shapes/diamond.ts +27 -0
  213. package/src/vendor/mermaid-ascii/ascii/shapes/hexagon.ts +27 -0
  214. package/src/vendor/mermaid-ascii/ascii/shapes/index.ts +101 -0
  215. package/src/vendor/mermaid-ascii/ascii/shapes/rectangle.ts +175 -0
  216. package/src/vendor/mermaid-ascii/ascii/shapes/rounded.ts +27 -0
  217. package/src/vendor/mermaid-ascii/ascii/shapes/special.ts +296 -0
  218. package/src/vendor/mermaid-ascii/ascii/shapes/stadium.ts +114 -0
  219. package/src/vendor/mermaid-ascii/ascii/shapes/state.ts +192 -0
  220. package/src/vendor/mermaid-ascii/ascii/shapes/types.ts +73 -0
  221. package/src/vendor/mermaid-ascii/ascii/types.ts +273 -0
  222. package/src/vendor/mermaid-ascii/ascii/validate.ts +120 -0
  223. package/src/vendor/mermaid-ascii/ascii/xychart.ts +875 -0
  224. package/src/vendor/mermaid-ascii/class/parser.ts +290 -0
  225. package/src/vendor/mermaid-ascii/class/types.ts +121 -0
  226. package/src/vendor/mermaid-ascii/er/parser.ts +181 -0
  227. package/src/vendor/mermaid-ascii/er/types.ts +91 -0
  228. package/src/vendor/mermaid-ascii/index.ts +14 -0
  229. package/src/vendor/mermaid-ascii/multiline-utils.ts +30 -0
  230. package/src/vendor/mermaid-ascii/parser.ts +645 -0
  231. package/src/vendor/mermaid-ascii/sequence/parser.ts +207 -0
  232. package/src/vendor/mermaid-ascii/sequence/types.ts +146 -0
  233. package/src/vendor/mermaid-ascii/text-metrics.ts +71 -0
  234. package/src/vendor/mermaid-ascii/types.ts +164 -0
  235. package/src/vendor/mermaid-ascii/xychart/colors.ts +140 -0
  236. package/src/vendor/mermaid-ascii/xychart/parser.ts +115 -0
  237. package/src/vendor/mermaid-ascii/xychart/types.ts +150 -0
  238. package/src/version.ts +99 -0
  239. package/src/vterm/buffer.ts +218 -0
  240. package/src/vterm/terminal.ts +773 -0
  241. package/src/vterm.ts +8 -0
  242. package/src/which.ts +234 -0
  243. package/src/worker-host.ts +100 -0
  244. package/src/xml.ts +313 -0
package/src/cli.ts ADDED
@@ -0,0 +1,529 @@
1
+ /**
2
+ * Minimal CLI framework — drop-in replacement for the subset of @oclif/core
3
+ * actually used by the coding agent. Provides `Command`, `Args`, `Flags`,
4
+ * and a `run()` entry point with explicit command registration.
5
+ *
6
+ * Design goals:
7
+ * - Zero dependencies beyond node builtins
8
+ * - No filesystem scanning, no manifest files, no plugin loading
9
+ * - Lazy command imports (only the invoked command is loaded)
10
+ * - Typed `this.parse()` output matching oclif's API shape
11
+ */
12
+ import * as fs from "node:fs";
13
+ import { parseArgs as nodeParseArgs } from "node:util";
14
+
15
+ /**
16
+ * Streaming startup marker, enabled by `PI_DEBUG_STARTUP`. Local copy of
17
+ * `logger.startupMarker` so the minimal `--version`/bootstrap import graph
18
+ * stays free of the winston-backed logger module. Synchronous on purpose:
19
+ * a command module whose import hangs (dlopen, fs on a dead mount) must
20
+ * still leave its `:start` marker behind.
21
+ */
22
+ function startupMarker(text: string): void {
23
+ if (!process.env.PI_DEBUG_STARTUP) return;
24
+ try {
25
+ fs.writeSync(2, `[startup] ${text}\n`);
26
+ } catch {
27
+ // stderr unavailable; markers are best-effort
28
+ }
29
+ }
30
+
31
+ /**
32
+ * A user-facing argument/flag validation failure. Thrown by {@link Command.parse}
33
+ * for missing/invalid positionals and flags. The top-level {@link run} handler
34
+ * prints its message plus the command usage line to stderr and exits 1, instead
35
+ * of letting it bubble to the process-level catch — which would dump a minified
36
+ * `dist/cli.js` code frame over a plain argument mistake (issue #5369).
37
+ */
38
+ export class CliUsageError extends Error {
39
+ constructor(message: string) {
40
+ super(message);
41
+ this.name = "CliUsageError";
42
+ }
43
+ }
44
+
45
+ // ---------------------------------------------------------------------------
46
+ // Flag & Arg descriptors
47
+ // ---------------------------------------------------------------------------
48
+
49
+ export interface FlagDescriptor<K extends "string" | "boolean" | "integer" = "string" | "boolean" | "integer"> {
50
+ kind: K;
51
+ description?: string;
52
+ char?: string;
53
+ default?: unknown;
54
+ multiple?: boolean;
55
+ options?: readonly string[];
56
+ required?: boolean;
57
+ }
58
+
59
+ export interface ArgDescriptor {
60
+ kind: "string";
61
+ description?: string;
62
+ required?: boolean;
63
+ multiple?: boolean;
64
+ options?: readonly string[];
65
+ }
66
+
67
+ interface FlagInput {
68
+ description?: string;
69
+ char?: string;
70
+ default?: unknown;
71
+ multiple?: boolean;
72
+ options?: readonly string[];
73
+ required?: boolean;
74
+ }
75
+
76
+ interface ArgInput {
77
+ description?: string;
78
+ required?: boolean;
79
+ multiple?: boolean;
80
+ options?: readonly string[];
81
+ }
82
+
83
+ /** Builders that match the `Flags.*()` / `Args.*()` API from oclif. */
84
+ export const Flags = {
85
+ string<T extends FlagInput>(opts?: T): FlagDescriptor<"string"> & T {
86
+ return { kind: "string" as const, ...opts } as FlagDescriptor<"string"> & T;
87
+ },
88
+ boolean<T extends FlagInput>(opts?: T): FlagDescriptor<"boolean"> & T {
89
+ return { kind: "boolean" as const, ...opts } as FlagDescriptor<"boolean"> & T;
90
+ },
91
+ integer<T extends FlagInput & { default?: number }>(opts?: T): FlagDescriptor<"integer"> & T {
92
+ return { kind: "integer" as const, ...opts } as FlagDescriptor<"integer"> & T;
93
+ },
94
+ };
95
+
96
+ export const Args = {
97
+ string<T extends ArgInput>(opts?: T): ArgDescriptor & T {
98
+ return { kind: "string" as const, ...opts } as ArgDescriptor & T;
99
+ },
100
+ };
101
+
102
+ // ---------------------------------------------------------------------------
103
+ // Parse result types — mirrors oclif's typed output from this.parse()
104
+ // ---------------------------------------------------------------------------
105
+
106
+ type FlagValue<D extends FlagDescriptor> = D["kind"] extends "boolean"
107
+ ? D extends { default: boolean }
108
+ ? boolean
109
+ : boolean | undefined
110
+ : D["kind"] extends "integer"
111
+ ? D extends { default: number }
112
+ ? number
113
+ : number | undefined
114
+ : D extends { multiple: true }
115
+ ? string[] | undefined
116
+ : string | undefined;
117
+
118
+ type ArgValue<D extends ArgDescriptor> = D extends { multiple: true } ? string[] | undefined : string | undefined;
119
+
120
+ type FlagValues<T extends Record<string, FlagDescriptor>> = { [K in keyof T]: FlagValue<T[K]> };
121
+ type ArgValues<T extends Record<string, ArgDescriptor>> = { [K in keyof T]: ArgValue<T[K]> };
122
+
123
+ export interface ParseOutput<
124
+ F extends Record<string, FlagDescriptor> = Record<string, FlagDescriptor>,
125
+ A extends Record<string, ArgDescriptor> = Record<string, ArgDescriptor>,
126
+ > {
127
+ flags: FlagValues<F>;
128
+ args: ArgValues<A>;
129
+ argv: string[];
130
+ }
131
+
132
+ // ---------------------------------------------------------------------------
133
+ // Command base class
134
+ // ---------------------------------------------------------------------------
135
+
136
+ export interface CommandMetadata {
137
+ description?: string;
138
+ hidden?: boolean;
139
+ flags?: Record<string, FlagDescriptor>;
140
+ args?: Record<string, ArgDescriptor>;
141
+ examples?: string[];
142
+ }
143
+
144
+ export interface CommandCtor extends CommandMetadata {
145
+ new (argv: string[], config: CliConfig): Command;
146
+ strict?: boolean;
147
+ aliases?: string[];
148
+ }
149
+
150
+ /** Configuration passed to every command instance and help renderers. */
151
+ export interface CliConfig<TCommand extends CommandMetadata = CommandCtor> {
152
+ bin: string;
153
+ version: string;
154
+ /** All registered commands keyed by their canonical name. */
155
+ commands: Map<string, TCommand>;
156
+ }
157
+
158
+ /** Minimal Command base matching the oclif surface we use. */
159
+ export abstract class Command {
160
+ argv: string[];
161
+ config: CliConfig;
162
+
163
+ constructor(argv: string[], config: CliConfig) {
164
+ this.argv = argv;
165
+ this.config = config;
166
+ }
167
+
168
+ abstract run(): Promise<void>;
169
+
170
+ /**
171
+ * Parse argv against the static `flags` and `args` declared on the
172
+ * concrete command class. Returns a typed `{ flags, args, argv }` object.
173
+ */
174
+ async parse<C extends CommandCtor>(
175
+ _Cmd: C,
176
+ ): Promise<
177
+ ParseOutput<
178
+ NonNullable<C["flags"]> extends Record<string, FlagDescriptor>
179
+ ? NonNullable<C["flags"]>
180
+ : Record<string, FlagDescriptor>,
181
+ NonNullable<C["args"]> extends Record<string, ArgDescriptor>
182
+ ? NonNullable<C["args"]>
183
+ : Record<string, ArgDescriptor>
184
+ >
185
+ > {
186
+ const Cmd = _Cmd as CommandCtor;
187
+ const flagDefs = (Cmd.flags ?? {}) as Record<string, FlagDescriptor>;
188
+ const argDefs = (Cmd.args ?? {}) as Record<string, ArgDescriptor>;
189
+ const strict = Cmd.strict !== false;
190
+
191
+ // Build node:util parseArgs options from flag descriptors
192
+ const options: Record<
193
+ string,
194
+ { type: "string" | "boolean"; short?: string; multiple?: boolean; default?: string | boolean }
195
+ > = {};
196
+ for (const [name, desc] of Object.entries(flagDefs)) {
197
+ const opt: (typeof options)[string] = {
198
+ type: desc.kind === "boolean" ? "boolean" : "string",
199
+ };
200
+ if (desc.char) opt.short = desc.char;
201
+ if (desc.multiple) opt.multiple = true;
202
+ if (desc.default !== undefined) {
203
+ opt.default = desc.kind === "boolean" ? Boolean(desc.default) : String(desc.default);
204
+ }
205
+ options[name] = opt;
206
+ }
207
+
208
+ // strict=false when command declares args (positionals must pass through)
209
+ // or when the command itself opts out
210
+ const { values: rawValues, positionals } = (() => {
211
+ try {
212
+ return nodeParseArgs({
213
+ args: this.argv,
214
+ options,
215
+ allowPositionals: true,
216
+ strict,
217
+ });
218
+ } catch (error) {
219
+ throw new CliUsageError(error instanceof Error ? error.message : String(error));
220
+ }
221
+ })();
222
+
223
+ // Convert raw values to proper types and validate
224
+ const flags: Record<string, unknown> = {};
225
+ for (const [name, desc] of Object.entries(flagDefs)) {
226
+ const raw = rawValues[name];
227
+ if (desc.kind === "integer") {
228
+ if (raw === undefined || typeof raw === "boolean") {
229
+ flags[name] = desc.default ?? undefined;
230
+ } else {
231
+ const n = Number.parseInt(raw as string, 10);
232
+ if (Number.isNaN(n)) {
233
+ throw new CliUsageError(`Expected integer for --${name}, got "${raw}"`);
234
+ }
235
+ flags[name] = n;
236
+ }
237
+ } else if (desc.kind === "boolean") {
238
+ flags[name] =
239
+ raw !== undefined ? Boolean(raw) : desc.default !== undefined ? Boolean(desc.default) : undefined;
240
+ } else {
241
+ // string
242
+ const val = raw !== undefined && typeof raw !== "boolean" ? raw : (desc.default ?? undefined);
243
+ // Validate options constraint
244
+ if (val !== undefined && desc.options && !Array.isArray(val)) {
245
+ if (!desc.options.includes(val as string)) {
246
+ throw new CliUsageError(
247
+ `Expected --${name} to be one of: ${[...desc.options].join(", ")}; got "${val}"`,
248
+ );
249
+ }
250
+ }
251
+ flags[name] = val;
252
+ }
253
+ // Validate required
254
+ if (desc.required && flags[name] === undefined) {
255
+ throw new CliUsageError(`Missing required flag: --${name}`);
256
+ }
257
+ }
258
+
259
+ // Map positionals to named args in declaration order and validate
260
+ const args: Record<string, unknown> = {};
261
+ let posIdx = 0;
262
+ for (const [argName, desc] of Object.entries(argDefs)) {
263
+ if (desc.multiple) {
264
+ const val = positionals.slice(posIdx);
265
+ args[argName] = val.length > 0 ? val : undefined;
266
+ posIdx = positionals.length;
267
+ } else {
268
+ const val = positionals[posIdx];
269
+ args[argName] = val;
270
+ posIdx++;
271
+ }
272
+ // Validate required
273
+ if (desc.required && args[argName] === undefined) {
274
+ throw new CliUsageError(`Missing required argument: ${argName}`);
275
+ }
276
+ // Validate options constraint
277
+ const argVal = args[argName];
278
+ if (argVal !== undefined && desc.options && typeof argVal === "string") {
279
+ if (!desc.options.includes(argVal)) {
280
+ throw new CliUsageError(
281
+ `Expected ${argName} to be one of: ${[...desc.options].join(", ")}; got "${argVal}"`,
282
+ );
283
+ }
284
+ }
285
+ }
286
+
287
+ return { flags, args, argv: positionals } as never;
288
+ }
289
+ }
290
+
291
+ // ---------------------------------------------------------------------------
292
+ // Help rendering
293
+ // ---------------------------------------------------------------------------
294
+
295
+ /** Render full root help: header, default command details, subcommand list. */
296
+ export function renderRootHelp(config: CliConfig<CommandMetadata>): void {
297
+ const { bin, version, commands } = config;
298
+ const lines: string[] = [];
299
+ lines.push(`${bin} v${version}\n`);
300
+ lines.push("USAGE");
301
+ lines.push(` $ ${bin} [COMMAND]\n`);
302
+
303
+ // Show the default command's flags/args/examples inline.
304
+ // The default command is the one marked hidden (it's the implicit entry point).
305
+ const defaultCmd = [...commands.values()].find(command => command.hidden);
306
+ if (defaultCmd) {
307
+ renderCommandBody(lines, defaultCmd);
308
+ }
309
+
310
+ // List visible subcommands
311
+ const visible = [...commands.entries()].filter(([, C]) => !C.hidden);
312
+ if (visible.length > 0) {
313
+ lines.push("COMMANDS");
314
+ const maxLen = Math.max(...visible.map(([n]) => n.length));
315
+ for (const [name, command] of visible.sort((a, b) => a[0].localeCompare(b[0]))) {
316
+ lines.push(` ${name.padEnd(maxLen + 2)}${command.description ?? ""}`);
317
+ }
318
+ lines.push("");
319
+ }
320
+
321
+ process.stdout.write(lines.join("\n"));
322
+ }
323
+
324
+ /**
325
+ * Format a command's positional args for a USAGE line. Required args render
326
+ * bare (`MODELS`), optional args wrapped in brackets (`[MODELS]`), and
327
+ * `multiple` args get a trailing ellipsis (`MODELS...`) so a required
328
+ * variadic reads as `MODELS...`, not the misleading optional `[MODELS]`.
329
+ */
330
+ function formatUsageArgs(Cmd: CommandCtor): string {
331
+ const entries = Object.entries(Cmd.args ?? {});
332
+ if (entries.length === 0) return "";
333
+ const parts = entries.map(([name, desc]) => {
334
+ const label = `${name.toUpperCase()}${desc.multiple ? "..." : ""}`;
335
+ return desc.required ? label : `[${label}]`;
336
+ });
337
+ return ` ${parts.join(" ")}`;
338
+ }
339
+
340
+ /** Build the single USAGE line for a command (without the leading label). */
341
+ export function commandUsageLine(bin: string, id: string, Cmd: CommandCtor): string {
342
+ const hasFlags = Object.keys(Cmd.flags ?? {}).length > 0;
343
+ return `$ ${bin} ${id}${formatUsageArgs(Cmd)}${hasFlags ? " [FLAGS]" : ""}`;
344
+ }
345
+
346
+ /** Render help for a single command. */
347
+ export function renderCommandHelp(bin: string, id: string, Cmd: CommandCtor): void {
348
+ const lines: string[] = [];
349
+ if (Cmd.description) lines.push(`${Cmd.description}\n`);
350
+ lines.push("USAGE");
351
+ lines.push(` ${commandUsageLine(bin, id, Cmd)}\n`);
352
+ renderCommandBody(lines, Cmd);
353
+ process.stdout.write(lines.join("\n"));
354
+ }
355
+
356
+ function renderCommandBody(lines: string[], command: CommandMetadata): void {
357
+ const argDefs = command.args ?? {};
358
+ const flagDefs = command.flags ?? {};
359
+
360
+ // Arguments
361
+ const argEntries = Object.entries(argDefs);
362
+ if (argEntries.length > 0) {
363
+ lines.push("ARGUMENTS");
364
+ const maxLen = Math.max(...argEntries.map(([n]) => n.length));
365
+ for (const [name, desc] of argEntries) {
366
+ const parts = [name.toUpperCase().padEnd(maxLen + 2)];
367
+ if (desc.description) parts.push(desc.description);
368
+ if (desc.options) parts.push(`(${[...desc.options].join("|")})`);
369
+ lines.push(` ${parts.join(" ")}`);
370
+ }
371
+ lines.push("");
372
+ }
373
+
374
+ // Flags
375
+ const flagEntries = Object.entries(flagDefs);
376
+ if (flagEntries.length > 0) {
377
+ lines.push("FLAGS");
378
+ const formatted: [string, string][] = [];
379
+ for (const [name, desc] of flagEntries) {
380
+ const charPart = desc.char ? `-${desc.char}, ` : " ";
381
+ const namePart = `--${name}`;
382
+ const typePart = desc.kind === "boolean" ? "" : desc.kind === "integer" ? "=<int>" : "=<value>";
383
+ formatted.push([` ${charPart}${namePart}${typePart}`, desc.description ?? ""]);
384
+ }
385
+ const maxLeft = Math.max(...formatted.map(([l]) => l.length));
386
+ for (const [left, right] of formatted) {
387
+ lines.push(`${left.padEnd(maxLeft + 2)}${right}`);
388
+ }
389
+ lines.push("");
390
+ }
391
+
392
+ // Examples
393
+ if (command.examples && command.examples.length > 0) {
394
+ lines.push("EXAMPLES");
395
+ for (const ex of command.examples) {
396
+ for (const line of ex.split("\n")) {
397
+ lines.push(` ${line}`);
398
+ }
399
+ }
400
+ lines.push("");
401
+ }
402
+ }
403
+
404
+ // ---------------------------------------------------------------------------
405
+ // CLI entry point
406
+ // ---------------------------------------------------------------------------
407
+
408
+ /** A lazily-loaded command: canonical name, loader, and optional aliases. */
409
+ export interface CommandEntry {
410
+ name: string;
411
+ load: () => Promise<CommandCtor>;
412
+ help?: CommandMetadata;
413
+ aliases?: string[];
414
+ }
415
+
416
+ export interface RunOptions {
417
+ bin: string;
418
+ version: string;
419
+ argv: string[];
420
+ commands: CommandEntry[];
421
+ /** Custom help renderer with the fully loaded command constructors. */
422
+ help?: (config: CliConfig) => Promise<void> | void;
423
+ /** Lightweight help renderer backed by static command metadata. */
424
+ metadataHelp?: (config: CliConfig<CommandMetadata>) => Promise<void> | void;
425
+ }
426
+
427
+ /** Find a command entry by exact name or alias. */
428
+ function findEntry(commands: CommandEntry[], id: string): CommandEntry | undefined {
429
+ return commands.find(e => e.name === id) ?? commands.find(e => e.aliases?.includes(id));
430
+ }
431
+
432
+ /**
433
+ * Main entry point — replaces `run()` from @oclif/core.
434
+ *
435
+ * Each command is explicitly registered with a lazy loader.
436
+ * No filesystem scanning, no plugin system, no package.json reading.
437
+ */
438
+ export async function run(opts: RunOptions): Promise<void> {
439
+ const { bin, version, argv } = opts;
440
+
441
+ const commandId = argv[0] ?? "";
442
+ const commandArgv = argv.slice(1);
443
+
444
+ // Top-level help
445
+ if (commandId === "--help" || commandId === "-h" || commandId === "help" || commandId === "") {
446
+ if (opts.help) {
447
+ await opts.help(await loadAllCommands(opts));
448
+ } else {
449
+ const config = await loadAllCommandMetadata(opts);
450
+ if (opts.metadataHelp) {
451
+ await opts.metadataHelp(config);
452
+ } else {
453
+ renderRootHelp(config);
454
+ }
455
+ }
456
+ return;
457
+ }
458
+
459
+ // Version
460
+ if (commandId === "--version" || commandId === "-v") {
461
+ process.stdout.write(`${bin}/${version}\n`);
462
+ return;
463
+ }
464
+
465
+ // Per-command help: load only the requested command. Loading the full
466
+ // command table here would make `omp <cmd> --help` hang or crash whenever
467
+ // any *unrelated* command module misbehaves at import time.
468
+ if (commandArgv.includes("--help") || commandArgv.includes("-h")) {
469
+ const entry = findEntry(opts.commands, commandId);
470
+ if (entry) {
471
+ const Cmd = await loadEntry(entry);
472
+ renderCommandHelp(bin, entry.name, Cmd);
473
+ } else {
474
+ process.stderr.write(`Unknown command: ${commandId}\n`);
475
+ }
476
+ return;
477
+ }
478
+
479
+ // Find command by name or alias
480
+ const entry = findEntry(opts.commands, commandId);
481
+
482
+ if (!entry) {
483
+ process.stderr.write(`Error: command ${commandId} not found\n`);
484
+ process.exitCode = 1;
485
+ return;
486
+ }
487
+
488
+ const Cmd = await loadEntry(entry);
489
+ const config: CliConfig = { bin, version, commands: new Map([[entry.name, Cmd]]) };
490
+ const instance = new Cmd(commandArgv, config);
491
+ try {
492
+ await instance.run();
493
+ } catch (error) {
494
+ // A usage mistake (missing/invalid arg or flag) is not a crash: print the
495
+ // message and the command's usage line, then exit 1. Letting it reach the
496
+ // process-level catch would dump a minified `dist/cli.js` code frame over a
497
+ // plain argument error (issue #5369).
498
+ if (error instanceof CliUsageError) {
499
+ process.stderr.write(`error: ${error.message}\n\n`);
500
+ process.stderr.write(`USAGE\n ${commandUsageLine(bin, entry.name, Cmd)}\n`);
501
+ process.stderr.write(`\nRun \`${bin} ${entry.name} --help\` for details.\n`);
502
+ process.exitCode = 1;
503
+ return;
504
+ }
505
+ throw error;
506
+ }
507
+ }
508
+
509
+ /** Load one command module, leaving streaming markers around the import. */
510
+ async function loadEntry(entry: CommandEntry): Promise<CommandCtor> {
511
+ startupMarker(`cli:load:${entry.name}:start`);
512
+ const Cmd = await entry.load();
513
+ startupMarker(`cli:load:${entry.name}:done`);
514
+ return Cmd;
515
+ }
516
+
517
+ /** Load every command constructor for backward-compatible custom help callbacks. */
518
+ async function loadAllCommands(opts: RunOptions): Promise<CliConfig> {
519
+ const loaded = await Promise.all(opts.commands.map(async entry => [entry.name, await loadEntry(entry)] as const));
520
+ return { bin: opts.bin, version: opts.version, commands: new Map(loaded) };
521
+ }
522
+
523
+ /** Resolve static command metadata for lightweight root help. */
524
+ async function loadAllCommandMetadata(opts: RunOptions): Promise<CliConfig<CommandMetadata>> {
525
+ const loaded = await Promise.all(
526
+ opts.commands.map(async entry => [entry.name, entry.help ?? (await loadEntry(entry))] as const),
527
+ );
528
+ return { bin: opts.bin, version: opts.version, commands: new Map(loaded) };
529
+ }