@claude-flow/cli 3.0.0-alpha.1
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/.agentic-flow/intelligence.json +16 -0
- package/.claude-flow/metrics/agent-metrics.json +1 -0
- package/.claude-flow/metrics/performance.json +87 -0
- package/.claude-flow/metrics/task-metrics.json +10 -0
- package/README.md +1186 -0
- package/__tests__/README.md +140 -0
- package/__tests__/TEST_SUMMARY.md +144 -0
- package/__tests__/cli.test.ts +558 -0
- package/__tests__/commands.test.ts +726 -0
- package/__tests__/config-adapter.test.ts +362 -0
- package/__tests__/config-loading.test.ts +106 -0
- package/__tests__/coverage/.tmp/coverage-0.json +1 -0
- package/__tests__/coverage/.tmp/coverage-1.json +1 -0
- package/__tests__/coverage/.tmp/coverage-2.json +1 -0
- package/__tests__/coverage/.tmp/coverage-3.json +1 -0
- package/__tests__/coverage/.tmp/coverage-4.json +1 -0
- package/__tests__/coverage/.tmp/coverage-5.json +1 -0
- package/__tests__/mcp-client.test.ts +480 -0
- package/__tests__/p1-commands.test.ts +1064 -0
- package/bin/cli.js +14 -0
- package/dist/src/commands/agent.d.ts +8 -0
- package/dist/src/commands/agent.d.ts.map +1 -0
- package/dist/src/commands/agent.js +803 -0
- package/dist/src/commands/agent.js.map +1 -0
- package/dist/src/commands/config.d.ts +8 -0
- package/dist/src/commands/config.d.ts.map +1 -0
- package/dist/src/commands/config.js +406 -0
- package/dist/src/commands/config.js.map +1 -0
- package/dist/src/commands/hive-mind.d.ts +8 -0
- package/dist/src/commands/hive-mind.d.ts.map +1 -0
- package/dist/src/commands/hive-mind.js +627 -0
- package/dist/src/commands/hive-mind.js.map +1 -0
- package/dist/src/commands/hooks.d.ts +8 -0
- package/dist/src/commands/hooks.d.ts.map +1 -0
- package/dist/src/commands/hooks.js +2098 -0
- package/dist/src/commands/hooks.js.map +1 -0
- package/dist/src/commands/index.d.ts +51 -0
- package/dist/src/commands/index.d.ts.map +1 -0
- package/dist/src/commands/index.js +105 -0
- package/dist/src/commands/index.js.map +1 -0
- package/dist/src/commands/init.d.ts +8 -0
- package/dist/src/commands/init.d.ts.map +1 -0
- package/dist/src/commands/init.js +532 -0
- package/dist/src/commands/init.js.map +1 -0
- package/dist/src/commands/mcp.d.ts +11 -0
- package/dist/src/commands/mcp.d.ts.map +1 -0
- package/dist/src/commands/mcp.js +662 -0
- package/dist/src/commands/mcp.js.map +1 -0
- package/dist/src/commands/memory.d.ts +8 -0
- package/dist/src/commands/memory.d.ts.map +1 -0
- package/dist/src/commands/memory.js +911 -0
- package/dist/src/commands/memory.js.map +1 -0
- package/dist/src/commands/migrate.d.ts +8 -0
- package/dist/src/commands/migrate.d.ts.map +1 -0
- package/dist/src/commands/migrate.js +398 -0
- package/dist/src/commands/migrate.js.map +1 -0
- package/dist/src/commands/process.d.ts +10 -0
- package/dist/src/commands/process.d.ts.map +1 -0
- package/dist/src/commands/process.js +566 -0
- package/dist/src/commands/process.js.map +1 -0
- package/dist/src/commands/session.d.ts +8 -0
- package/dist/src/commands/session.d.ts.map +1 -0
- package/dist/src/commands/session.js +750 -0
- package/dist/src/commands/session.js.map +1 -0
- package/dist/src/commands/start.d.ts +8 -0
- package/dist/src/commands/start.d.ts.map +1 -0
- package/dist/src/commands/start.js +398 -0
- package/dist/src/commands/start.js.map +1 -0
- package/dist/src/commands/status.d.ts +8 -0
- package/dist/src/commands/status.d.ts.map +1 -0
- package/dist/src/commands/status.js +560 -0
- package/dist/src/commands/status.js.map +1 -0
- package/dist/src/commands/swarm.d.ts +8 -0
- package/dist/src/commands/swarm.d.ts.map +1 -0
- package/dist/src/commands/swarm.js +573 -0
- package/dist/src/commands/swarm.js.map +1 -0
- package/dist/src/commands/task.d.ts +8 -0
- package/dist/src/commands/task.d.ts.map +1 -0
- package/dist/src/commands/task.js +671 -0
- package/dist/src/commands/task.js.map +1 -0
- package/dist/src/commands/workflow.d.ts +8 -0
- package/dist/src/commands/workflow.d.ts.map +1 -0
- package/dist/src/commands/workflow.js +617 -0
- package/dist/src/commands/workflow.js.map +1 -0
- package/dist/src/config-adapter.d.ts +15 -0
- package/dist/src/config-adapter.d.ts.map +1 -0
- package/dist/src/config-adapter.js +185 -0
- package/dist/src/config-adapter.js.map +1 -0
- package/dist/src/index.d.ts +55 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +312 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/infrastructure/in-memory-repositories.d.ts +68 -0
- package/dist/src/infrastructure/in-memory-repositories.d.ts.map +1 -0
- package/dist/src/infrastructure/in-memory-repositories.js +264 -0
- package/dist/src/infrastructure/in-memory-repositories.js.map +1 -0
- package/dist/src/init/claudemd-generator.d.ts +15 -0
- package/dist/src/init/claudemd-generator.d.ts.map +1 -0
- package/dist/src/init/claudemd-generator.js +626 -0
- package/dist/src/init/claudemd-generator.js.map +1 -0
- package/dist/src/init/executor.d.ts +11 -0
- package/dist/src/init/executor.d.ts.map +1 -0
- package/dist/src/init/executor.js +647 -0
- package/dist/src/init/executor.js.map +1 -0
- package/dist/src/init/helpers-generator.d.ts +42 -0
- package/dist/src/init/helpers-generator.d.ts.map +1 -0
- package/dist/src/init/helpers-generator.js +613 -0
- package/dist/src/init/helpers-generator.js.map +1 -0
- package/dist/src/init/index.d.ts +12 -0
- package/dist/src/init/index.d.ts.map +1 -0
- package/dist/src/init/index.js +15 -0
- package/dist/src/init/index.js.map +1 -0
- package/dist/src/init/mcp-generator.d.ts +18 -0
- package/dist/src/init/mcp-generator.d.ts.map +1 -0
- package/dist/src/init/mcp-generator.js +71 -0
- package/dist/src/init/mcp-generator.js.map +1 -0
- package/dist/src/init/settings-generator.d.ts +14 -0
- package/dist/src/init/settings-generator.d.ts.map +1 -0
- package/dist/src/init/settings-generator.js +257 -0
- package/dist/src/init/settings-generator.js.map +1 -0
- package/dist/src/init/statusline-generator.d.ts +14 -0
- package/dist/src/init/statusline-generator.d.ts.map +1 -0
- package/dist/src/init/statusline-generator.js +206 -0
- package/dist/src/init/statusline-generator.js.map +1 -0
- package/dist/src/init/types.d.ts +240 -0
- package/dist/src/init/types.d.ts.map +1 -0
- package/dist/src/init/types.js +210 -0
- package/dist/src/init/types.js.map +1 -0
- package/dist/src/mcp-client.d.ts +92 -0
- package/dist/src/mcp-client.d.ts.map +1 -0
- package/dist/src/mcp-client.js +189 -0
- package/dist/src/mcp-client.js.map +1 -0
- package/dist/src/mcp-server.d.ts +153 -0
- package/dist/src/mcp-server.d.ts.map +1 -0
- package/dist/src/mcp-server.js +448 -0
- package/dist/src/mcp-server.js.map +1 -0
- package/dist/src/mcp-tools/agent-tools.d.ts +8 -0
- package/dist/src/mcp-tools/agent-tools.d.ts.map +1 -0
- package/dist/src/mcp-tools/agent-tools.js +90 -0
- package/dist/src/mcp-tools/agent-tools.js.map +1 -0
- package/dist/src/mcp-tools/config-tools.d.ts +8 -0
- package/dist/src/mcp-tools/config-tools.d.ts.map +1 -0
- package/dist/src/mcp-tools/config-tools.js +86 -0
- package/dist/src/mcp-tools/config-tools.js.map +1 -0
- package/dist/src/mcp-tools/hooks-tools.d.ts +41 -0
- package/dist/src/mcp-tools/hooks-tools.d.ts.map +1 -0
- package/dist/src/mcp-tools/hooks-tools.js +1646 -0
- package/dist/src/mcp-tools/hooks-tools.js.map +1 -0
- package/dist/src/mcp-tools/index.d.ts +12 -0
- package/dist/src/mcp-tools/index.d.ts.map +1 -0
- package/dist/src/mcp-tools/index.js +11 -0
- package/dist/src/mcp-tools/index.js.map +1 -0
- package/dist/src/mcp-tools/memory-tools.d.ts +8 -0
- package/dist/src/mcp-tools/memory-tools.d.ts.map +1 -0
- package/dist/src/mcp-tools/memory-tools.js +87 -0
- package/dist/src/mcp-tools/memory-tools.js.map +1 -0
- package/dist/src/mcp-tools/swarm-tools.d.ts +8 -0
- package/dist/src/mcp-tools/swarm-tools.d.ts.map +1 -0
- package/dist/src/mcp-tools/swarm-tools.js +67 -0
- package/dist/src/mcp-tools/swarm-tools.js.map +1 -0
- package/dist/src/mcp-tools/types.d.ts +31 -0
- package/dist/src/mcp-tools/types.d.ts.map +1 -0
- package/dist/src/mcp-tools/types.js +7 -0
- package/dist/src/mcp-tools/types.js.map +1 -0
- package/dist/src/output.d.ts +117 -0
- package/dist/src/output.d.ts.map +1 -0
- package/dist/src/output.js +471 -0
- package/dist/src/output.js.map +1 -0
- package/dist/src/parser.d.ts +41 -0
- package/dist/src/parser.d.ts.map +1 -0
- package/dist/src/parser.js +353 -0
- package/dist/src/parser.js.map +1 -0
- package/dist/src/prompt.d.ts +44 -0
- package/dist/src/prompt.d.ts.map +1 -0
- package/dist/src/prompt.js +501 -0
- package/dist/src/prompt.js.map +1 -0
- package/dist/src/types.d.ts +198 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +38 -0
- package/dist/src/types.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/docs/CONFIG_LOADING.md +236 -0
- package/docs/IMPLEMENTATION_COMPLETE.md +421 -0
- package/docs/MCP_CLIENT_GUIDE.md +620 -0
- package/docs/REFACTORING_SUMMARY.md +247 -0
- package/package.json +29 -0
- package/src/commands/agent.ts +941 -0
- package/src/commands/config.ts +452 -0
- package/src/commands/hive-mind.ts +762 -0
- package/src/commands/hooks.ts +2603 -0
- package/src/commands/index.ts +115 -0
- package/src/commands/init.ts +597 -0
- package/src/commands/mcp.ts +753 -0
- package/src/commands/memory.ts +1063 -0
- package/src/commands/migrate.ts +447 -0
- package/src/commands/process.ts +617 -0
- package/src/commands/session.ts +891 -0
- package/src/commands/start.ts +457 -0
- package/src/commands/status.ts +705 -0
- package/src/commands/swarm.ts +648 -0
- package/src/commands/task.ts +792 -0
- package/src/commands/workflow.ts +742 -0
- package/src/config-adapter.ts +210 -0
- package/src/index.ts +383 -0
- package/src/infrastructure/in-memory-repositories.ts +310 -0
- package/src/init/claudemd-generator.ts +631 -0
- package/src/init/executor.ts +756 -0
- package/src/init/helpers-generator.ts +628 -0
- package/src/init/index.ts +60 -0
- package/src/init/mcp-generator.ts +83 -0
- package/src/init/settings-generator.ts +274 -0
- package/src/init/statusline-generator.ts +211 -0
- package/src/init/types.ts +447 -0
- package/src/mcp-client.ts +227 -0
- package/src/mcp-server.ts +571 -0
- package/src/mcp-tools/agent-tools.ts +92 -0
- package/src/mcp-tools/config-tools.ts +88 -0
- package/src/mcp-tools/hooks-tools.ts +1819 -0
- package/src/mcp-tools/index.ts +12 -0
- package/src/mcp-tools/memory-tools.ts +89 -0
- package/src/mcp-tools/swarm-tools.ts +69 -0
- package/src/mcp-tools/types.ts +33 -0
- package/src/output.ts +593 -0
- package/src/parser.ts +417 -0
- package/src/prompt.ts +619 -0
- package/src/types.ts +287 -0
- package/tsconfig.json +16 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vitest.config.ts +13 -0
package/src/parser.ts
ADDED
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* V3 CLI Command Parser
|
|
3
|
+
* Advanced argument parsing with validation and type coercion
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Command, CommandOption, ParsedFlags, CommandContext, V3Config } from './types.js';
|
|
7
|
+
|
|
8
|
+
export interface ParseResult {
|
|
9
|
+
command: string[];
|
|
10
|
+
flags: ParsedFlags;
|
|
11
|
+
positional: string[];
|
|
12
|
+
raw: string[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ParserOptions {
|
|
16
|
+
stopAtFirstNonFlag?: boolean;
|
|
17
|
+
allowUnknownFlags?: boolean;
|
|
18
|
+
booleanFlags?: string[];
|
|
19
|
+
stringFlags?: string[];
|
|
20
|
+
arrayFlags?: string[];
|
|
21
|
+
aliases?: Record<string, string>;
|
|
22
|
+
defaults?: Record<string, unknown>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class CommandParser {
|
|
26
|
+
private options: ParserOptions;
|
|
27
|
+
private commands: Map<string, Command> = new Map();
|
|
28
|
+
private globalOptions: CommandOption[] = [];
|
|
29
|
+
|
|
30
|
+
constructor(options: ParserOptions = {}) {
|
|
31
|
+
this.options = {
|
|
32
|
+
stopAtFirstNonFlag: false,
|
|
33
|
+
allowUnknownFlags: false,
|
|
34
|
+
...options
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
this.initializeGlobalOptions();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private initializeGlobalOptions(): void {
|
|
41
|
+
this.globalOptions = [
|
|
42
|
+
{
|
|
43
|
+
name: 'help',
|
|
44
|
+
short: 'h',
|
|
45
|
+
description: 'Show help information',
|
|
46
|
+
type: 'boolean',
|
|
47
|
+
default: false
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: 'version',
|
|
51
|
+
short: 'V',
|
|
52
|
+
description: 'Show version number',
|
|
53
|
+
type: 'boolean',
|
|
54
|
+
default: false
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: 'verbose',
|
|
58
|
+
short: 'v',
|
|
59
|
+
description: 'Enable verbose output',
|
|
60
|
+
type: 'boolean',
|
|
61
|
+
default: false
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: 'quiet',
|
|
65
|
+
short: 'q',
|
|
66
|
+
description: 'Suppress non-essential output',
|
|
67
|
+
type: 'boolean',
|
|
68
|
+
default: false
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: 'config',
|
|
72
|
+
short: 'c',
|
|
73
|
+
description: 'Path to configuration file',
|
|
74
|
+
type: 'string'
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: 'format',
|
|
78
|
+
short: 'f',
|
|
79
|
+
description: 'Output format (text, json, table)',
|
|
80
|
+
type: 'string',
|
|
81
|
+
default: 'text',
|
|
82
|
+
choices: ['text', 'json', 'table']
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: 'no-color',
|
|
86
|
+
description: 'Disable colored output',
|
|
87
|
+
type: 'boolean',
|
|
88
|
+
default: false
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: 'interactive',
|
|
92
|
+
short: 'i',
|
|
93
|
+
description: 'Enable interactive mode',
|
|
94
|
+
type: 'boolean',
|
|
95
|
+
default: true
|
|
96
|
+
}
|
|
97
|
+
];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
registerCommand(command: Command): void {
|
|
101
|
+
this.commands.set(command.name, command);
|
|
102
|
+
if (command.aliases) {
|
|
103
|
+
for (const alias of command.aliases) {
|
|
104
|
+
this.commands.set(alias, command);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
getCommand(name: string): Command | undefined {
|
|
110
|
+
return this.commands.get(name);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
getAllCommands(): Command[] {
|
|
114
|
+
// Return unique commands (filter out aliases)
|
|
115
|
+
const seen = new Set<Command>();
|
|
116
|
+
return Array.from(this.commands.values()).filter(cmd => {
|
|
117
|
+
if (seen.has(cmd)) return false;
|
|
118
|
+
seen.add(cmd);
|
|
119
|
+
return true;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
parse(args: string[]): ParseResult {
|
|
124
|
+
const result: ParseResult = {
|
|
125
|
+
command: [],
|
|
126
|
+
flags: { _: [] },
|
|
127
|
+
positional: [],
|
|
128
|
+
raw: [...args]
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// Build flag configuration from global options
|
|
132
|
+
const aliases = this.buildAliases();
|
|
133
|
+
const booleanFlags = this.getBooleanFlags();
|
|
134
|
+
|
|
135
|
+
let i = 0;
|
|
136
|
+
let parsingFlags = true;
|
|
137
|
+
|
|
138
|
+
while (i < args.length) {
|
|
139
|
+
const arg = args[i];
|
|
140
|
+
|
|
141
|
+
// Check for end of flags marker
|
|
142
|
+
if (arg === '--') {
|
|
143
|
+
parsingFlags = false;
|
|
144
|
+
i++;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Handle flags
|
|
149
|
+
if (parsingFlags && arg.startsWith('-')) {
|
|
150
|
+
const parseResult = this.parseFlag(args, i, aliases, booleanFlags);
|
|
151
|
+
|
|
152
|
+
// Apply to result flags
|
|
153
|
+
Object.assign(result.flags, parseResult.flags);
|
|
154
|
+
i = parseResult.nextIndex;
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Handle positional arguments
|
|
159
|
+
if (result.command.length === 0 && this.commands.has(arg)) {
|
|
160
|
+
// This is a command
|
|
161
|
+
result.command.push(arg);
|
|
162
|
+
|
|
163
|
+
// Check for subcommand (level 1)
|
|
164
|
+
const cmd = this.commands.get(arg);
|
|
165
|
+
if (cmd?.subcommands && i + 1 < args.length) {
|
|
166
|
+
const nextArg = args[i + 1];
|
|
167
|
+
const subCmd = cmd.subcommands.find(sc => sc.name === nextArg || sc.aliases?.includes(nextArg));
|
|
168
|
+
if (subCmd) {
|
|
169
|
+
result.command.push(nextArg);
|
|
170
|
+
i++;
|
|
171
|
+
|
|
172
|
+
// Check for nested subcommand (level 2)
|
|
173
|
+
if (subCmd.subcommands && i + 1 < args.length) {
|
|
174
|
+
const nestedArg = args[i + 1];
|
|
175
|
+
const nestedCmd = subCmd.subcommands.find(sc => sc.name === nestedArg || sc.aliases?.includes(nestedArg));
|
|
176
|
+
if (nestedCmd) {
|
|
177
|
+
result.command.push(nestedArg);
|
|
178
|
+
i++;
|
|
179
|
+
|
|
180
|
+
// Check for deeply nested subcommand (level 3)
|
|
181
|
+
if (nestedCmd.subcommands && i + 1 < args.length) {
|
|
182
|
+
const deepArg = args[i + 1];
|
|
183
|
+
const deepCmd = nestedCmd.subcommands.find(sc => sc.name === deepArg || sc.aliases?.includes(deepArg));
|
|
184
|
+
if (deepCmd) {
|
|
185
|
+
result.command.push(deepArg);
|
|
186
|
+
i++;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
} else {
|
|
194
|
+
// Positional argument
|
|
195
|
+
result.positional.push(arg);
|
|
196
|
+
result.flags._.push(arg);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
i++;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Apply defaults
|
|
203
|
+
this.applyDefaults(result.flags);
|
|
204
|
+
|
|
205
|
+
return result;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
private parseFlag(
|
|
209
|
+
args: string[],
|
|
210
|
+
index: number,
|
|
211
|
+
aliases: Record<string, string>,
|
|
212
|
+
booleanFlags: Set<string>
|
|
213
|
+
): { flags: ParsedFlags; nextIndex: number } {
|
|
214
|
+
const flags: ParsedFlags = { _: [] };
|
|
215
|
+
const arg = args[index];
|
|
216
|
+
let nextIndex = index + 1;
|
|
217
|
+
|
|
218
|
+
if (arg.startsWith('--')) {
|
|
219
|
+
// Long flag
|
|
220
|
+
const equalIndex = arg.indexOf('=');
|
|
221
|
+
|
|
222
|
+
if (equalIndex !== -1) {
|
|
223
|
+
// --flag=value
|
|
224
|
+
const key = arg.slice(2, equalIndex);
|
|
225
|
+
const value = arg.slice(equalIndex + 1);
|
|
226
|
+
flags[this.normalizeKey(key)] = this.parseValue(value);
|
|
227
|
+
} else if (arg.startsWith('--no-')) {
|
|
228
|
+
// --no-flag (boolean negation)
|
|
229
|
+
const key = arg.slice(5);
|
|
230
|
+
flags[this.normalizeKey(key)] = false;
|
|
231
|
+
} else {
|
|
232
|
+
const key = arg.slice(2);
|
|
233
|
+
const normalizedKey = this.normalizeKey(key);
|
|
234
|
+
|
|
235
|
+
if (booleanFlags.has(normalizedKey)) {
|
|
236
|
+
flags[normalizedKey] = true;
|
|
237
|
+
} else if (nextIndex < args.length && !args[nextIndex].startsWith('-')) {
|
|
238
|
+
flags[normalizedKey] = this.parseValue(args[nextIndex]);
|
|
239
|
+
nextIndex++;
|
|
240
|
+
} else {
|
|
241
|
+
flags[normalizedKey] = true;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
} else if (arg.startsWith('-')) {
|
|
245
|
+
// Short flag(s)
|
|
246
|
+
const chars = arg.slice(1);
|
|
247
|
+
|
|
248
|
+
if (chars.length === 1) {
|
|
249
|
+
// Single short flag
|
|
250
|
+
const key = aliases[chars] || chars;
|
|
251
|
+
const normalizedKey = this.normalizeKey(key);
|
|
252
|
+
|
|
253
|
+
if (booleanFlags.has(normalizedKey)) {
|
|
254
|
+
flags[normalizedKey] = true;
|
|
255
|
+
} else if (nextIndex < args.length && !args[nextIndex].startsWith('-')) {
|
|
256
|
+
flags[normalizedKey] = this.parseValue(args[nextIndex]);
|
|
257
|
+
nextIndex++;
|
|
258
|
+
} else {
|
|
259
|
+
flags[normalizedKey] = true;
|
|
260
|
+
}
|
|
261
|
+
} else {
|
|
262
|
+
// Multiple short flags combined (e.g., -abc)
|
|
263
|
+
for (const char of chars) {
|
|
264
|
+
const key = aliases[char] || char;
|
|
265
|
+
flags[this.normalizeKey(key)] = true;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return { flags, nextIndex };
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
private parseValue(value: string): string | number | boolean {
|
|
274
|
+
// Boolean
|
|
275
|
+
if (value.toLowerCase() === 'true') return true;
|
|
276
|
+
if (value.toLowerCase() === 'false') return false;
|
|
277
|
+
|
|
278
|
+
// Number
|
|
279
|
+
const num = Number(value);
|
|
280
|
+
if (!isNaN(num) && value.trim() !== '') return num;
|
|
281
|
+
|
|
282
|
+
// String
|
|
283
|
+
return value;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
private normalizeKey(key: string): string {
|
|
287
|
+
// Convert kebab-case to camelCase
|
|
288
|
+
return key.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
private buildAliases(): Record<string, string> {
|
|
292
|
+
const aliases: Record<string, string> = {};
|
|
293
|
+
|
|
294
|
+
for (const opt of this.globalOptions) {
|
|
295
|
+
if (opt.short) {
|
|
296
|
+
aliases[opt.short] = opt.name;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Add aliases from all commands
|
|
301
|
+
for (const cmd of this.commands.values()) {
|
|
302
|
+
if (cmd.options) {
|
|
303
|
+
for (const opt of cmd.options) {
|
|
304
|
+
if (opt.short) {
|
|
305
|
+
aliases[opt.short] = opt.name;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return { ...aliases, ...this.options.aliases };
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
private getBooleanFlags(): Set<string> {
|
|
315
|
+
const flags = new Set<string>();
|
|
316
|
+
|
|
317
|
+
for (const opt of this.globalOptions) {
|
|
318
|
+
if (opt.type === 'boolean') {
|
|
319
|
+
flags.add(this.normalizeKey(opt.name));
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Add boolean flags from all commands
|
|
324
|
+
for (const cmd of this.commands.values()) {
|
|
325
|
+
if (cmd.options) {
|
|
326
|
+
for (const opt of cmd.options) {
|
|
327
|
+
if (opt.type === 'boolean') {
|
|
328
|
+
flags.add(this.normalizeKey(opt.name));
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (this.options.booleanFlags) {
|
|
335
|
+
for (const flag of this.options.booleanFlags) {
|
|
336
|
+
flags.add(this.normalizeKey(flag));
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return flags;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
private applyDefaults(flags: ParsedFlags): void {
|
|
344
|
+
// Apply global option defaults
|
|
345
|
+
for (const opt of this.globalOptions) {
|
|
346
|
+
const key = this.normalizeKey(opt.name);
|
|
347
|
+
if (flags[key] === undefined && opt.default !== undefined) {
|
|
348
|
+
flags[key] = opt.default as string | boolean | number | string[];
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Apply custom defaults
|
|
353
|
+
if (this.options.defaults) {
|
|
354
|
+
for (const [key, value] of Object.entries(this.options.defaults)) {
|
|
355
|
+
const normalizedKey = this.normalizeKey(key);
|
|
356
|
+
if (flags[normalizedKey] === undefined) {
|
|
357
|
+
flags[normalizedKey] = value as string | boolean | number | string[];
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
validateFlags(flags: ParsedFlags, command?: Command): string[] {
|
|
364
|
+
const errors: string[] = [];
|
|
365
|
+
const allOptions = [...this.globalOptions];
|
|
366
|
+
|
|
367
|
+
if (command?.options) {
|
|
368
|
+
allOptions.push(...command.options);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Check required flags
|
|
372
|
+
for (const opt of allOptions) {
|
|
373
|
+
const key = this.normalizeKey(opt.name);
|
|
374
|
+
|
|
375
|
+
if (opt.required && (flags[key] === undefined || flags[key] === '')) {
|
|
376
|
+
errors.push(`Required option missing: --${opt.name}`);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Check choices
|
|
380
|
+
if (opt.choices && flags[key] !== undefined) {
|
|
381
|
+
const value = String(flags[key]);
|
|
382
|
+
if (!opt.choices.includes(value)) {
|
|
383
|
+
errors.push(`Invalid value for --${opt.name}: ${value}. Must be one of: ${opt.choices.join(', ')}`);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Run custom validator
|
|
388
|
+
if (opt.validate && flags[key] !== undefined) {
|
|
389
|
+
const result = opt.validate(flags[key]);
|
|
390
|
+
if (result !== true) {
|
|
391
|
+
errors.push(typeof result === 'string' ? result : `Invalid value for --${opt.name}`);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Check for unknown flags if not allowed
|
|
397
|
+
if (!this.options.allowUnknownFlags) {
|
|
398
|
+
const knownFlags = new Set(allOptions.map(opt => this.normalizeKey(opt.name)));
|
|
399
|
+
knownFlags.add('_'); // Positional args
|
|
400
|
+
|
|
401
|
+
for (const key of Object.keys(flags)) {
|
|
402
|
+
if (!knownFlags.has(key) && key !== '_') {
|
|
403
|
+
errors.push(`Unknown option: --${key}`);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return errors;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
getGlobalOptions(): CommandOption[] {
|
|
412
|
+
return [...this.globalOptions];
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Export singleton parser instance
|
|
417
|
+
export const commandParser = new CommandParser({ allowUnknownFlags: true });
|