@wundr.io/cli 1.0.11 → 1.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/wundr.js +8 -4
- package/dist/ai/ai-service.d.ts.map +1 -1
- package/dist/ai/ai-service.js.map +1 -1
- package/dist/ai/claude-client.js.map +1 -1
- package/dist/ai/conversation-manager.js.map +1 -1
- package/dist/commands/ai.d.ts.map +1 -1
- package/dist/commands/ai.js +179 -24
- package/dist/commands/ai.js.map +1 -1
- package/dist/commands/analyze-optimized.d.ts.map +1 -1
- package/dist/commands/analyze-optimized.js +15 -6
- package/dist/commands/analyze-optimized.js.map +1 -1
- package/dist/commands/batch.d.ts +22 -0
- package/dist/commands/batch.d.ts.map +1 -1
- package/dist/commands/batch.js +130 -14
- package/dist/commands/batch.js.map +1 -1
- package/dist/commands/chat.d.ts +1 -0
- package/dist/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js +7 -3
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/claude-init.d.ts +1 -1
- package/dist/commands/claude-init.d.ts.map +1 -1
- package/dist/commands/claude-init.js +16 -16
- package/dist/commands/claude-init.js.map +1 -1
- package/dist/commands/claude-setup.d.ts +5 -5
- package/dist/commands/claude-setup.d.ts.map +1 -1
- package/dist/commands/claude-setup.js +65 -59
- package/dist/commands/claude-setup.js.map +1 -1
- package/dist/commands/computer-setup.d.ts +1 -0
- package/dist/commands/computer-setup.d.ts.map +1 -1
- package/dist/commands/computer-setup.js +35 -7
- package/dist/commands/computer-setup.js.map +1 -1
- package/dist/commands/dashboard.js.map +1 -1
- package/dist/commands/govern.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +3 -3
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/orchestrator.d.ts.map +1 -1
- package/dist/commands/orchestrator.js +11 -4
- package/dist/commands/orchestrator.js.map +1 -1
- package/dist/commands/performance-optimizer.d.ts.map +1 -1
- package/dist/commands/performance-optimizer.js.map +1 -1
- package/dist/commands/rag.d.ts.map +1 -1
- package/dist/commands/rag.js +9 -6
- package/dist/commands/rag.js.map +1 -1
- package/dist/commands/setup.d.ts +5 -10
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +35 -260
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/watch.d.ts.map +1 -1
- package/dist/commands/watch.js.map +1 -1
- package/dist/context/session-manager.js.map +1 -1
- package/dist/framework/command-interface.d.ts +349 -0
- package/dist/framework/command-interface.d.ts.map +1 -0
- package/dist/framework/command-interface.js +101 -0
- package/dist/framework/command-interface.js.map +1 -0
- package/dist/framework/command-registry.d.ts +173 -0
- package/dist/framework/command-registry.d.ts.map +1 -0
- package/dist/framework/command-registry.js +734 -0
- package/dist/framework/command-registry.js.map +1 -0
- package/dist/framework/completion-exporter.d.ts +79 -0
- package/dist/framework/completion-exporter.d.ts.map +1 -0
- package/dist/framework/completion-exporter.js +259 -0
- package/dist/framework/completion-exporter.js.map +1 -0
- package/dist/framework/debug-logger.d.ts +163 -0
- package/dist/framework/debug-logger.d.ts.map +1 -0
- package/dist/framework/debug-logger.js +373 -0
- package/dist/framework/debug-logger.js.map +1 -0
- package/dist/framework/error-handler.d.ts +196 -0
- package/dist/framework/error-handler.d.ts.map +1 -0
- package/dist/framework/error-handler.js +613 -0
- package/dist/framework/error-handler.js.map +1 -0
- package/dist/framework/help-generator.d.ts +78 -0
- package/dist/framework/help-generator.d.ts.map +1 -0
- package/dist/framework/help-generator.js +414 -0
- package/dist/framework/help-generator.js.map +1 -0
- package/dist/framework/index.d.ts +62 -0
- package/dist/framework/index.d.ts.map +1 -0
- package/dist/framework/index.js +95 -0
- package/dist/framework/index.js.map +1 -0
- package/dist/framework/interactive-repl.d.ts +138 -0
- package/dist/framework/interactive-repl.d.ts.map +1 -0
- package/dist/framework/interactive-repl.js +567 -0
- package/dist/framework/interactive-repl.js.map +1 -0
- package/dist/framework/output-formatter.d.ts +274 -0
- package/dist/framework/output-formatter.d.ts.map +1 -0
- package/dist/framework/output-formatter.js +545 -0
- package/dist/framework/output-formatter.js.map +1 -0
- package/dist/framework/progress-manager.d.ts +192 -0
- package/dist/framework/progress-manager.d.ts.map +1 -0
- package/dist/framework/progress-manager.js +408 -0
- package/dist/framework/progress-manager.js.map +1 -0
- package/dist/interactive/interactive-mode.js.map +1 -1
- package/dist/nlp/command-mapper.js.map +1 -1
- package/dist/nlp/command-parser.js.map +1 -1
- package/dist/nlp/intent-parser.d.ts.map +1 -1
- package/dist/nlp/intent-parser.js +4 -2
- package/dist/nlp/intent-parser.js.map +1 -1
- package/dist/plugins/plugin-manager.d.ts +2 -1
- package/dist/plugins/plugin-manager.d.ts.map +1 -1
- package/dist/plugins/plugin-manager.js +30 -19
- package/dist/plugins/plugin-manager.js.map +1 -1
- package/dist/utils/backup-rollback-manager.d.ts.map +1 -1
- package/dist/utils/backup-rollback-manager.js +1 -2
- package/dist/utils/backup-rollback-manager.js.map +1 -1
- package/dist/utils/logger.js.map +1 -1
- package/package.json +6 -6
- package/src/ai/ai-service.ts +16 -17
- package/src/ai/claude-client.ts +16 -16
- package/src/ai/conversation-manager.ts +29 -29
- package/src/cli.ts +4 -4
- package/src/commands/ai.ts +246 -78
- package/src/commands/alignment.ts +74 -74
- package/src/commands/analyze-optimized.ts +111 -78
- package/src/commands/analyze.ts +14 -14
- package/src/commands/batch.ts +179 -42
- package/src/commands/chat.ts +37 -30
- package/src/commands/claude-init.ts +41 -45
- package/src/commands/claude-setup.ts +204 -119
- package/src/commands/computer-setup.ts +85 -43
- package/src/commands/create-command.ts +4 -4
- package/src/commands/create.ts +27 -27
- package/src/commands/dashboard.ts +24 -24
- package/src/commands/govern.ts +25 -25
- package/src/commands/governance.ts +34 -34
- package/src/commands/guardian.ts +56 -56
- package/src/commands/init.ts +25 -22
- package/src/commands/orchestrator.ts +68 -41
- package/src/commands/performance-optimizer.ts +34 -35
- package/src/commands/plugins.ts +27 -27
- package/src/commands/project-update.ts +175 -72
- package/src/commands/rag.ts +185 -78
- package/src/commands/session.ts +35 -35
- package/src/commands/setup.ts +40 -344
- package/src/commands/test-init.ts +3 -3
- package/src/commands/test.ts +4 -4
- package/src/commands/watch.ts +28 -29
- package/src/commands/worktree.ts +49 -49
- package/src/context/context-manager.ts +10 -10
- package/src/context/session-manager.ts +41 -41
- package/src/framework/command-interface.ts +520 -0
- package/src/framework/command-registry.ts +942 -0
- package/src/framework/completion-exporter.ts +383 -0
- package/src/framework/debug-logger.ts +519 -0
- package/src/framework/error-handler.ts +867 -0
- package/src/framework/help-generator.ts +540 -0
- package/src/framework/index.ts +169 -0
- package/src/framework/interactive-repl.ts +703 -0
- package/src/framework/output-formatter.ts +834 -0
- package/src/framework/progress-manager.ts +539 -0
- package/src/index.ts +4 -4
- package/src/interactive/interactive-mode.ts +16 -16
- package/src/lib/conflict-resolution.ts +799 -9
- package/src/lib/merge-strategy.ts +529 -7
- package/src/lib/safety-mechanisms.ts +422 -18
- package/src/lib/state-detection.ts +1015 -13
- package/src/nlp/command-mapper.ts +29 -29
- package/src/nlp/command-parser.ts +17 -17
- package/src/nlp/intent-classifier.ts +7 -7
- package/src/nlp/intent-parser.ts +54 -52
- package/src/plugins/plugin-manager.ts +61 -39
- package/src/tests/computer-setup-integration.test.ts +46 -15
- package/src/types/modules.d.ts +424 -1
- package/src/utils/backup-rollback-manager.ts +11 -8
- package/src/utils/config-manager.ts +3 -3
- package/src/utils/error-handler.ts +2 -2
- package/src/utils/logger.ts +22 -22
- package/templates/batch/ci-cd.yaml +7 -7
- package/test-suites/api/health.spec.ts +20 -23
- package/test-suites/helpers/test-config.ts +14 -13
- package/test-suites/ui/accessibility.spec.ts +27 -22
- package/test-suites/ui/smoke.spec.ts +26 -21
- package/dist/commands/computer-setup-commands.d.ts +0 -53
- package/dist/commands/computer-setup-commands.d.ts.map +0 -1
- package/dist/commands/computer-setup-commands.js +0 -705
- package/dist/commands/computer-setup-commands.js.map +0 -1
- package/dist/commands/vp.d.ts +0 -7
- package/dist/commands/vp.d.ts.map +0 -1
- package/dist/commands/vp.js +0 -571
- package/dist/commands/vp.js.map +0 -1
- package/src/commands/computer-setup-commands.ts +0 -872
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Help Generator - Generates structured help text from command metadata.
|
|
3
|
+
*
|
|
4
|
+
* Produces:
|
|
5
|
+
* - Categorized command listings
|
|
6
|
+
* - Per-command detailed help with examples
|
|
7
|
+
* - Man-page style output for documentation
|
|
8
|
+
* - Markdown help for README generation
|
|
9
|
+
* - Search across all commands
|
|
10
|
+
*
|
|
11
|
+
* @module framework/help-generator
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import chalk from 'chalk';
|
|
15
|
+
|
|
16
|
+
import type { CommandDefinition, CommandCategory } from './command-interface';
|
|
17
|
+
import { CATEGORY_LABELS } from './command-interface';
|
|
18
|
+
import type { CommandRegistry } from './command-registry';
|
|
19
|
+
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Types
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Options for help text generation.
|
|
26
|
+
*/
|
|
27
|
+
export interface HelpGeneratorOptions {
|
|
28
|
+
/** Program name. Defaults to 'wundr'. */
|
|
29
|
+
programName?: string;
|
|
30
|
+
|
|
31
|
+
/** Maximum line width. Defaults to terminal width or 80. */
|
|
32
|
+
maxWidth?: number;
|
|
33
|
+
|
|
34
|
+
/** Whether to use colors. Defaults to TTY detection. */
|
|
35
|
+
color?: boolean;
|
|
36
|
+
|
|
37
|
+
/** Output format. */
|
|
38
|
+
format?: 'terminal' | 'markdown' | 'plain';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Result of a command search.
|
|
43
|
+
*/
|
|
44
|
+
export interface SearchResult {
|
|
45
|
+
command: CommandDefinition;
|
|
46
|
+
matchType: 'name' | 'alias' | 'description' | 'category';
|
|
47
|
+
score: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Help Generator
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
export class HelpGenerator {
|
|
55
|
+
private programName: string;
|
|
56
|
+
private maxWidth: number;
|
|
57
|
+
private useColor: boolean;
|
|
58
|
+
private format: 'terminal' | 'markdown' | 'plain';
|
|
59
|
+
|
|
60
|
+
constructor(
|
|
61
|
+
private registry: CommandRegistry,
|
|
62
|
+
options: HelpGeneratorOptions = {}
|
|
63
|
+
) {
|
|
64
|
+
this.programName = options.programName ?? 'wundr';
|
|
65
|
+
this.maxWidth = options.maxWidth ?? process.stdout.columns ?? 80;
|
|
66
|
+
this.useColor = options.color ?? process.stdout.isTTY === true;
|
|
67
|
+
this.format = options.format ?? 'terminal';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// -------------------------------------------------------------------------
|
|
71
|
+
// Main Help
|
|
72
|
+
// -------------------------------------------------------------------------
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Generate the main help text showing all commands grouped by category.
|
|
76
|
+
*/
|
|
77
|
+
generateMainHelp(): string {
|
|
78
|
+
if (this.format === 'markdown') {
|
|
79
|
+
return this.generateMarkdownMainHelp();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const lines: string[] = [];
|
|
83
|
+
|
|
84
|
+
// Header
|
|
85
|
+
lines.push('');
|
|
86
|
+
lines.push(this.heading(`${this.programName} - CLI Tool`));
|
|
87
|
+
lines.push('');
|
|
88
|
+
|
|
89
|
+
// Usage
|
|
90
|
+
lines.push(this.sectionTitle('Usage'));
|
|
91
|
+
lines.push(` ${this.programName} <command> [options]`);
|
|
92
|
+
lines.push('');
|
|
93
|
+
|
|
94
|
+
// Commands by category
|
|
95
|
+
const grouped = this.registry.grouped();
|
|
96
|
+
const categories = Array.from(grouped.keys()).sort();
|
|
97
|
+
|
|
98
|
+
for (const category of categories) {
|
|
99
|
+
const commands = grouped.get(category);
|
|
100
|
+
if (!commands || commands.length === 0) continue;
|
|
101
|
+
|
|
102
|
+
// Skip hidden commands
|
|
103
|
+
const visible = commands.filter(
|
|
104
|
+
cmd => !cmd.hidden && !cmd.name.includes(':')
|
|
105
|
+
);
|
|
106
|
+
if (visible.length === 0) continue;
|
|
107
|
+
|
|
108
|
+
const label =
|
|
109
|
+
category === 'uncategorized'
|
|
110
|
+
? 'Other Commands'
|
|
111
|
+
: (CATEGORY_LABELS[category as CommandCategory] ?? category);
|
|
112
|
+
|
|
113
|
+
lines.push(this.sectionTitle(label));
|
|
114
|
+
|
|
115
|
+
// Find max name width for alignment
|
|
116
|
+
const maxNameWidth = Math.max(
|
|
117
|
+
...visible.map(cmd => {
|
|
118
|
+
const aliases = cmd.aliases ? `, ${cmd.aliases.join(', ')}` : '';
|
|
119
|
+
return cmd.name.length + aliases.length;
|
|
120
|
+
})
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
for (const cmd of visible) {
|
|
124
|
+
const aliases = cmd.aliases ? `, ${cmd.aliases.join(', ')}` : '';
|
|
125
|
+
const nameCol = (cmd.name + aliases).padEnd(maxNameWidth + 2);
|
|
126
|
+
lines.push(` ${this.highlight(nameCol)} ${cmd.description}`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
lines.push('');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Global options
|
|
133
|
+
lines.push(this.sectionTitle('Global Options'));
|
|
134
|
+
lines.push(' --verbose Enable verbose logging');
|
|
135
|
+
lines.push(' --quiet Suppress output');
|
|
136
|
+
lines.push(' --json Output as JSON');
|
|
137
|
+
lines.push(' --no-color Disable colored output');
|
|
138
|
+
lines.push(' --dry-run Show what would be done');
|
|
139
|
+
lines.push(' --config <path> Specify config file');
|
|
140
|
+
lines.push(' -h, --help Show help');
|
|
141
|
+
lines.push(' -v, --version Show version');
|
|
142
|
+
lines.push('');
|
|
143
|
+
|
|
144
|
+
// Footer
|
|
145
|
+
lines.push(
|
|
146
|
+
this.dim(
|
|
147
|
+
`Run '${this.programName} <command> --help' for detailed help on a command.`
|
|
148
|
+
)
|
|
149
|
+
);
|
|
150
|
+
lines.push('');
|
|
151
|
+
|
|
152
|
+
return lines.join('\n');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// -------------------------------------------------------------------------
|
|
156
|
+
// Command Help
|
|
157
|
+
// -------------------------------------------------------------------------
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Generate detailed help for a specific command.
|
|
161
|
+
*/
|
|
162
|
+
generateCommandHelp(command: CommandDefinition): string {
|
|
163
|
+
if (this.format === 'markdown') {
|
|
164
|
+
return this.generateMarkdownCommandHelp(command);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const lines: string[] = [];
|
|
168
|
+
|
|
169
|
+
// Header
|
|
170
|
+
lines.push('');
|
|
171
|
+
lines.push(this.heading(command.name));
|
|
172
|
+
lines.push(` ${command.description}`);
|
|
173
|
+
lines.push('');
|
|
174
|
+
|
|
175
|
+
// Aliases
|
|
176
|
+
if (command.aliases && command.aliases.length > 0) {
|
|
177
|
+
lines.push(this.sectionTitle('Aliases'));
|
|
178
|
+
lines.push(` ${command.aliases.join(', ')}`);
|
|
179
|
+
lines.push('');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Usage
|
|
183
|
+
lines.push(this.sectionTitle('Usage'));
|
|
184
|
+
const usage = this.buildUsageLine(command);
|
|
185
|
+
lines.push(` ${usage}`);
|
|
186
|
+
lines.push('');
|
|
187
|
+
|
|
188
|
+
// Arguments
|
|
189
|
+
if (command.arguments && command.arguments.length > 0) {
|
|
190
|
+
lines.push(this.sectionTitle('Arguments'));
|
|
191
|
+
const maxArgWidth = Math.max(
|
|
192
|
+
...command.arguments.map(a => a.name.length)
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
for (const arg of command.arguments) {
|
|
196
|
+
const nameCol = arg.name.padEnd(maxArgWidth + 2);
|
|
197
|
+
const required = arg.required
|
|
198
|
+
? this.warn('(required)')
|
|
199
|
+
: this.dim('(optional)');
|
|
200
|
+
const defaultVal = arg.defaultValue
|
|
201
|
+
? this.dim(` [default: ${arg.defaultValue}]`)
|
|
202
|
+
: '';
|
|
203
|
+
lines.push(
|
|
204
|
+
` ${this.highlight(nameCol)} ${arg.description} ${required}${defaultVal}`
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
lines.push('');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Options
|
|
211
|
+
if (command.options && command.options.length > 0) {
|
|
212
|
+
lines.push(this.sectionTitle('Options'));
|
|
213
|
+
const maxFlagWidth = Math.max(
|
|
214
|
+
...command.options.map(o => o.flags.length)
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
for (const opt of command.options) {
|
|
218
|
+
const flagCol = opt.flags.padEnd(maxFlagWidth + 2);
|
|
219
|
+
const required = opt.required ? this.warn('(required)') : '';
|
|
220
|
+
const choices = opt.choices
|
|
221
|
+
? this.dim(` [${opt.choices.join('|')}]`)
|
|
222
|
+
: '';
|
|
223
|
+
const defaultVal =
|
|
224
|
+
opt.defaultValue !== undefined
|
|
225
|
+
? this.dim(` [default: ${opt.defaultValue}]`)
|
|
226
|
+
: '';
|
|
227
|
+
const envVar = opt.envVar ? this.dim(` [env: ${opt.envVar}]`) : '';
|
|
228
|
+
lines.push(
|
|
229
|
+
` ${this.highlight(flagCol)} ${opt.description}${required}${choices}${defaultVal}${envVar}`
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
lines.push('');
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Subcommands
|
|
236
|
+
if (command.subcommands && command.subcommands.length > 0) {
|
|
237
|
+
lines.push(this.sectionTitle('Subcommands'));
|
|
238
|
+
const maxSubWidth = Math.max(
|
|
239
|
+
...command.subcommands.map(s => s.name.length)
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
for (const sub of command.subcommands) {
|
|
243
|
+
const nameCol = sub.name.padEnd(maxSubWidth + 2);
|
|
244
|
+
lines.push(` ${this.highlight(nameCol)} ${sub.description}`);
|
|
245
|
+
}
|
|
246
|
+
lines.push('');
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Examples
|
|
250
|
+
if (command.examples && command.examples.length > 0) {
|
|
251
|
+
lines.push(this.sectionTitle('Examples'));
|
|
252
|
+
for (const ex of command.examples) {
|
|
253
|
+
lines.push(` ${this.highlight(`$ ${ex.command}`)}`);
|
|
254
|
+
lines.push(` ${this.dim(ex.description)}`);
|
|
255
|
+
lines.push('');
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return lines.join('\n');
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// -------------------------------------------------------------------------
|
|
263
|
+
// Search
|
|
264
|
+
// -------------------------------------------------------------------------
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Search for commands matching a query string.
|
|
268
|
+
*/
|
|
269
|
+
search(query: string): SearchResult[] {
|
|
270
|
+
const normalizedQuery = query.toLowerCase();
|
|
271
|
+
const results: SearchResult[] = [];
|
|
272
|
+
|
|
273
|
+
for (const command of this.registry.list()) {
|
|
274
|
+
if (command.hidden) continue;
|
|
275
|
+
|
|
276
|
+
let bestScore = 0;
|
|
277
|
+
let matchType: SearchResult['matchType'] = 'name';
|
|
278
|
+
|
|
279
|
+
// Name match
|
|
280
|
+
const nameScore = this.fuzzyScore(
|
|
281
|
+
normalizedQuery,
|
|
282
|
+
command.name.toLowerCase()
|
|
283
|
+
);
|
|
284
|
+
if (nameScore > bestScore) {
|
|
285
|
+
bestScore = nameScore;
|
|
286
|
+
matchType = 'name';
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Alias match
|
|
290
|
+
if (command.aliases) {
|
|
291
|
+
for (const alias of command.aliases) {
|
|
292
|
+
const aliasScore = this.fuzzyScore(
|
|
293
|
+
normalizedQuery,
|
|
294
|
+
alias.toLowerCase()
|
|
295
|
+
);
|
|
296
|
+
if (aliasScore > bestScore) {
|
|
297
|
+
bestScore = aliasScore;
|
|
298
|
+
matchType = 'alias';
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Description match
|
|
304
|
+
const descScore =
|
|
305
|
+
this.fuzzyScore(normalizedQuery, command.description.toLowerCase()) *
|
|
306
|
+
0.6;
|
|
307
|
+
if (descScore > bestScore) {
|
|
308
|
+
bestScore = descScore;
|
|
309
|
+
matchType = 'description';
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Category match
|
|
313
|
+
if (command.category) {
|
|
314
|
+
const catLabel = CATEGORY_LABELS[command.category]?.toLowerCase() ?? '';
|
|
315
|
+
const catScore = this.fuzzyScore(normalizedQuery, catLabel) * 0.4;
|
|
316
|
+
if (catScore > bestScore) {
|
|
317
|
+
bestScore = catScore;
|
|
318
|
+
matchType = 'category';
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (bestScore > 0.3) {
|
|
323
|
+
results.push({ command, matchType, score: bestScore });
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return results.sort((a, b) => b.score - a.score);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// -------------------------------------------------------------------------
|
|
331
|
+
// Markdown Output
|
|
332
|
+
// -------------------------------------------------------------------------
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Generate the main help as Markdown.
|
|
336
|
+
*/
|
|
337
|
+
private generateMarkdownMainHelp(): string {
|
|
338
|
+
const lines: string[] = [];
|
|
339
|
+
|
|
340
|
+
lines.push(`# ${this.programName} CLI`);
|
|
341
|
+
lines.push('');
|
|
342
|
+
lines.push('## Usage');
|
|
343
|
+
lines.push('');
|
|
344
|
+
lines.push('```');
|
|
345
|
+
lines.push(`${this.programName} <command> [options]`);
|
|
346
|
+
lines.push('```');
|
|
347
|
+
lines.push('');
|
|
348
|
+
|
|
349
|
+
const grouped = this.registry.grouped();
|
|
350
|
+
const categories = Array.from(grouped.keys()).sort();
|
|
351
|
+
|
|
352
|
+
for (const category of categories) {
|
|
353
|
+
const commands = grouped.get(category);
|
|
354
|
+
if (!commands || commands.length === 0) continue;
|
|
355
|
+
|
|
356
|
+
const visible = commands.filter(
|
|
357
|
+
cmd => !cmd.hidden && !cmd.name.includes(':')
|
|
358
|
+
);
|
|
359
|
+
if (visible.length === 0) continue;
|
|
360
|
+
|
|
361
|
+
const label =
|
|
362
|
+
category === 'uncategorized'
|
|
363
|
+
? 'Other Commands'
|
|
364
|
+
: (CATEGORY_LABELS[category as CommandCategory] ?? category);
|
|
365
|
+
|
|
366
|
+
lines.push(`## ${label}`);
|
|
367
|
+
lines.push('');
|
|
368
|
+
lines.push('| Command | Description |');
|
|
369
|
+
lines.push('|---------|-------------|');
|
|
370
|
+
|
|
371
|
+
for (const cmd of visible) {
|
|
372
|
+
const aliases = cmd.aliases ? ` (${cmd.aliases.join(', ')})` : '';
|
|
373
|
+
lines.push(`| \`${cmd.name}\`${aliases} | ${cmd.description} |`);
|
|
374
|
+
}
|
|
375
|
+
lines.push('');
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
lines.push('## Global Options');
|
|
379
|
+
lines.push('');
|
|
380
|
+
lines.push('| Option | Description |');
|
|
381
|
+
lines.push('|--------|-------------|');
|
|
382
|
+
lines.push('| `--verbose` | Enable verbose logging |');
|
|
383
|
+
lines.push('| `--quiet` | Suppress output |');
|
|
384
|
+
lines.push('| `--json` | Output as JSON |');
|
|
385
|
+
lines.push('| `--no-color` | Disable colored output |');
|
|
386
|
+
lines.push('| `--dry-run` | Show what would be done |');
|
|
387
|
+
lines.push('| `--config <path>` | Specify config file |');
|
|
388
|
+
lines.push('');
|
|
389
|
+
|
|
390
|
+
return lines.join('\n');
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Generate per-command help as Markdown.
|
|
395
|
+
*/
|
|
396
|
+
private generateMarkdownCommandHelp(command: CommandDefinition): string {
|
|
397
|
+
const lines: string[] = [];
|
|
398
|
+
|
|
399
|
+
lines.push(`# ${command.name}`);
|
|
400
|
+
lines.push('');
|
|
401
|
+
lines.push(command.description);
|
|
402
|
+
lines.push('');
|
|
403
|
+
|
|
404
|
+
if (command.aliases && command.aliases.length > 0) {
|
|
405
|
+
lines.push(`**Aliases:** ${command.aliases.join(', ')}`);
|
|
406
|
+
lines.push('');
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
lines.push('## Usage');
|
|
410
|
+
lines.push('');
|
|
411
|
+
lines.push('```');
|
|
412
|
+
lines.push(this.buildUsageLine(command));
|
|
413
|
+
lines.push('```');
|
|
414
|
+
lines.push('');
|
|
415
|
+
|
|
416
|
+
if (command.arguments && command.arguments.length > 0) {
|
|
417
|
+
lines.push('## Arguments');
|
|
418
|
+
lines.push('');
|
|
419
|
+
lines.push('| Name | Description | Required | Default |');
|
|
420
|
+
lines.push('|------|-------------|----------|---------|');
|
|
421
|
+
for (const arg of command.arguments) {
|
|
422
|
+
lines.push(
|
|
423
|
+
`| \`${arg.name}\` | ${arg.description} | ${arg.required ? 'Yes' : 'No'} | ${arg.defaultValue ?? '-'} |`
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
lines.push('');
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (command.options && command.options.length > 0) {
|
|
430
|
+
lines.push('## Options');
|
|
431
|
+
lines.push('');
|
|
432
|
+
lines.push('| Flag | Description | Required | Default |');
|
|
433
|
+
lines.push('|------|-------------|----------|---------|');
|
|
434
|
+
for (const opt of command.options) {
|
|
435
|
+
const defaultVal =
|
|
436
|
+
opt.defaultValue !== undefined ? String(opt.defaultValue) : '-';
|
|
437
|
+
lines.push(
|
|
438
|
+
`| \`${opt.flags}\` | ${opt.description} | ${opt.required ? 'Yes' : 'No'} | ${defaultVal} |`
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
lines.push('');
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (command.examples && command.examples.length > 0) {
|
|
445
|
+
lines.push('## Examples');
|
|
446
|
+
lines.push('');
|
|
447
|
+
for (const ex of command.examples) {
|
|
448
|
+
lines.push(`**${ex.description}:**`);
|
|
449
|
+
lines.push('```');
|
|
450
|
+
lines.push(ex.command);
|
|
451
|
+
lines.push('```');
|
|
452
|
+
lines.push('');
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
return lines.join('\n');
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// -------------------------------------------------------------------------
|
|
460
|
+
// Private Helpers
|
|
461
|
+
// -------------------------------------------------------------------------
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Build a usage line from command definition.
|
|
465
|
+
*/
|
|
466
|
+
private buildUsageLine(command: CommandDefinition): string {
|
|
467
|
+
const parts = [this.programName, command.name];
|
|
468
|
+
|
|
469
|
+
if (command.arguments) {
|
|
470
|
+
for (const arg of command.arguments) {
|
|
471
|
+
if (arg.required) {
|
|
472
|
+
parts.push(arg.variadic ? `<${arg.name}...>` : `<${arg.name}>`);
|
|
473
|
+
} else {
|
|
474
|
+
parts.push(arg.variadic ? `[${arg.name}...]` : `[${arg.name}]`);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (command.options && command.options.length > 0) {
|
|
480
|
+
parts.push('[options]');
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
return parts.join(' ');
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Simple fuzzy matching score between query and target.
|
|
488
|
+
* Returns 0-1 where 1 is an exact match.
|
|
489
|
+
*/
|
|
490
|
+
private fuzzyScore(query: string, target: string): number {
|
|
491
|
+
if (query === target) return 1;
|
|
492
|
+
if (target.startsWith(query)) return 0.9;
|
|
493
|
+
if (target.includes(query)) return 0.7;
|
|
494
|
+
|
|
495
|
+
// Character-by-character fuzzy
|
|
496
|
+
let qi = 0;
|
|
497
|
+
let ti = 0;
|
|
498
|
+
let matched = 0;
|
|
499
|
+
|
|
500
|
+
while (qi < query.length && ti < target.length) {
|
|
501
|
+
if (query[qi] === target[ti]) {
|
|
502
|
+
matched++;
|
|
503
|
+
qi++;
|
|
504
|
+
}
|
|
505
|
+
ti++;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
if (qi < query.length) return 0;
|
|
509
|
+
return matched / target.length;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// -------------------------------------------------------------------------
|
|
513
|
+
// Text formatting helpers
|
|
514
|
+
// -------------------------------------------------------------------------
|
|
515
|
+
|
|
516
|
+
private heading(text: string): string {
|
|
517
|
+
if (!this.useColor) return text;
|
|
518
|
+
return chalk.cyan.bold(text);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
private sectionTitle(text: string): string {
|
|
522
|
+
if (!this.useColor) return `${text}:`;
|
|
523
|
+
return chalk.white.bold(`${text}:`);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
private highlight(text: string): string {
|
|
527
|
+
if (!this.useColor) return text;
|
|
528
|
+
return chalk.green(text);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
private dim(text: string): string {
|
|
532
|
+
if (!this.useColor) return text;
|
|
533
|
+
return chalk.gray(text);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
private warn(text: string): string {
|
|
537
|
+
if (!this.useColor) return text;
|
|
538
|
+
return chalk.yellow(text);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wundr CLI Framework - Public API
|
|
3
|
+
*
|
|
4
|
+
* This module provides the core framework for building CLI commands:
|
|
5
|
+
*
|
|
6
|
+
* - **CommandDefinition**: The interface every command implements
|
|
7
|
+
* - **CommandRegistry**: Auto-discovery and Commander.js integration
|
|
8
|
+
* - **OutputFormatter**: Consistent table, JSON, YAML, status, progress output
|
|
9
|
+
* - **CliErrorHandler**: Typed errors with classification and recovery
|
|
10
|
+
* - **InteractiveRepl**: REPL loop with history, aliases, and tab completion
|
|
11
|
+
* - **HelpGenerator**: Structured help text from command metadata
|
|
12
|
+
* - **CompletionExporter**: Shell completion script generation
|
|
13
|
+
* - **DebugLogger**: Verbose/debug logging with TTY detection
|
|
14
|
+
* - **ProgressBar / StepTracker**: Progress tracking for long operations
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* import {
|
|
19
|
+
* CommandDefinition,
|
|
20
|
+
* CommandRegistry,
|
|
21
|
+
* OutputFormatter,
|
|
22
|
+
* CliErrorHandler,
|
|
23
|
+
* InteractiveRepl,
|
|
24
|
+
* HelpGenerator,
|
|
25
|
+
* CompletionExporter,
|
|
26
|
+
* DebugLogger,
|
|
27
|
+
* ProgressBar,
|
|
28
|
+
* StepTracker,
|
|
29
|
+
* commandSuccess,
|
|
30
|
+
* commandFailure,
|
|
31
|
+
* validationOk,
|
|
32
|
+
* validationFail,
|
|
33
|
+
* } from './framework';
|
|
34
|
+
*
|
|
35
|
+
* const myCommand: CommandDefinition = {
|
|
36
|
+
* name: 'status',
|
|
37
|
+
* description: 'Show system status',
|
|
38
|
+
* category: 'daemon',
|
|
39
|
+
* async execute(args, options, context) {
|
|
40
|
+
* const data = await getStatus();
|
|
41
|
+
* const formatter = new OutputFormatter();
|
|
42
|
+
* const message = formatter.keyValue(data);
|
|
43
|
+
* return commandSuccess(data, message);
|
|
44
|
+
* },
|
|
45
|
+
* };
|
|
46
|
+
*
|
|
47
|
+
* const registry = new CommandRegistry();
|
|
48
|
+
* registry.register(myCommand);
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* @module framework
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
// Command Interface - Types and contracts
|
|
55
|
+
export {
|
|
56
|
+
// Core interface
|
|
57
|
+
type CommandDefinition,
|
|
58
|
+
type CommandModule,
|
|
59
|
+
type CommandHook,
|
|
60
|
+
|
|
61
|
+
// Argument/option types
|
|
62
|
+
type ArgumentDefinition,
|
|
63
|
+
type OptionDefinition,
|
|
64
|
+
type CommandExample,
|
|
65
|
+
|
|
66
|
+
// Categories
|
|
67
|
+
type CommandCategory,
|
|
68
|
+
CATEGORY_LABELS,
|
|
69
|
+
|
|
70
|
+
// Validation
|
|
71
|
+
type ValidationResult,
|
|
72
|
+
type ValidationError,
|
|
73
|
+
validationOk,
|
|
74
|
+
validationFail,
|
|
75
|
+
|
|
76
|
+
// Context
|
|
77
|
+
type CommandContext,
|
|
78
|
+
type GlobalOptions,
|
|
79
|
+
type ContextLogger,
|
|
80
|
+
type OutputFormatterInterface,
|
|
81
|
+
type ConfigManagerInterface,
|
|
82
|
+
|
|
83
|
+
// Results
|
|
84
|
+
type CommandResult,
|
|
85
|
+
commandSuccess,
|
|
86
|
+
commandFailure,
|
|
87
|
+
|
|
88
|
+
// Legacy adapter
|
|
89
|
+
wrapLegacyCommand,
|
|
90
|
+
} from './command-interface';
|
|
91
|
+
|
|
92
|
+
// Command Registry
|
|
93
|
+
export {
|
|
94
|
+
CommandRegistry,
|
|
95
|
+
type RegistryOptions,
|
|
96
|
+
type RegisteredCommand,
|
|
97
|
+
} from './command-registry';
|
|
98
|
+
|
|
99
|
+
// Output Formatter
|
|
100
|
+
export {
|
|
101
|
+
OutputFormatter,
|
|
102
|
+
type TableOptions,
|
|
103
|
+
type ColumnDefinition,
|
|
104
|
+
type JsonOptions,
|
|
105
|
+
type KeyValueOptions,
|
|
106
|
+
type ListOptions,
|
|
107
|
+
type TreeNode,
|
|
108
|
+
type TreeOptions,
|
|
109
|
+
type ProgressOptions,
|
|
110
|
+
type StatusState,
|
|
111
|
+
} from './output-formatter';
|
|
112
|
+
|
|
113
|
+
// Error Handler
|
|
114
|
+
export {
|
|
115
|
+
CliErrorHandler,
|
|
116
|
+
CliError,
|
|
117
|
+
type ErrorCode,
|
|
118
|
+
type ErrorDetails,
|
|
119
|
+
type RecoverySuggestion,
|
|
120
|
+
type ErrorRecoveryHandler,
|
|
121
|
+
type ErrorClassification,
|
|
122
|
+
type ClassifiedError,
|
|
123
|
+
classifyError,
|
|
124
|
+
classifyAnyError,
|
|
125
|
+
} from './error-handler';
|
|
126
|
+
|
|
127
|
+
// Interactive REPL
|
|
128
|
+
export {
|
|
129
|
+
InteractiveRepl,
|
|
130
|
+
type ReplOptions,
|
|
131
|
+
type HistoryEntry,
|
|
132
|
+
} from './interactive-repl';
|
|
133
|
+
|
|
134
|
+
// Help Generator
|
|
135
|
+
export {
|
|
136
|
+
HelpGenerator,
|
|
137
|
+
type HelpGeneratorOptions,
|
|
138
|
+
type SearchResult,
|
|
139
|
+
} from './help-generator';
|
|
140
|
+
|
|
141
|
+
// Completion Exporter
|
|
142
|
+
export {
|
|
143
|
+
CompletionExporter,
|
|
144
|
+
type ShellType,
|
|
145
|
+
type CompletionData,
|
|
146
|
+
type CompletionCommand,
|
|
147
|
+
type CompletionArgument,
|
|
148
|
+
type CompletionOption,
|
|
149
|
+
} from './completion-exporter';
|
|
150
|
+
|
|
151
|
+
// Debug Logger
|
|
152
|
+
export {
|
|
153
|
+
DebugLogger,
|
|
154
|
+
TaggedLogger,
|
|
155
|
+
type LogLevel,
|
|
156
|
+
type LogEntry,
|
|
157
|
+
type DebugLoggerOptions,
|
|
158
|
+
} from './debug-logger';
|
|
159
|
+
|
|
160
|
+
// Progress Manager
|
|
161
|
+
export {
|
|
162
|
+
ProgressBar,
|
|
163
|
+
MultiProgressManager,
|
|
164
|
+
StepTracker,
|
|
165
|
+
type ProgressBarOptions,
|
|
166
|
+
type ProgressState,
|
|
167
|
+
type ProgressStep,
|
|
168
|
+
type StepTrackerOptions,
|
|
169
|
+
} from './progress-manager';
|