@wundr.io/cli 1.0.12 → 1.0.14

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 (233) hide show
  1. package/dist/ai/ai-service.d.ts +152 -0
  2. package/dist/ai/ai-service.d.ts.map +1 -0
  3. package/dist/ai/ai-service.js +430 -0
  4. package/dist/ai/ai-service.js.map +1 -0
  5. package/dist/ai/claude-client.d.ts +130 -0
  6. package/dist/ai/claude-client.d.ts.map +1 -0
  7. package/dist/ai/claude-client.js +340 -0
  8. package/dist/ai/claude-client.js.map +1 -0
  9. package/dist/ai/conversation-manager.d.ts +164 -0
  10. package/dist/ai/conversation-manager.d.ts.map +1 -0
  11. package/dist/ai/conversation-manager.js +614 -0
  12. package/dist/ai/conversation-manager.js.map +1 -0
  13. package/dist/ai/index.d.ts +5 -0
  14. package/dist/ai/index.d.ts.map +1 -0
  15. package/dist/ai/index.js +8 -0
  16. package/dist/ai/index.js.map +1 -0
  17. package/dist/cli.d.ts +36 -0
  18. package/dist/cli.d.ts.map +1 -0
  19. package/dist/cli.js +192 -0
  20. package/dist/cli.js.map +1 -0
  21. package/dist/commands/ai.d.ts +89 -0
  22. package/dist/commands/ai.d.ts.map +1 -0
  23. package/dist/commands/ai.js +954 -0
  24. package/dist/commands/ai.js.map +1 -0
  25. package/dist/commands/alignment.d.ts +78 -0
  26. package/dist/commands/alignment.d.ts.map +1 -0
  27. package/dist/commands/alignment.js +817 -0
  28. package/dist/commands/alignment.js.map +1 -0
  29. package/dist/commands/analyze-optimized.d.ts +14 -0
  30. package/dist/commands/analyze-optimized.d.ts.map +1 -0
  31. package/dist/commands/analyze-optimized.js +609 -0
  32. package/dist/commands/analyze-optimized.js.map +1 -0
  33. package/dist/commands/analyze.d.ts +65 -0
  34. package/dist/commands/analyze.d.ts.map +1 -0
  35. package/dist/commands/analyze.js +435 -0
  36. package/dist/commands/analyze.js.map +1 -0
  37. package/dist/commands/batch.d.ts +93 -0
  38. package/dist/commands/batch.d.ts.map +1 -0
  39. package/dist/commands/batch.js +854 -0
  40. package/dist/commands/batch.js.map +1 -0
  41. package/dist/commands/chat.d.ts +72 -0
  42. package/dist/commands/chat.d.ts.map +1 -0
  43. package/dist/commands/chat.js +678 -0
  44. package/dist/commands/chat.js.map +1 -0
  45. package/dist/commands/claude-init.d.ts +28 -0
  46. package/dist/commands/claude-init.d.ts.map +1 -0
  47. package/dist/commands/claude-init.js +591 -0
  48. package/dist/commands/claude-init.js.map +1 -0
  49. package/dist/commands/claude-setup.d.ts +119 -0
  50. package/dist/commands/claude-setup.d.ts.map +1 -0
  51. package/dist/commands/claude-setup.js +1079 -0
  52. package/dist/commands/claude-setup.js.map +1 -0
  53. package/dist/commands/computer-setup.d.ts +8 -0
  54. package/dist/commands/computer-setup.d.ts.map +1 -0
  55. package/dist/commands/computer-setup.js +877 -0
  56. package/dist/commands/computer-setup.js.map +1 -0
  57. package/dist/commands/create-command.d.ts +7 -0
  58. package/dist/commands/create-command.d.ts.map +1 -0
  59. package/dist/commands/create-command.js +158 -0
  60. package/dist/commands/create-command.js.map +1 -0
  61. package/dist/commands/create.d.ts +74 -0
  62. package/dist/commands/create.d.ts.map +1 -0
  63. package/dist/commands/create.js +556 -0
  64. package/dist/commands/create.js.map +1 -0
  65. package/dist/commands/dashboard.d.ts +91 -0
  66. package/dist/commands/dashboard.d.ts.map +1 -0
  67. package/dist/commands/dashboard.js +538 -0
  68. package/dist/commands/dashboard.js.map +1 -0
  69. package/dist/commands/govern.d.ts +70 -0
  70. package/dist/commands/govern.d.ts.map +1 -0
  71. package/dist/commands/govern.js +481 -0
  72. package/dist/commands/govern.js.map +1 -0
  73. package/dist/commands/governance.d.ts +17 -0
  74. package/dist/commands/governance.d.ts.map +1 -0
  75. package/dist/commands/governance.js +703 -0
  76. package/dist/commands/governance.js.map +1 -0
  77. package/dist/commands/guardian.d.ts +20 -0
  78. package/dist/commands/guardian.d.ts.map +1 -0
  79. package/dist/commands/guardian.js +597 -0
  80. package/dist/commands/guardian.js.map +1 -0
  81. package/dist/commands/init.d.ts +59 -0
  82. package/dist/commands/init.d.ts.map +1 -0
  83. package/dist/commands/init.js +650 -0
  84. package/dist/commands/init.js.map +1 -0
  85. package/dist/commands/orchestrator.d.ts +7 -0
  86. package/dist/commands/orchestrator.d.ts.map +1 -0
  87. package/dist/commands/orchestrator.js +578 -0
  88. package/dist/commands/orchestrator.js.map +1 -0
  89. package/dist/commands/performance-optimizer.d.ts +30 -0
  90. package/dist/commands/performance-optimizer.d.ts.map +1 -0
  91. package/dist/commands/performance-optimizer.js +650 -0
  92. package/dist/commands/performance-optimizer.js.map +1 -0
  93. package/dist/commands/plugins.d.ts +87 -0
  94. package/dist/commands/plugins.d.ts.map +1 -0
  95. package/dist/commands/plugins.js +685 -0
  96. package/dist/commands/plugins.js.map +1 -0
  97. package/dist/commands/rag.d.ts +7 -0
  98. package/dist/commands/rag.d.ts.map +1 -0
  99. package/dist/commands/rag.js +751 -0
  100. package/dist/commands/rag.js.map +1 -0
  101. package/dist/commands/session.d.ts +41 -0
  102. package/dist/commands/session.d.ts.map +1 -0
  103. package/dist/commands/session.js +441 -0
  104. package/dist/commands/session.js.map +1 -0
  105. package/dist/commands/setup.d.ts +24 -0
  106. package/dist/commands/setup.d.ts.map +1 -0
  107. package/dist/commands/setup.js +172 -0
  108. package/dist/commands/setup.js.map +1 -0
  109. package/dist/commands/test-init.d.ts +9 -0
  110. package/dist/commands/test-init.d.ts.map +1 -0
  111. package/dist/commands/test-init.js +222 -0
  112. package/dist/commands/test-init.js.map +1 -0
  113. package/dist/commands/test.d.ts +25 -0
  114. package/dist/commands/test.d.ts.map +1 -0
  115. package/dist/commands/test.js +217 -0
  116. package/dist/commands/test.js.map +1 -0
  117. package/dist/commands/watch.d.ts +76 -0
  118. package/dist/commands/watch.d.ts.map +1 -0
  119. package/dist/commands/watch.js +613 -0
  120. package/dist/commands/watch.js.map +1 -0
  121. package/dist/commands/worktree.d.ts +63 -0
  122. package/dist/commands/worktree.d.ts.map +1 -0
  123. package/dist/commands/worktree.js +774 -0
  124. package/dist/commands/worktree.js.map +1 -0
  125. package/dist/context/context-manager.d.ts +155 -0
  126. package/dist/context/context-manager.d.ts.map +1 -0
  127. package/dist/context/context-manager.js +383 -0
  128. package/dist/context/context-manager.js.map +1 -0
  129. package/dist/context/index.d.ts +3 -0
  130. package/dist/context/index.d.ts.map +1 -0
  131. package/dist/context/index.js +6 -0
  132. package/dist/context/index.js.map +1 -0
  133. package/dist/context/session-manager.d.ts +207 -0
  134. package/dist/context/session-manager.d.ts.map +1 -0
  135. package/dist/context/session-manager.js +686 -0
  136. package/dist/context/session-manager.js.map +1 -0
  137. package/dist/framework/command-interface.d.ts +349 -0
  138. package/dist/framework/command-interface.d.ts.map +1 -0
  139. package/dist/framework/command-interface.js +101 -0
  140. package/dist/framework/command-interface.js.map +1 -0
  141. package/dist/framework/command-registry.d.ts +173 -0
  142. package/dist/framework/command-registry.d.ts.map +1 -0
  143. package/dist/framework/command-registry.js +734 -0
  144. package/dist/framework/command-registry.js.map +1 -0
  145. package/dist/framework/completion-exporter.d.ts +79 -0
  146. package/dist/framework/completion-exporter.d.ts.map +1 -0
  147. package/dist/framework/completion-exporter.js +259 -0
  148. package/dist/framework/completion-exporter.js.map +1 -0
  149. package/dist/framework/debug-logger.d.ts +163 -0
  150. package/dist/framework/debug-logger.d.ts.map +1 -0
  151. package/dist/framework/debug-logger.js +373 -0
  152. package/dist/framework/debug-logger.js.map +1 -0
  153. package/dist/framework/error-handler.d.ts +196 -0
  154. package/dist/framework/error-handler.d.ts.map +1 -0
  155. package/dist/framework/error-handler.js +613 -0
  156. package/dist/framework/error-handler.js.map +1 -0
  157. package/dist/framework/help-generator.d.ts +78 -0
  158. package/dist/framework/help-generator.d.ts.map +1 -0
  159. package/dist/framework/help-generator.js +414 -0
  160. package/dist/framework/help-generator.js.map +1 -0
  161. package/dist/framework/index.d.ts +62 -0
  162. package/dist/framework/index.d.ts.map +1 -0
  163. package/dist/framework/index.js +95 -0
  164. package/dist/framework/index.js.map +1 -0
  165. package/dist/framework/interactive-repl.d.ts +138 -0
  166. package/dist/framework/interactive-repl.d.ts.map +1 -0
  167. package/dist/framework/interactive-repl.js +567 -0
  168. package/dist/framework/interactive-repl.js.map +1 -0
  169. package/dist/framework/output-formatter.d.ts +274 -0
  170. package/dist/framework/output-formatter.d.ts.map +1 -0
  171. package/dist/framework/output-formatter.js +545 -0
  172. package/dist/framework/output-formatter.js.map +1 -0
  173. package/dist/framework/progress-manager.d.ts +192 -0
  174. package/dist/framework/progress-manager.d.ts.map +1 -0
  175. package/dist/framework/progress-manager.js +408 -0
  176. package/dist/framework/progress-manager.js.map +1 -0
  177. package/dist/index.d.ts +8 -0
  178. package/dist/index.d.ts.map +1 -0
  179. package/dist/index.js +51 -0
  180. package/dist/index.js.map +1 -0
  181. package/dist/interactive/interactive-mode.d.ts +76 -0
  182. package/dist/interactive/interactive-mode.d.ts.map +1 -0
  183. package/dist/interactive/interactive-mode.js +732 -0
  184. package/dist/interactive/interactive-mode.js.map +1 -0
  185. package/dist/nlp/command-mapper.d.ts +174 -0
  186. package/dist/nlp/command-mapper.d.ts.map +1 -0
  187. package/dist/nlp/command-mapper.js +624 -0
  188. package/dist/nlp/command-mapper.js.map +1 -0
  189. package/dist/nlp/command-parser.d.ts +106 -0
  190. package/dist/nlp/command-parser.d.ts.map +1 -0
  191. package/dist/nlp/command-parser.js +417 -0
  192. package/dist/nlp/command-parser.js.map +1 -0
  193. package/dist/nlp/index.d.ts +5 -0
  194. package/dist/nlp/index.d.ts.map +1 -0
  195. package/dist/nlp/index.js +8 -0
  196. package/dist/nlp/index.js.map +1 -0
  197. package/dist/nlp/intent-classifier.d.ts +59 -0
  198. package/dist/nlp/intent-classifier.d.ts.map +1 -0
  199. package/dist/nlp/intent-classifier.js +384 -0
  200. package/dist/nlp/intent-classifier.js.map +1 -0
  201. package/dist/nlp/intent-parser.d.ts +152 -0
  202. package/dist/nlp/intent-parser.d.ts.map +1 -0
  203. package/dist/nlp/intent-parser.js +746 -0
  204. package/dist/nlp/intent-parser.js.map +1 -0
  205. package/dist/plugins/plugin-manager.d.ts +121 -0
  206. package/dist/plugins/plugin-manager.d.ts.map +1 -0
  207. package/dist/plugins/plugin-manager.js +606 -0
  208. package/dist/plugins/plugin-manager.js.map +1 -0
  209. package/dist/types/index.d.ts +224 -0
  210. package/dist/types/index.d.ts.map +1 -0
  211. package/dist/types/index.js +3 -0
  212. package/dist/types/index.js.map +1 -0
  213. package/dist/utils/backup-rollback-manager.d.ts +72 -0
  214. package/dist/utils/backup-rollback-manager.d.ts.map +1 -0
  215. package/dist/utils/backup-rollback-manager.js +288 -0
  216. package/dist/utils/backup-rollback-manager.js.map +1 -0
  217. package/dist/utils/claude-config-installer.d.ts +98 -0
  218. package/dist/utils/claude-config-installer.d.ts.map +1 -0
  219. package/dist/utils/claude-config-installer.js +678 -0
  220. package/dist/utils/claude-config-installer.js.map +1 -0
  221. package/dist/utils/config-manager.d.ts +73 -0
  222. package/dist/utils/config-manager.d.ts.map +1 -0
  223. package/dist/utils/config-manager.js +339 -0
  224. package/dist/utils/config-manager.js.map +1 -0
  225. package/dist/utils/error-handler.d.ts +46 -0
  226. package/dist/utils/error-handler.d.ts.map +1 -0
  227. package/dist/utils/error-handler.js +169 -0
  228. package/dist/utils/error-handler.js.map +1 -0
  229. package/dist/utils/logger.d.ts +25 -0
  230. package/dist/utils/logger.d.ts.map +1 -0
  231. package/dist/utils/logger.js +105 -0
  232. package/dist/utils/logger.js.map +1 -0
  233. package/package.json +6 -6
@@ -0,0 +1,734 @@
1
+ "use strict";
2
+ /**
3
+ * Command Registry - Auto-discovery, registration, and Commander.js integration.
4
+ *
5
+ * The registry is the central hub that:
6
+ * 1. Discovers CommandDefinition files from the commands/ directory
7
+ * 2. Validates definitions at registration time
8
+ * 3. Builds a Commander.js program from registered definitions
9
+ * 4. Provides lookup for shell completion generation
10
+ * 5. Supports legacy command wrapping for incremental migration
11
+ *
12
+ * @module framework/command-registry
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.CommandRegistry = void 0;
16
+ const tslib_1 = require("tslib");
17
+ const fs = tslib_1.__importStar(require("fs"));
18
+ const path = tslib_1.__importStar(require("path"));
19
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
20
+ const commander_1 = require("commander");
21
+ const command_interface_1 = require("./command-interface");
22
+ // ---------------------------------------------------------------------------
23
+ // Command Registry
24
+ // ---------------------------------------------------------------------------
25
+ class CommandRegistry {
26
+ commands = new Map();
27
+ globalHooks = [];
28
+ options;
29
+ constructor(options = {}) {
30
+ this.options = {
31
+ strict: options.strict ?? false,
32
+ commandsDir: options.commandsDir ?? path.join(__dirname, '..', 'commands'),
33
+ filePattern: options.filePattern ?? /\.(command)\.(ts|js)$/,
34
+ };
35
+ }
36
+ // -------------------------------------------------------------------------
37
+ // Registration
38
+ // -------------------------------------------------------------------------
39
+ /**
40
+ * Register a single command definition.
41
+ *
42
+ * @param definition - The command to register
43
+ * @param hooks - Optional lifecycle hooks for this command
44
+ * @throws Error in strict mode if command name is already registered
45
+ */
46
+ register(definition, hooks = []) {
47
+ const name = definition.name;
48
+ if (this.commands.has(name)) {
49
+ if (this.options.strict) {
50
+ throw new Error(`Command "${name}" is already registered. ` +
51
+ `Disable strict mode or use a different name.`);
52
+ }
53
+ // Non-strict: warn and overwrite
54
+ console.warn(chalk_1.default.yellow(`[registry] Overwriting existing command: ${name}`));
55
+ }
56
+ // Validate the definition at registration time
57
+ this.validateDefinition(definition);
58
+ this.commands.set(name, {
59
+ definition,
60
+ hooks,
61
+ registeredAt: new Date(),
62
+ });
63
+ // Recursively register subcommands
64
+ if (definition.subcommands) {
65
+ for (const sub of definition.subcommands) {
66
+ // Prefix subcommand names with parent for flat lookup
67
+ const qualifiedName = `${name}:${sub.name}`;
68
+ const qualifiedSub = { ...sub, name: qualifiedName };
69
+ this.register(qualifiedSub, hooks);
70
+ }
71
+ }
72
+ }
73
+ /**
74
+ * Register a command module (definition + hooks bundle).
75
+ */
76
+ registerModule(mod) {
77
+ this.register(mod.command, mod.hooks);
78
+ }
79
+ /**
80
+ * Register a global hook that runs for all commands.
81
+ */
82
+ registerGlobalHook(hook) {
83
+ this.globalHooks.push(hook);
84
+ }
85
+ /**
86
+ * Remove a registered command.
87
+ */
88
+ unregister(name) {
89
+ return this.commands.delete(name);
90
+ }
91
+ // -------------------------------------------------------------------------
92
+ // Auto-Discovery
93
+ // -------------------------------------------------------------------------
94
+ /**
95
+ * Discover and register commands from a directory.
96
+ *
97
+ * Scans the directory for files matching the configured pattern.
98
+ * Each file should export either:
99
+ * - A `module` property conforming to `CommandModule`
100
+ * - A `command` property conforming to `CommandDefinition`
101
+ * - A default export conforming to `CommandDefinition`
102
+ *
103
+ * @param directory - Directory to scan. Defaults to configured commandsDir.
104
+ * @returns Number of commands discovered and registered.
105
+ */
106
+ async discoverCommands(directory) {
107
+ const dir = directory ?? this.options.commandsDir;
108
+ let count = 0;
109
+ if (!fs.existsSync(dir)) {
110
+ console.warn(chalk_1.default.yellow(`[registry] Commands directory not found: ${dir}`));
111
+ return 0;
112
+ }
113
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
114
+ for (const entry of entries) {
115
+ if (!entry.isFile()) {
116
+ continue;
117
+ }
118
+ // Check file pattern
119
+ if (!this.options.filePattern.test(entry.name)) {
120
+ continue;
121
+ }
122
+ const filePath = path.join(dir, entry.name);
123
+ try {
124
+ const exported = await this.loadModule(filePath);
125
+ if (exported.module && typeof exported.module === 'object') {
126
+ // CommandModule export
127
+ const mod = exported.module;
128
+ if (mod.command &&
129
+ mod.command.name &&
130
+ typeof mod.command.execute === 'function') {
131
+ this.registerModule(mod);
132
+ count++;
133
+ }
134
+ }
135
+ else if (exported.command && typeof exported.command === 'object') {
136
+ // Direct CommandDefinition export
137
+ const def = exported.command;
138
+ if (def.name && typeof def.execute === 'function') {
139
+ this.register(def);
140
+ count++;
141
+ }
142
+ }
143
+ else if (exported.default && typeof exported.default === 'object') {
144
+ // Default export
145
+ const def = exported.default;
146
+ if (def.name && typeof def.execute === 'function') {
147
+ this.register(def);
148
+ count++;
149
+ }
150
+ }
151
+ }
152
+ catch (error) {
153
+ console.warn(chalk_1.default.yellow(`[registry] Failed to load command from ${entry.name}: ` +
154
+ `${error instanceof Error ? error.message : String(error)}`));
155
+ }
156
+ }
157
+ return count;
158
+ }
159
+ // -------------------------------------------------------------------------
160
+ // Multi-Directory Discovery
161
+ // -------------------------------------------------------------------------
162
+ /**
163
+ * Discover commands from multiple directories.
164
+ *
165
+ * @param directories - Array of directory paths to scan
166
+ * @returns Total number of commands discovered
167
+ */
168
+ async discoverFromDirectories(directories) {
169
+ let total = 0;
170
+ for (const dir of directories) {
171
+ total += await this.discoverCommands(dir);
172
+ }
173
+ return total;
174
+ }
175
+ // -------------------------------------------------------------------------
176
+ // Lookup
177
+ // -------------------------------------------------------------------------
178
+ /**
179
+ * Get a registered command by name.
180
+ */
181
+ get(name) {
182
+ return this.commands.get(name)?.definition;
183
+ }
184
+ /**
185
+ * Find a command by name or alias.
186
+ * Searches direct name first, then aliases.
187
+ */
188
+ findByNameOrAlias(nameOrAlias) {
189
+ // Direct name lookup
190
+ const direct = this.commands.get(nameOrAlias);
191
+ if (direct)
192
+ return direct.definition;
193
+ // Search aliases
194
+ for (const registered of Array.from(this.commands.values())) {
195
+ const { definition } = registered;
196
+ if (definition.aliases && definition.aliases.includes(nameOrAlias)) {
197
+ return definition;
198
+ }
199
+ }
200
+ return undefined;
201
+ }
202
+ /**
203
+ * Check if a command is registered.
204
+ */
205
+ has(name) {
206
+ return this.commands.has(name);
207
+ }
208
+ /**
209
+ * List all registered commands, optionally filtered by category.
210
+ */
211
+ list(category) {
212
+ const all = Array.from(this.commands.values()).map(r => r.definition);
213
+ if (category) {
214
+ return all.filter(cmd => cmd.category === category);
215
+ }
216
+ return all;
217
+ }
218
+ /**
219
+ * List all registered command names.
220
+ */
221
+ names() {
222
+ return Array.from(this.commands.keys());
223
+ }
224
+ /**
225
+ * Get commands grouped by category.
226
+ */
227
+ grouped() {
228
+ const groups = new Map();
229
+ for (const registered of Array.from(this.commands.values())) {
230
+ const cat = registered.definition.category ?? 'uncategorized';
231
+ const existing = groups.get(cat) ?? [];
232
+ existing.push(registered.definition);
233
+ groups.set(cat, existing);
234
+ }
235
+ return groups;
236
+ }
237
+ /**
238
+ * Get all command names for shell completion.
239
+ */
240
+ getCompletionWords() {
241
+ const words = [];
242
+ for (const registered of Array.from(this.commands.values())) {
243
+ if (registered.definition.hidden) {
244
+ continue;
245
+ }
246
+ words.push(registered.definition.name);
247
+ if (registered.definition.aliases) {
248
+ words.push(...registered.definition.aliases);
249
+ }
250
+ }
251
+ return words.sort();
252
+ }
253
+ // -------------------------------------------------------------------------
254
+ // Commander.js Integration
255
+ // -------------------------------------------------------------------------
256
+ /**
257
+ * Build a Commander.js program from all registered commands.
258
+ *
259
+ * This is the bridge between the registry's CommandDefinition world
260
+ * and Commander.js's imperative API. Call this once after all commands
261
+ * are registered.
262
+ *
263
+ * @param program - The root Commander.js Command instance
264
+ * @param contextFactory - Factory that creates a CommandContext for each invocation
265
+ */
266
+ buildProgram(program, contextFactory) {
267
+ for (const registered of Array.from(this.commands.values())) {
268
+ const { definition, hooks } = registered;
269
+ // Skip qualified subcommand names (they are handled by their parent)
270
+ if (definition.name.includes(':')) {
271
+ continue;
272
+ }
273
+ // Check for legacy factory
274
+ const legacyFactory = definition._legacyFactory;
275
+ if (legacyFactory) {
276
+ // Legacy command: add the pre-built Commander.Command directly
277
+ program.addCommand(legacyFactory());
278
+ continue;
279
+ }
280
+ // Build a new Commander.Command from the definition
281
+ const cmd = this.buildCommand(definition, hooks, contextFactory);
282
+ program.addCommand(cmd);
283
+ }
284
+ }
285
+ /**
286
+ * Build a single Commander.Command from a CommandDefinition.
287
+ */
288
+ buildCommand(definition, hooks, contextFactory) {
289
+ const cmd = new commander_1.Command(definition.name);
290
+ cmd.description(definition.description);
291
+ // Aliases
292
+ if (definition.aliases) {
293
+ for (const alias of definition.aliases) {
294
+ cmd.alias(alias);
295
+ }
296
+ }
297
+ // Hidden - Commander.js doesn't expose hideHelp, so we use the internal approach
298
+ if (definition.hidden) {
299
+ cmd._hidden = true;
300
+ }
301
+ // Arguments
302
+ if (definition.arguments) {
303
+ for (const arg of definition.arguments) {
304
+ const spec = arg.required
305
+ ? arg.variadic
306
+ ? `<${arg.name}...>`
307
+ : `<${arg.name}>`
308
+ : arg.variadic
309
+ ? `[${arg.name}...]`
310
+ : `[${arg.name}]`;
311
+ cmd.argument(spec, arg.description, arg.defaultValue);
312
+ }
313
+ }
314
+ // Options
315
+ if (definition.options) {
316
+ for (const opt of definition.options) {
317
+ if (opt.required) {
318
+ cmd.requiredOption(opt.flags, opt.description, opt.defaultValue);
319
+ }
320
+ else {
321
+ cmd.option(opt.flags, opt.description, opt.defaultValue);
322
+ }
323
+ if (opt.choices) {
324
+ // Commander.js doesn't have built-in choices on options;
325
+ // we handle this in validation instead.
326
+ }
327
+ }
328
+ }
329
+ // Examples in help text
330
+ if (definition.examples && definition.examples.length > 0) {
331
+ const examplesText = definition.examples
332
+ .map(ex => ` ${chalk_1.default.green(ex.command)} ${chalk_1.default.gray(ex.description)}`)
333
+ .join('\n');
334
+ cmd.addHelpText('after', `\n${chalk_1.default.gray('Examples:')}\n${examplesText}\n`);
335
+ }
336
+ // Subcommands
337
+ if (definition.subcommands) {
338
+ for (const sub of definition.subcommands) {
339
+ const subCmd = this.buildCommand(sub, hooks, contextFactory);
340
+ cmd.addCommand(subCmd);
341
+ }
342
+ }
343
+ // Action handler
344
+ cmd.action(async (...actionArgs) => {
345
+ // Commander passes positional args first, then options object, then the Command
346
+ const commanderCmd = actionArgs[actionArgs.length - 1];
347
+ const options = actionArgs[actionArgs.length - 2];
348
+ // Build positional args map
349
+ const args = {};
350
+ if (definition.arguments) {
351
+ for (let i = 0; i < definition.arguments.length; i++) {
352
+ const argDef = definition.arguments[i];
353
+ if (argDef) {
354
+ args[argDef.name] = actionArgs[i];
355
+ }
356
+ }
357
+ }
358
+ // Merge environment variable defaults into options
359
+ if (definition.options) {
360
+ for (const opt of definition.options) {
361
+ if (opt.envVar) {
362
+ const optName = this.flagsToOptionName(opt.flags);
363
+ if (options[optName] === undefined && process.env[opt.envVar]) {
364
+ options[optName] = process.env[opt.envVar];
365
+ }
366
+ }
367
+ }
368
+ }
369
+ // Create execution context
370
+ const rootOpts = commanderCmd.parent?.opts() ?? commanderCmd.opts();
371
+ const globalOpts = {
372
+ verbose: !!rootOpts['verbose'],
373
+ quiet: !!rootOpts['quiet'],
374
+ json: !!rootOpts['json'] || !!options['json'],
375
+ noColor: !!rootOpts['noColor'],
376
+ dryRun: !!rootOpts['dryRun'] || !!options['dryRun'],
377
+ config: rootOpts['config'],
378
+ };
379
+ const context = contextFactory(globalOpts);
380
+ try {
381
+ // Run pre-validate hooks
382
+ const allHooks = [...this.globalHooks, ...hooks];
383
+ await this.runHooks('preValidate', allHooks, definition, context);
384
+ // Validate
385
+ let validation = (0, command_interface_1.validationOk)();
386
+ if (definition.validate) {
387
+ validation = await definition.validate(args, options, context);
388
+ }
389
+ // Validate option choices
390
+ if (definition.options) {
391
+ for (const opt of definition.options) {
392
+ if (opt.choices) {
393
+ const optName = this.flagsToOptionName(opt.flags);
394
+ const value = options[optName];
395
+ if (value !== undefined && !opt.choices.includes(String(value))) {
396
+ validation = {
397
+ valid: false,
398
+ errors: [
399
+ ...validation.errors,
400
+ {
401
+ field: optName,
402
+ message: `Invalid value "${value}" for --${optName}. Allowed: ${opt.choices.join(', ')}`,
403
+ suggestion: `Use one of: ${opt.choices.join(', ')}`,
404
+ },
405
+ ],
406
+ };
407
+ }
408
+ }
409
+ }
410
+ }
411
+ // Validate option conflicts
412
+ if (definition.options) {
413
+ for (const opt of definition.options) {
414
+ if (opt.conflicts) {
415
+ const optName = this.flagsToOptionName(opt.flags);
416
+ if (options[optName] !== undefined) {
417
+ for (const conflictName of opt.conflicts) {
418
+ if (options[conflictName] !== undefined) {
419
+ validation = {
420
+ valid: false,
421
+ errors: [
422
+ ...validation.errors,
423
+ {
424
+ field: optName,
425
+ message: `Option --${optName} conflicts with --${conflictName}`,
426
+ suggestion: `Use either --${optName} or --${conflictName}, not both`,
427
+ },
428
+ ],
429
+ };
430
+ }
431
+ }
432
+ }
433
+ }
434
+ }
435
+ }
436
+ // Run post-validate hooks
437
+ await this.runHooks('postValidate', allHooks, definition, context);
438
+ if (!validation.valid) {
439
+ for (const err of validation.errors) {
440
+ console.error(chalk_1.default.red(`Validation error [${err.field}]: ${err.message}`));
441
+ if (err.suggestion) {
442
+ console.error(chalk_1.default.yellow(` Suggestion: ${err.suggestion}`));
443
+ }
444
+ }
445
+ process.exitCode = 1;
446
+ return;
447
+ }
448
+ // Run pre-execute hooks
449
+ await this.runHooks('preExecute', allHooks, definition, context);
450
+ // Execute
451
+ const result = await definition.execute(args, options, context);
452
+ // Run post-execute hooks
453
+ await this.runHooks('postExecute', allHooks, definition, context, result);
454
+ // Handle result
455
+ this.handleResult(result, context);
456
+ }
457
+ catch (error) {
458
+ // Attempt rollback
459
+ if (definition.rollback && error instanceof Error) {
460
+ try {
461
+ await definition.rollback(error, context);
462
+ }
463
+ catch (rollbackError) {
464
+ context.logger.error(`Rollback failed: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`);
465
+ }
466
+ }
467
+ // Format error output
468
+ if (error instanceof Error) {
469
+ if (globalOpts.verbose) {
470
+ console.error(chalk_1.default.red(`\nCommand "${definition.name}" failed:`));
471
+ console.error(chalk_1.default.red(error.message));
472
+ if (error.stack) {
473
+ console.error(chalk_1.default.gray(error.stack));
474
+ }
475
+ }
476
+ else {
477
+ console.error(chalk_1.default.red(`Error: ${error.message}`));
478
+ console.error(chalk_1.default.gray(`Run with --verbose for details`));
479
+ }
480
+ }
481
+ else {
482
+ console.error(chalk_1.default.red(`Error: ${String(error)}`));
483
+ }
484
+ process.exitCode = 1;
485
+ }
486
+ });
487
+ return cmd;
488
+ }
489
+ // -------------------------------------------------------------------------
490
+ // Shell Completion Generation
491
+ // -------------------------------------------------------------------------
492
+ /**
493
+ * Generate a bash completion script.
494
+ */
495
+ generateBashCompletion(programName = 'wundr') {
496
+ const commands = this.getCompletionWords();
497
+ return `
498
+ # Bash completion for ${programName}
499
+ # Generated by @wundr/cli framework
500
+
501
+ _${programName}_completions() {
502
+ local cur="\${COMP_WORDS[COMP_CWORD]}"
503
+ local commands="${commands.join(' ')}"
504
+ local global_opts="--verbose --quiet --json --no-color --dry-run --config --help --version"
505
+
506
+ if [[ \${COMP_CWORD} -eq 1 ]]; then
507
+ COMPREPLY=( $(compgen -W "\${commands} \${global_opts}" -- "\${cur}") )
508
+ else
509
+ local cmd="\${COMP_WORDS[1]}"
510
+ case "\${cmd}" in
511
+ ${this.generateBashCaseClauses()}
512
+ *)
513
+ COMPREPLY=( $(compgen -W "\${global_opts}" -- "\${cur}") )
514
+ ;;
515
+ esac
516
+ fi
517
+ }
518
+
519
+ complete -F _${programName}_completions ${programName}
520
+ `.trim();
521
+ }
522
+ /**
523
+ * Generate a zsh completion script.
524
+ */
525
+ generateZshCompletion(programName = 'wundr') {
526
+ const commands = this.list()
527
+ .filter(cmd => !cmd.hidden && !cmd.name.includes(':'))
528
+ .map(cmd => `'${cmd.name}:${cmd.description.replace(/'/g, '')}'`)
529
+ .join('\n ');
530
+ return `
531
+ #compdef ${programName}
532
+ # Zsh completion for ${programName}
533
+ # Generated by @wundr/cli framework
534
+
535
+ _${programName}() {
536
+ local -a commands
537
+ commands=(
538
+ ${commands}
539
+ )
540
+
541
+ _arguments -C \\
542
+ '--verbose[Enable verbose logging]' \\
543
+ '--quiet[Suppress output]' \\
544
+ '--json[Output as JSON]' \\
545
+ '--no-color[Disable colored output]' \\
546
+ '--dry-run[Show what would be done]' \\
547
+ '--config[Specify config file]:file:_files' \\
548
+ '-h[Show help]' \\
549
+ '-v[Show version]' \\
550
+ '1:command:->command' \\
551
+ '*::arg:->args'
552
+
553
+ case $state in
554
+ command)
555
+ _describe -t commands 'command' commands
556
+ ;;
557
+ args)
558
+ case $words[1] in
559
+ ${this.generateZshCaseClauses()}
560
+ esac
561
+ ;;
562
+ esac
563
+ }
564
+
565
+ _${programName}
566
+ `.trim();
567
+ }
568
+ // -------------------------------------------------------------------------
569
+ // Private Helpers
570
+ // -------------------------------------------------------------------------
571
+ /**
572
+ * Load a module from a file path. Handles both ESM and CJS.
573
+ */
574
+ async loadModule(filePath) {
575
+ try {
576
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
577
+ return require(filePath);
578
+ }
579
+ catch {
580
+ // Fall back to dynamic import for ESM
581
+ return await Promise.resolve(`${filePath}`).then(s => tslib_1.__importStar(require(s)));
582
+ }
583
+ }
584
+ /**
585
+ * Validate a command definition at registration time.
586
+ */
587
+ validateDefinition(definition) {
588
+ if (!definition.name || typeof definition.name !== 'string') {
589
+ throw new Error('Command definition must have a non-empty name');
590
+ }
591
+ if (!definition.description || typeof definition.description !== 'string') {
592
+ throw new Error(`Command "${definition.name}" must have a description`);
593
+ }
594
+ if (typeof definition.execute !== 'function') {
595
+ throw new Error(`Command "${definition.name}" must have an execute function`);
596
+ }
597
+ // Validate arguments don't have duplicates
598
+ if (definition.arguments) {
599
+ const names = new Set();
600
+ for (const arg of definition.arguments) {
601
+ if (names.has(arg.name)) {
602
+ throw new Error(`Command "${definition.name}" has duplicate argument name: ${arg.name}`);
603
+ }
604
+ names.add(arg.name);
605
+ }
606
+ }
607
+ }
608
+ /**
609
+ * Run hooks for a specific phase.
610
+ */
611
+ async runHooks(phase, hooks, command, context, result) {
612
+ const matching = hooks.filter(h => {
613
+ if (h.phase !== phase)
614
+ return false;
615
+ if (h.commands && !h.commands.includes(command.name))
616
+ return false;
617
+ return true;
618
+ });
619
+ for (const hook of matching) {
620
+ const shouldContinue = await hook.handler(command, context, result);
621
+ if (shouldContinue === false &&
622
+ (phase === 'preValidate' || phase === 'preExecute')) {
623
+ throw new Error(`Command "${command.name}" aborted by ${phase} hook`);
624
+ }
625
+ }
626
+ }
627
+ /**
628
+ * Handle a CommandResult by formatting output appropriately.
629
+ */
630
+ handleResult(result, context) {
631
+ if (result.exitCode !== 0) {
632
+ if (result.message) {
633
+ console.error(chalk_1.default.red(result.message));
634
+ }
635
+ process.exitCode = result.exitCode;
636
+ return;
637
+ }
638
+ // Warnings
639
+ if (result.warnings && result.warnings.length > 0) {
640
+ for (const warning of result.warnings) {
641
+ console.error(chalk_1.default.yellow(`Warning: ${warning}`));
642
+ }
643
+ }
644
+ // JSON mode: output data directly
645
+ if (context.globalOptions.json && result.data !== undefined) {
646
+ const output = typeof result.data === 'string'
647
+ ? result.data
648
+ : JSON.stringify(result.data, null, 2);
649
+ console.log(output);
650
+ return;
651
+ }
652
+ // Quiet mode: no message output
653
+ if (context.globalOptions.quiet) {
654
+ return;
655
+ }
656
+ // Normal mode: output message
657
+ if (result.message) {
658
+ console.log(result.message);
659
+ }
660
+ }
661
+ /**
662
+ * Convert Commander.js flag specification to a camelCase option name.
663
+ * e.g., '-p, --port <number>' -> 'port'
664
+ * e.g., '--dry-run' -> 'dryRun'
665
+ */
666
+ flagsToOptionName(flags) {
667
+ const match = flags.match(/--([a-z-]+)/);
668
+ if (!match || !match[1])
669
+ return flags;
670
+ return match[1].replace(/-([a-z])/g, (_, char) => char.toUpperCase());
671
+ }
672
+ /**
673
+ * Generate bash case clauses for subcommand completion.
674
+ */
675
+ generateBashCaseClauses() {
676
+ const clauses = [];
677
+ for (const registered of Array.from(this.commands.values())) {
678
+ const { definition } = registered;
679
+ if (definition.hidden || definition.name.includes(':'))
680
+ continue;
681
+ const subNames = [];
682
+ const optFlags = [];
683
+ if (definition.subcommands) {
684
+ for (const sub of definition.subcommands) {
685
+ subNames.push(sub.name);
686
+ }
687
+ }
688
+ if (definition.options) {
689
+ for (const opt of definition.options) {
690
+ const longMatch = opt.flags.match(/--[a-z-]+/);
691
+ if (longMatch)
692
+ optFlags.push(longMatch[0]);
693
+ }
694
+ }
695
+ const words = [...subNames, ...optFlags].join(' ');
696
+ if (words) {
697
+ clauses.push(` ${definition.name})\n COMPREPLY=( $(compgen -W "${words}" -- "\${cur}") )\n ;;`);
698
+ }
699
+ }
700
+ return clauses.join('\n');
701
+ }
702
+ /**
703
+ * Generate zsh case clauses for subcommand completion.
704
+ */
705
+ generateZshCaseClauses() {
706
+ const clauses = [];
707
+ for (const registered of Array.from(this.commands.values())) {
708
+ const { definition } = registered;
709
+ if (definition.hidden || definition.name.includes(':'))
710
+ continue;
711
+ const args = [];
712
+ if (definition.subcommands) {
713
+ const subs = definition.subcommands
714
+ .map(s => `'${s.name}:${s.description.replace(/'/g, '')}'`)
715
+ .join(' ');
716
+ args.push(`_values 'subcommand' ${subs}`);
717
+ }
718
+ if (definition.options) {
719
+ for (const opt of definition.options) {
720
+ const longMatch = opt.flags.match(/--([a-z-]+)/);
721
+ if (longMatch) {
722
+ args.push(`'--${longMatch[1]}[${opt.description.replace(/'/g, '')}]'`);
723
+ }
724
+ }
725
+ }
726
+ if (args.length > 0) {
727
+ clauses.push(` ${definition.name})\n _arguments ${args.join(' \\\n ')}\n ;;`);
728
+ }
729
+ }
730
+ return clauses.join('\n');
731
+ }
732
+ }
733
+ exports.CommandRegistry = CommandRegistry;
734
+ //# sourceMappingURL=command-registry.js.map