@clankmates/cli 0.5.1 → 0.5.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clankmates/cli",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "devDependencies": {
5
5
  "@types/bun": "1.3.10",
6
6
  "typescript": "^5.9.3"
package/src/cli.ts CHANGED
@@ -50,7 +50,9 @@ export async function runCli(
50
50
  }
51
51
 
52
52
  if (command === "help") {
53
- const helpText = renderHelp(parsed.positionals);
53
+ const helpText = renderHelp(parsed.positionals, {
54
+ boldSectionTitles: shouldBoldHelpSections(io),
55
+ });
54
56
 
55
57
  if (!helpText) {
56
58
  throw new CliError(formatUnknownHelpTopic(parsed.positionals), 2);
@@ -61,12 +63,18 @@ export async function runCli(
61
63
  }
62
64
 
63
65
  if (!command) {
64
- io.stdout(renderHelp([])!);
66
+ io.stdout(
67
+ renderHelp([], {
68
+ boldSectionTitles: shouldBoldHelpSections(io),
69
+ })!,
70
+ );
65
71
  return 0;
66
72
  }
67
73
 
68
74
  if (parsed.flags.help === true) {
69
- const helpText = renderHelp([command, ...parsed.positionals]);
75
+ const helpText = renderHelp([command, ...parsed.positionals], {
76
+ boldSectionTitles: shouldBoldHelpSections(io),
77
+ });
70
78
 
71
79
  if (!helpText) {
72
80
  throw new CliError(
@@ -80,7 +88,11 @@ export async function runCli(
80
88
  }
81
89
 
82
90
  if (resolvesToHelpGroup([command, ...parsed.positionals])) {
83
- io.stdout(renderHelp([command, ...parsed.positionals])!);
91
+ io.stdout(
92
+ renderHelp([command, ...parsed.positionals], {
93
+ boldSectionTitles: shouldBoldHelpSections(io),
94
+ })!,
95
+ );
84
96
  return 0;
85
97
  }
86
98
 
@@ -103,6 +115,26 @@ export async function runCli(
103
115
  }
104
116
  }
105
117
 
118
+ function shouldBoldHelpSections(io: Io): boolean {
119
+ if (io.stdoutIsTTY !== true) {
120
+ return false;
121
+ }
122
+
123
+ if (process.env.NO_COLOR !== undefined) {
124
+ return false;
125
+ }
126
+
127
+ if (process.env.CLICOLOR === "0" || process.env.FORCE_COLOR === "0") {
128
+ return false;
129
+ }
130
+
131
+ if (process.env.TERM === "dumb") {
132
+ return false;
133
+ }
134
+
135
+ return true;
136
+ }
137
+
106
138
  function formatUnknownHelpTopic(path: string[]): string {
107
139
  const topic = path.join(" ");
108
140
  return topic
package/src/lib/help.ts CHANGED
@@ -1,6 +1,12 @@
1
1
  import { CLI_VERSION } from "./version";
2
2
 
3
3
  const CLI_NAME = "clankm";
4
+ const ANSI_BOLD = "\u001b[1m";
5
+ const ANSI_RESET = "\u001b[0m";
6
+
7
+ interface HelpRenderOptions {
8
+ boldSectionTitles?: boolean;
9
+ }
4
10
 
5
11
  interface HelpOption {
6
12
  flag: string;
@@ -945,7 +951,7 @@ const HELP_ROOT = group(
945
951
  ),
946
952
  ],
947
953
  {
948
- description: "Use `--json` for machine consumption and scoped help for everything else.",
954
+ description: "Work with clankmates.com from the command line.",
949
955
  usage: [`${CLI_NAME} <command>`, `${CLI_NAME} help <command-path>`],
950
956
  examples: [
951
957
  `${CLI_NAME}`,
@@ -963,7 +969,10 @@ const HELP_ROOT = group(
963
969
  },
964
970
  );
965
971
 
966
- export function renderHelp(path: string[]): string | undefined {
972
+ export function renderHelp(
973
+ path: string[],
974
+ options: HelpRenderOptions = {},
975
+ ): string | undefined {
967
976
  const resolved = resolveHelpNode(path);
968
977
 
969
978
  if (!resolved) {
@@ -971,8 +980,8 @@ export function renderHelp(path: string[]): string | undefined {
971
980
  }
972
981
 
973
982
  return resolved.node.kind === "group"
974
- ? renderGroupHelp(resolved.node, resolved.path)
975
- : renderCommandHelp(resolved.node, resolved.path);
983
+ ? renderGroupHelp(resolved.node, resolved.path, options)
984
+ : renderCommandHelp(resolved.node, resolved.path, options);
976
985
  }
977
986
 
978
987
  export function resolvesToHelpGroup(path: string[]): boolean {
@@ -1010,7 +1019,11 @@ function matchesSegment(node: HelpNode, segment: string): boolean {
1010
1019
  return node.name === segment || node.aliases?.includes(segment) === true;
1011
1020
  }
1012
1021
 
1013
- function renderGroupHelp(node: HelpGroup, path: string[]): string {
1022
+ function renderGroupHelp(
1023
+ node: HelpGroup,
1024
+ path: string[],
1025
+ options: HelpRenderOptions,
1026
+ ): string {
1014
1027
  const title = path.length === 0 ? `${CLI_NAME} ${CLI_VERSION}` : `${CLI_NAME} ${path.join(" ")}`;
1015
1028
  const sections: string[] = [title];
1016
1029
 
@@ -1021,26 +1034,32 @@ function renderGroupHelp(node: HelpGroup, path: string[]): string {
1021
1034
  }
1022
1035
 
1023
1036
  if (node.usage && node.usage.length > 0) {
1024
- sections.push(renderUsageSection(node.usage));
1037
+ sections.push(renderUsageSection(node.usage, options));
1025
1038
  }
1026
1039
 
1027
1040
  if (node.aliases && node.aliases.length > 0) {
1028
- sections.push(renderLineListSection("Aliases", node.aliases));
1041
+ sections.push(renderLineListSection("Aliases", node.aliases, false, options));
1029
1042
  }
1030
1043
 
1031
1044
  const childHeading = path.length === 0 ? "Commands" : "Subcommands";
1032
- sections.push(renderEntrySection(childHeading, node.children));
1045
+ sections.push(renderEntrySection(childHeading, node.children, options));
1033
1046
 
1034
1047
  if (node.options && node.options.length > 0) {
1035
- sections.push(renderOptionSection(path.length === 0 ? "Global Flags" : "Options", node.options));
1048
+ sections.push(
1049
+ renderOptionSection(
1050
+ path.length === 0 ? "Global Flags" : "Options",
1051
+ node.options,
1052
+ options,
1053
+ ),
1054
+ );
1036
1055
  }
1037
1056
 
1038
1057
  if (node.examples && node.examples.length > 0) {
1039
- sections.push(renderLineListSection("Examples", node.examples, true));
1058
+ sections.push(renderLineListSection("Examples", node.examples, true, options));
1040
1059
  }
1041
1060
 
1042
1061
  if (node.notes && node.notes.length > 0) {
1043
- sections.push(renderLineListSection("Notes", node.notes));
1062
+ sections.push(renderLineListSection("Notes", node.notes, false, options));
1044
1063
  }
1045
1064
 
1046
1065
  if (path.length === 0) {
@@ -1052,43 +1071,56 @@ function renderGroupHelp(node: HelpGroup, path: string[]): string {
1052
1071
  return sections.join("\n\n");
1053
1072
  }
1054
1073
 
1055
- function renderCommandHelp(node: HelpCommand, path: string[]): string {
1074
+ function renderCommandHelp(
1075
+ node: HelpCommand,
1076
+ path: string[],
1077
+ options: HelpRenderOptions,
1078
+ ): string {
1056
1079
  const title = `${CLI_NAME} ${path.join(" ")}`;
1057
1080
  const sections: string[] = [title, node.description ?? node.summary];
1058
1081
 
1059
1082
  if (node.aliases && node.aliases.length > 0) {
1060
- sections.push(renderLineListSection("Aliases", node.aliases));
1083
+ sections.push(renderLineListSection("Aliases", node.aliases, false, options));
1061
1084
  }
1062
1085
 
1063
1086
  if (node.usage && node.usage.length > 0) {
1064
- sections.push(renderUsageSection(node.usage));
1087
+ sections.push(renderUsageSection(node.usage, options));
1065
1088
  }
1066
1089
 
1067
1090
  if (node.options && node.options.length > 0) {
1068
- sections.push(renderOptionSection("Options", node.options));
1091
+ sections.push(renderOptionSection("Options", node.options, options));
1069
1092
  }
1070
1093
 
1071
1094
  if (node.examples && node.examples.length > 0) {
1072
- sections.push(renderLineListSection("Examples", node.examples, true));
1095
+ sections.push(renderLineListSection("Examples", node.examples, true, options));
1073
1096
  }
1074
1097
 
1075
1098
  if (node.notes && node.notes.length > 0) {
1076
- sections.push(renderLineListSection("Notes", node.notes));
1099
+ sections.push(renderLineListSection("Notes", node.notes, false, options));
1077
1100
  }
1078
1101
 
1079
1102
  return sections.join("\n\n");
1080
1103
  }
1081
1104
 
1082
- function renderUsageSection(usage: string[]): string {
1083
- return ["Usage:", ...usage.map((line) => ` ${line}`)].join("\n");
1105
+ function renderUsageSection(
1106
+ usage: string[],
1107
+ options: HelpRenderOptions,
1108
+ ): string {
1109
+ return [formatSectionTitle("USAGE", options), ...usage.map((line) => ` ${line}`)].join(
1110
+ "\n",
1111
+ );
1084
1112
  }
1085
1113
 
1086
- function renderEntrySection(title: string, entries: HelpNode[]): string {
1087
- const labels = entries.map((entry) => formatEntryLabel(entry));
1114
+ function renderEntrySection(
1115
+ title: string,
1116
+ entries: HelpNode[],
1117
+ options: HelpRenderOptions,
1118
+ ): string {
1119
+ const labels = entries.map((entry) => `${formatEntryLabel(entry)}:`);
1088
1120
  const width = labels.reduce((current, label) => Math.max(current, label.length), 0);
1089
1121
 
1090
1122
  return [
1091
- `${title}:`,
1123
+ formatSectionTitle(title, options),
1092
1124
  ...entries.map((entry, index) => {
1093
1125
  const label = labels[index]!.padEnd(width, " ");
1094
1126
  return ` ${label} ${entry.summary}`;
@@ -1096,11 +1128,15 @@ function renderEntrySection(title: string, entries: HelpNode[]): string {
1096
1128
  ].join("\n");
1097
1129
  }
1098
1130
 
1099
- function renderOptionSection(title: string, options: HelpOption[]): string {
1131
+ function renderOptionSection(
1132
+ title: string,
1133
+ options: HelpOption[],
1134
+ renderOptions: HelpRenderOptions,
1135
+ ): string {
1100
1136
  const width = options.reduce((current, entry) => Math.max(current, entry.flag.length), 0);
1101
1137
 
1102
1138
  return [
1103
- `${title}:`,
1139
+ formatSectionTitle(title, renderOptions),
1104
1140
  ...options.map((entry) => ` ${entry.flag.padEnd(width, " ")} ${entry.description}`),
1105
1141
  ].join("\n");
1106
1142
  }
@@ -1109,9 +1145,10 @@ function renderLineListSection(
1109
1145
  title: string,
1110
1146
  lines: string[],
1111
1147
  code = false,
1148
+ options: HelpRenderOptions,
1112
1149
  ): string {
1113
1150
  return [
1114
- `${title}:`,
1151
+ formatSectionTitle(title, options),
1115
1152
  ...lines.map((line) => ` ${code ? line : line}`),
1116
1153
  ].join("\n");
1117
1154
  }
@@ -1121,3 +1158,11 @@ function formatEntryLabel(entry: HelpNode): string {
1121
1158
  ? `${entry.name}, ${entry.aliases.join(", ")}`
1122
1159
  : entry.name;
1123
1160
  }
1161
+
1162
+ function formatSectionTitle(
1163
+ title: string,
1164
+ options: HelpRenderOptions = {},
1165
+ ): string {
1166
+ const heading = title.toUpperCase();
1167
+ return options.boldSectionTitles ? `${ANSI_BOLD}${heading}${ANSI_RESET}` : heading;
1168
+ }
package/src/lib/output.ts CHANGED
@@ -3,12 +3,16 @@ import type { OutputMode } from "../types/api";
3
3
  export interface Io {
4
4
  stdout: (message: string) => void;
5
5
  stderr: (message: string) => void;
6
+ stdoutIsTTY?: boolean;
7
+ stderrIsTTY?: boolean;
6
8
  }
7
9
 
8
10
  export function defaultIo(): Io {
9
11
  return {
10
12
  stdout: (message) => process.stdout.write(`${message}\n`),
11
- stderr: (message) => process.stderr.write(`${message}\n`)
13
+ stderr: (message) => process.stderr.write(`${message}\n`),
14
+ stdoutIsTTY: process.stdout.isTTY,
15
+ stderrIsTTY: process.stderr.isTTY,
12
16
  };
13
17
  }
14
18