@xmorse/cac 6.0.0 → 6.0.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/deno/CAC.ts CHANGED
@@ -160,27 +160,18 @@ 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
-
170
163
  // Search sub-commands
171
- for (const command of sortedCommands) {
164
+ for (const command of this.commands) {
172
165
  const parsed = this.mri(argv.slice(2), command);
173
- const result = command.isMatched((parsed.args as string[]));
174
- if (result.matched) {
166
+ const commandName = parsed.args[0];
167
+ if (command.isMatched(commandName)) {
175
168
  shouldParse = false;
176
- const matchedCommandName = parsed.args.slice(0, result.consumedArgs).join(' ');
177
169
  const parsedInfo = {
178
170
  ...parsed,
179
- args: parsed.args.slice(result.consumedArgs)
171
+ args: parsed.args.slice(1)
180
172
  };
181
- this.setParsedInfo(parsedInfo, command, matchedCommandName);
182
- this.emit(`command:${matchedCommandName}`, command);
183
- break; // Stop after first match (greedy matching)
173
+ this.setParsedInfo(parsedInfo, command, commandName);
174
+ this.emit(`command:${commandName}`, command);
184
175
  }
185
176
  }
186
177
  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 } from "./utils.ts";
3
+ import { removeBrackets, findAllBrackets, findLongest, padRight, CACError, wrapText } from "./utils.ts";
4
4
  import { platformInfo } from "./deno.ts";
5
5
  interface CommandArg {
6
6
  required: boolean;
@@ -77,38 +77,13 @@ class Command {
77
77
  this.commandAction = callback;
78
78
  return this;
79
79
  }
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
- };
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);
112
87
  }
113
88
  get isDefaultCommand() {
114
89
  return this.name === '' || this.aliasNames.includes('!');
@@ -144,13 +119,24 @@ class Command {
144
119
  title: 'Usage',
145
120
  body: ` $ ${name} ${this.usageText || this.rawName}`
146
121
  });
122
+
123
+ // Show description for specific commands (not global/default)
124
+ if (!this.isGlobalCommand && !this.isDefaultCommand && this.description) {
125
+ const terminalWidth = process.stdout?.columns || 80;
126
+ sections.push({
127
+ title: 'Description',
128
+ body: wrapText(this.description, terminalWidth)
129
+ });
130
+ }
147
131
  const showCommands = (this.isGlobalCommand || this.isDefaultCommand) && commands.length > 0;
148
132
  if (showCommands) {
149
133
  const longestCommandName = findLongest(commands.map(command => command.rawName));
150
134
  sections.push({
151
135
  title: 'Commands',
152
136
  body: commands.map(command => {
153
- return ` ${padRight(command.rawName, longestCommandName.length)} ${command.description}`;
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}`;
154
140
  }).join('\n')
155
141
  });
156
142
  sections.push({
package/deno/utils.ts CHANGED
@@ -126,4 +126,44 @@ export class CACError extends Error {
126
126
  this.stack = new Error(message).stack;
127
127
  }
128
128
  }
129
- }
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
+ };
package/dist/index.js CHANGED
@@ -4,6 +4,37 @@ 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
+
7
38
  function toArr(any) {
8
39
  return any == null ? [] : Array.isArray(any) ? any : [any];
9
40
  }
@@ -352,13 +383,21 @@ class Command {
352
383
  title: "Usage",
353
384
  body: ` $ ${name} ${this.usageText || this.rawName}`
354
385
  });
386
+ if (!this.isGlobalCommand && !this.isDefaultCommand && this.description) {
387
+ const terminalWidth = process.stdout?.columns || 80;
388
+ sections.push({
389
+ title: "Description",
390
+ body: wrapText(this.description, terminalWidth)
391
+ });
392
+ }
355
393
  const showCommands = (this.isGlobalCommand || this.isDefaultCommand) && commands.length > 0;
356
394
  if (showCommands) {
357
395
  const longestCommandName = findLongest(commands.map((command) => command.rawName));
358
396
  sections.push({
359
397
  title: "Commands",
360
398
  body: commands.map((command) => {
361
- return ` ${padRight(command.rawName, longestCommandName.length)} ${command.description}`;
399
+ const firstLine = command.description.split('\n')[0].trim();
400
+ return ` ${padRight(command.rawName, longestCommandName.length)} ${firstLine}`;
362
401
  }).join("\n")
363
402
  });
364
403
  sections.push({
package/dist/index.mjs CHANGED
@@ -1,5 +1,36 @@
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
+
3
34
  function toArr(any) {
4
35
  return any == null ? [] : Array.isArray(any) ? any : [any];
5
36
  }
@@ -348,13 +379,21 @@ class Command {
348
379
  title: "Usage",
349
380
  body: ` $ ${name} ${this.usageText || this.rawName}`
350
381
  });
382
+ if (!this.isGlobalCommand && !this.isDefaultCommand && this.description) {
383
+ const terminalWidth = process.stdout?.columns || 80;
384
+ sections.push({
385
+ title: "Description",
386
+ body: wrapText(this.description, terminalWidth)
387
+ });
388
+ }
351
389
  const showCommands = (this.isGlobalCommand || this.isDefaultCommand) && commands.length > 0;
352
390
  if (showCommands) {
353
391
  const longestCommandName = findLongest(commands.map((command) => command.rawName));
354
392
  sections.push({
355
393
  title: "Commands",
356
394
  body: commands.map((command) => {
357
- return ` ${padRight(command.rawName, longestCommandName.length)} ${command.description}`;
395
+ const firstLine = command.description.split('\n')[0].trim();
396
+ return ` ${padRight(command.rawName, longestCommandName.length)} ${firstLine}`;
358
397
  }).join("\n")
359
398
  });
360
399
  sections.push({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xmorse/cac",
3
- "version": "6.0.0",
3
+ "version": "6.0.1",
4
4
  "description": "Simple yet powerful framework for building command-line apps.",
5
5
  "repository": {
6
6
  "url": "egoist/cac",
@@ -26,6 +26,16 @@
26
26
  "/deno",
27
27
  "/index-compat.js"
28
28
  ],
29
+ "scripts": {
30
+ "test": "jest",
31
+ "test:cov": "jest --coverage",
32
+ "build:deno": "node -r sucrase/register scripts/build-deno.ts",
33
+ "build:node": "rollup -c",
34
+ "build": "yarn build:deno && yarn build:node",
35
+ "toc": "markdown-toc -i README.md",
36
+ "prepublishOnly": "npm run build && cp mod.js mod.mjs",
37
+ "docs:api": "typedoc --out api-doc --readme none --exclude \"**/__test__/**\" --theme minimal"
38
+ },
29
39
  "author": "egoist <0x142857@gmail.com>",
30
40
  "license": "MIT",
31
41
  "devDependencies": {
@@ -90,14 +100,5 @@
90
100
  "hooks": {
91
101
  "pre-commit": "npm t && lint-staged"
92
102
  }
93
- },
94
- "scripts": {
95
- "test": "jest",
96
- "test:cov": "jest --coverage",
97
- "build:deno": "node -r sucrase/register scripts/build-deno.ts",
98
- "build:node": "rollup -c",
99
- "build": "yarn build:deno && yarn build:node",
100
- "toc": "markdown-toc -i README.md",
101
- "docs:api": "typedoc --out api-doc --readme none --exclude \"**/__test__/**\" --theme minimal"
102
103
  }
103
- }
104
+ }