@xmorse/cac 6.0.1 → 6.0.3

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/deno/CAC.ts CHANGED
@@ -160,18 +160,27 @@ class CAC extends EventEmitter {
160
160
  }
161
161
  let shouldParse = true;
162
162
 
163
+ // Sort by name length (longest first) so "mcp login" matches before "mcp"
164
+ const sortedCommands = [...this.commands].sort((a, b) => {
165
+ const aLength = a.name.split(' ').filter(Boolean).length;
166
+ const bLength = b.name.split(' ').filter(Boolean).length;
167
+ return bLength - aLength;
168
+ });
169
+
163
170
  // Search sub-commands
164
- for (const command of this.commands) {
171
+ for (const command of sortedCommands) {
165
172
  const parsed = this.mri(argv.slice(2), command);
166
- const commandName = parsed.args[0];
167
- if (command.isMatched(commandName)) {
173
+ const result = command.isMatched(parsed.args as string[]);
174
+ if (result.matched) {
168
175
  shouldParse = false;
176
+ const matchedCommandName = parsed.args.slice(0, result.consumedArgs).join(' ');
169
177
  const parsedInfo = {
170
178
  ...parsed,
171
- args: parsed.args.slice(1)
179
+ args: parsed.args.slice(result.consumedArgs)
172
180
  };
173
- this.setParsedInfo(parsedInfo, command, commandName);
174
- this.emit(`command:${commandName}`, command);
181
+ this.setParsedInfo(parsedInfo, command, matchedCommandName);
182
+ this.emit(`command:${matchedCommandName}`, command);
183
+ break; // Stop after first match (greedy matching)
175
184
  }
176
185
  }
177
186
  if (shouldParse) {
package/deno/Command.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import CAC from "./CAC.ts";
2
2
  import Option, { OptionConfig } from "./Option.ts";
3
- import { removeBrackets, findAllBrackets, findLongest, padRight, CACError, wrapText } from "./utils.ts";
3
+ import { removeBrackets, findAllBrackets, findLongest, padRight, CACError } from "./utils.ts";
4
4
  import { platformInfo } from "./deno.ts";
5
5
  interface CommandArg {
6
6
  required: boolean;
@@ -77,13 +77,38 @@ class Command {
77
77
  this.commandAction = callback;
78
78
  return this;
79
79
  }
80
-
81
- /**
82
- * Check if a command name is matched by this command
83
- * @param name Command name
84
- */
85
- isMatched(name: string) {
86
- return this.name === name || this.aliasNames.includes(name);
80
+ isMatched(args: string[]): {
81
+ matched: boolean;
82
+ consumedArgs: number;
83
+ } {
84
+ const nameParts = this.name.split(' ').filter(Boolean);
85
+ if (nameParts.length === 0) {
86
+ return {
87
+ matched: false,
88
+ consumedArgs: 0
89
+ };
90
+ }
91
+ if (args.length < nameParts.length) {
92
+ return {
93
+ matched: false,
94
+ consumedArgs: 0
95
+ };
96
+ }
97
+ for (let i = 0; i < nameParts.length; i++) {
98
+ if (nameParts[i] !== args[i]) {
99
+ if (i === 0 && this.aliasNames.includes(args[i])) {
100
+ continue;
101
+ }
102
+ return {
103
+ matched: false,
104
+ consumedArgs: 0
105
+ };
106
+ }
107
+ }
108
+ return {
109
+ matched: true,
110
+ consumedArgs: nameParts.length
111
+ };
87
112
  }
88
113
  get isDefaultCommand() {
89
114
  return this.name === '' || this.aliasNames.includes('!');
@@ -120,12 +145,11 @@ class Command {
120
145
  body: ` $ ${name} ${this.usageText || this.rawName}`
121
146
  });
122
147
 
123
- // Show description for specific commands (not global/default)
148
+ // Show full description for specific commands (not global/default)
124
149
  if (!this.isGlobalCommand && !this.isDefaultCommand && this.description) {
125
- const terminalWidth = process.stdout?.columns || 80;
126
150
  sections.push({
127
151
  title: 'Description',
128
- body: wrapText(this.description, terminalWidth)
152
+ body: this.description.split('\n').map(line => ` ${line}`).join('\n')
129
153
  });
130
154
  }
131
155
  const showCommands = (this.isGlobalCommand || this.isDefaultCommand) && commands.length > 0;
@@ -134,9 +158,7 @@ class Command {
134
158
  sections.push({
135
159
  title: 'Commands',
136
160
  body: commands.map(command => {
137
- // Only show first line of description in commands listing
138
- const firstLine = command.description.split('\n')[0].trim();
139
- return ` ${padRight(command.rawName, longestCommandName.length)} ${firstLine}`;
161
+ return ` ${padRight(command.rawName, longestCommandName.length)} ${command.description}`;
140
162
  }).join('\n')
141
163
  });
142
164
  sections.push({
package/deno/utils.ts CHANGED
@@ -126,44 +126,4 @@ export class CACError extends Error {
126
126
  this.stack = new Error(message).stack;
127
127
  }
128
128
  }
129
- }
130
-
131
- /**
132
- * Wrap text to fit within a given width, preserving existing line breaks
133
- * and special formatting (lists, code blocks, etc.)
134
- */
135
- export const wrapText = (text: string, width = 80, indent = ' '): string => {
136
- const lines = text.split('\n');
137
- const result: string[] = [];
138
- for (const line of lines) {
139
- // Preserve empty lines
140
- if (line.trim() === '') {
141
- result.push('');
142
- continue;
143
- }
144
-
145
- // Preserve lines that start with special chars (bullets, headers, code, indented)
146
- if (/^(\s*[-*>#`]|\s{4,}|#{1,6}\s)/.test(line)) {
147
- result.push(indent + line);
148
- continue;
149
- }
150
-
151
- // Wrap normal paragraphs
152
- const words = line.split(' ');
153
- let currentLine = indent;
154
- for (const word of words) {
155
- if (currentLine.length + word.length + 1 > width) {
156
- if (currentLine.trim()) {
157
- result.push(currentLine);
158
- }
159
- currentLine = indent + word;
160
- } else {
161
- currentLine = currentLine === indent ? indent + word : `${currentLine} ${word}`;
162
- }
163
- }
164
- if (currentLine.trim()) {
165
- result.push(currentLine);
166
- }
167
- }
168
- return result.join('\n');
169
- };
129
+ }
package/dist/index.js CHANGED
@@ -4,37 +4,6 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var events = require('events');
6
6
 
7
- function wrapText(text, width = 80, indent = ' ') {
8
- const lines = text.split('\n');
9
- const result = [];
10
- for (const line of lines) {
11
- if (line.trim() === '') {
12
- result.push('');
13
- continue;
14
- }
15
- if (/^(\s*[-*>#`]|\s{4,}|#{1,6}\s)/.test(line)) {
16
- result.push(indent + line);
17
- continue;
18
- }
19
- const words = line.split(' ');
20
- let currentLine = indent;
21
- for (const word of words) {
22
- if (currentLine.length + word.length + 1 > width) {
23
- if (currentLine.trim()) {
24
- result.push(currentLine);
25
- }
26
- currentLine = indent + word;
27
- } else {
28
- currentLine = currentLine === indent ? indent + word : `${currentLine} ${word}`;
29
- }
30
- }
31
- if (currentLine.trim()) {
32
- result.push(currentLine);
33
- }
34
- }
35
- return result.join('\n');
36
- }
37
-
38
7
  function toArr(any) {
39
8
  return any == null ? [] : Array.isArray(any) ? any : [any];
40
9
  }
@@ -384,10 +353,9 @@ class Command {
384
353
  body: ` $ ${name} ${this.usageText || this.rawName}`
385
354
  });
386
355
  if (!this.isGlobalCommand && !this.isDefaultCommand && this.description) {
387
- const terminalWidth = process.stdout?.columns || 80;
388
356
  sections.push({
389
357
  title: "Description",
390
- body: wrapText(this.description, terminalWidth)
358
+ body: this.description.split("\n").map((line) => ` ${line}`).join("\n")
391
359
  });
392
360
  }
393
361
  const showCommands = (this.isGlobalCommand || this.isDefaultCommand) && commands.length > 0;
@@ -396,8 +364,7 @@ class Command {
396
364
  sections.push({
397
365
  title: "Commands",
398
366
  body: commands.map((command) => {
399
- const firstLine = command.description.split('\n')[0].trim();
400
- return ` ${padRight(command.rawName, longestCommandName.length)} ${firstLine}`;
367
+ return ` ${padRight(command.rawName, longestCommandName.length)} ${command.description}`;
401
368
  }).join("\n")
402
369
  });
403
370
  sections.push({
package/dist/index.mjs CHANGED
@@ -1,36 +1,5 @@
1
1
  import { EventEmitter } from 'events';
2
2
 
3
- function wrapText(text, width = 80, indent = ' ') {
4
- const lines = text.split('\n');
5
- const result = [];
6
- for (const line of lines) {
7
- if (line.trim() === '') {
8
- result.push('');
9
- continue;
10
- }
11
- if (/^(\s*[-*>#`]|\s{4,}|#{1,6}\s)/.test(line)) {
12
- result.push(indent + line);
13
- continue;
14
- }
15
- const words = line.split(' ');
16
- let currentLine = indent;
17
- for (const word of words) {
18
- if (currentLine.length + word.length + 1 > width) {
19
- if (currentLine.trim()) {
20
- result.push(currentLine);
21
- }
22
- currentLine = indent + word;
23
- } else {
24
- currentLine = currentLine === indent ? indent + word : `${currentLine} ${word}`;
25
- }
26
- }
27
- if (currentLine.trim()) {
28
- result.push(currentLine);
29
- }
30
- }
31
- return result.join('\n');
32
- }
33
-
34
3
  function toArr(any) {
35
4
  return any == null ? [] : Array.isArray(any) ? any : [any];
36
5
  }
@@ -380,10 +349,9 @@ class Command {
380
349
  body: ` $ ${name} ${this.usageText || this.rawName}`
381
350
  });
382
351
  if (!this.isGlobalCommand && !this.isDefaultCommand && this.description) {
383
- const terminalWidth = process.stdout?.columns || 80;
384
352
  sections.push({
385
353
  title: "Description",
386
- body: wrapText(this.description, terminalWidth)
354
+ body: this.description.split("\n").map((line) => ` ${line}`).join("\n")
387
355
  });
388
356
  }
389
357
  const showCommands = (this.isGlobalCommand || this.isDefaultCommand) && commands.length > 0;
@@ -392,8 +360,7 @@ class Command {
392
360
  sections.push({
393
361
  title: "Commands",
394
362
  body: commands.map((command) => {
395
- const firstLine = command.description.split('\n')[0].trim();
396
- return ` ${padRight(command.rawName, longestCommandName.length)} ${firstLine}`;
363
+ return ` ${padRight(command.rawName, longestCommandName.length)} ${command.description}`;
397
364
  }).join("\n")
398
365
  });
399
366
  sections.push({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xmorse/cac",
3
- "version": "6.0.1",
3
+ "version": "6.0.3",
4
4
  "description": "Simple yet powerful framework for building command-line apps.",
5
5
  "repository": {
6
6
  "url": "egoist/cac",
@@ -31,7 +31,7 @@
31
31
  "test:cov": "jest --coverage",
32
32
  "build:deno": "node -r sucrase/register scripts/build-deno.ts",
33
33
  "build:node": "rollup -c",
34
- "build": "yarn build:deno && yarn build:node",
34
+ "build": "npm run build:deno && npm run build:node",
35
35
  "toc": "markdown-toc -i README.md",
36
36
  "prepublishOnly": "npm run build && cp mod.js mod.mjs",
37
37
  "docs:api": "typedoc --out api-doc --readme none --exclude \"**/__test__/**\" --theme minimal"