@lewishowles/cli-style 0.4.0 → 0.4.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/CHANGELOG.md +7 -0
- package/README.md +14 -10
- package/examples/diagnostic.mjs +8 -6
- package/package.json +2 -1
- package/src/cli/gallery-command.js +6 -16
- package/src/cli/render-command.js +66 -58
- package/src/create-cli-style.js +5 -4
- package/src/gallery/render-gallery.js +206 -198
- package/src/index.js +1 -5
- package/src/patterns/agent-transcript.js +6 -20
- package/src/patterns/audit-finding.js +1 -0
- package/src/patterns/command-result.js +7 -4
- package/src/patterns/compact-data-table.js +7 -8
- package/src/patterns/confirmation-result.js +4 -10
- package/src/patterns/diagnostic-report.js +16 -28
- package/src/patterns/helpers.js +4 -6
- package/src/patterns/next-step-block.js +8 -8
- package/src/patterns/task-summary.js +1 -0
- package/src/primitives/bar-chart.js +13 -13
- package/src/primitives/panel.js +5 -11
- package/src/primitives/status.js +4 -3
- package/src/primitives/step-progress.js +8 -6
- package/src/primitives/table.js +49 -42
- package/src/reporters/create-reporter.js +41 -20
- package/src/theme/results.js +1 -1
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
import { profiles } from "../profiles/profiles.js";
|
|
2
2
|
import { status } from "../primitives/status.js";
|
|
3
3
|
import { resultTypes } from "../theme/results.js";
|
|
4
|
-
import {
|
|
5
|
-
formatLabel,
|
|
6
|
-
isNonEmptyString,
|
|
7
|
-
isRecord,
|
|
8
|
-
renderSection,
|
|
9
|
-
renderTitle,
|
|
10
|
-
} from "./helpers.js";
|
|
4
|
+
import { formatLabel, isNonEmptyString, isRecord, renderSection, renderTitle } from "./helpers.js";
|
|
11
5
|
|
|
12
6
|
// Diagnostic reports use a stable title when the caller does not provide one.
|
|
13
7
|
const defaultTitle = "Diagnostic report";
|
|
@@ -49,9 +43,10 @@ export function diagnosticReport(report, options = {}) {
|
|
|
49
43
|
renderSkippedChecks(report.skippedChecks, options),
|
|
50
44
|
renderNextActions(report.nextActions, options),
|
|
51
45
|
].filter((section) => section !== "");
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
: defaultTitle;
|
|
46
|
+
|
|
47
|
+
const title =
|
|
48
|
+
typeof report.title === "string" && report.title !== "" ? report.title : defaultTitle;
|
|
49
|
+
|
|
55
50
|
return [renderTitle(title, options), ...sections].join("\n\n");
|
|
56
51
|
}
|
|
57
52
|
|
|
@@ -69,11 +64,10 @@ function renderChecks(checks, options) {
|
|
|
69
64
|
const validChecks = Array.isArray(checks)
|
|
70
65
|
? checks.filter((check) => isRecord(check) && isNonEmptyString(check.name))
|
|
71
66
|
: [];
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
formatLabel(check.name, check.detail, options),
|
|
75
|
-
|
|
76
|
-
));
|
|
67
|
+
|
|
68
|
+
const lines = validChecks.map((check) =>
|
|
69
|
+
status(check.result, formatLabel(check.name, check.detail, options), options),
|
|
70
|
+
);
|
|
77
71
|
|
|
78
72
|
return renderSection("Checks", lines, options);
|
|
79
73
|
}
|
|
@@ -92,11 +86,8 @@ function renderFindings(findings, options) {
|
|
|
92
86
|
const validFindings = Array.isArray(findings)
|
|
93
87
|
? findings.filter((finding) => isRecord(finding) && isNonEmptyString(finding.message))
|
|
94
88
|
: [];
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
finding.message,
|
|
98
|
-
options,
|
|
99
|
-
));
|
|
89
|
+
|
|
90
|
+
const lines = validFindings.map((finding) => status(finding.result, finding.message, options));
|
|
100
91
|
|
|
101
92
|
return renderSection("Findings", lines, options);
|
|
102
93
|
}
|
|
@@ -115,11 +106,10 @@ function renderSkippedChecks(skippedChecks, options) {
|
|
|
115
106
|
const validChecks = Array.isArray(skippedChecks)
|
|
116
107
|
? skippedChecks.filter((check) => isRecord(check) && isNonEmptyString(check.name))
|
|
117
108
|
: [];
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
formatLabel(check.name, check.reason, options),
|
|
121
|
-
|
|
122
|
-
));
|
|
109
|
+
|
|
110
|
+
const lines = validChecks.map((check) =>
|
|
111
|
+
status(resultTypes.SKIPPED, formatLabel(check.name, check.reason, options), options),
|
|
112
|
+
);
|
|
123
113
|
|
|
124
114
|
return renderSection("Skipped checks", lines, options);
|
|
125
115
|
}
|
|
@@ -135,9 +125,7 @@ function renderSkippedChecks(skippedChecks, options) {
|
|
|
135
125
|
* Rendered next-action section.
|
|
136
126
|
*/
|
|
137
127
|
function renderNextActions(nextActions, options) {
|
|
138
|
-
const validActions = Array.isArray(nextActions)
|
|
139
|
-
? nextActions.filter(isNonEmptyString)
|
|
140
|
-
: [];
|
|
128
|
+
const validActions = Array.isArray(nextActions) ? nextActions.filter(isNonEmptyString) : [];
|
|
141
129
|
const lines = validActions.map((action, index) => `${index + 1}. ${action}`);
|
|
142
130
|
|
|
143
131
|
return renderSection("Next actions", lines, options, false);
|
package/src/patterns/helpers.js
CHANGED
|
@@ -81,12 +81,10 @@ export function renderSection(title, lines, options, listItems = true) {
|
|
|
81
81
|
return "";
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
const heading = options.profile === profiles.AGENT
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
? lines.map((line) => `- ${line}`)
|
|
89
|
-
: lines;
|
|
84
|
+
const heading = options.profile === profiles.AGENT ? `## ${title}` : title;
|
|
85
|
+
|
|
86
|
+
const content =
|
|
87
|
+
options.profile === profiles.AGENT && listItems ? lines.map((line) => `- ${line}`) : lines;
|
|
90
88
|
|
|
91
89
|
return [heading, ...content].join("\n");
|
|
92
90
|
}
|
|
@@ -42,12 +42,17 @@ export function nextStepBlock(nextStep, options = {}) {
|
|
|
42
42
|
|
|
43
43
|
const title = isNonEmptyString(nextStep.title) ? nextStep.title : defaultTitle;
|
|
44
44
|
const next = renderNext(nextStep.next, options);
|
|
45
|
+
|
|
45
46
|
const reason = isNonEmptyString(nextStep.reason)
|
|
46
47
|
? renderSection("Why", [nextStep.reason], options, false)
|
|
47
48
|
: "";
|
|
49
|
+
|
|
48
50
|
const commands = renderCommands(nextStep.commands, options);
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
|
|
52
|
+
const alternatives = normaliseStringList(nextStep.alternatives).map(
|
|
53
|
+
(alternative) => `- ${alternative}`,
|
|
54
|
+
);
|
|
55
|
+
|
|
51
56
|
const sections = [
|
|
52
57
|
next,
|
|
53
58
|
reason,
|
|
@@ -101,12 +106,7 @@ function renderCommands(commands, options) {
|
|
|
101
106
|
}
|
|
102
107
|
|
|
103
108
|
if (options.profile === profiles.AGENT) {
|
|
104
|
-
return [
|
|
105
|
-
"## Commands",
|
|
106
|
-
"```sh",
|
|
107
|
-
...validCommands,
|
|
108
|
-
"```",
|
|
109
|
-
].join("\n");
|
|
109
|
+
return ["## Commands", "```sh", ...validCommands, "```"].join("\n");
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
return renderSection(
|
|
@@ -49,6 +49,7 @@ export function taskSummary(summary, options = {}) {
|
|
|
49
49
|
const detail = formatLabel(task, summary.summary, options);
|
|
50
50
|
const completed = normaliseStringList(summary.completed).map((item) => `- ${item}`);
|
|
51
51
|
const remaining = normaliseStringList(summary.remaining).map((item) => `- ${item}`);
|
|
52
|
+
|
|
52
53
|
const sections = [
|
|
53
54
|
status(summary.result ?? resultTypes.UNKNOWN, detail, options),
|
|
54
55
|
renderSection("Completed", completed, options, false),
|
|
@@ -31,9 +31,7 @@ export function barChart(options = {}) {
|
|
|
31
31
|
const maximum = Math.max(...rows.map((row) => Math.abs(row.value)), 1);
|
|
32
32
|
const width = normaliseWidth(options.barWidth);
|
|
33
33
|
|
|
34
|
-
return rows
|
|
35
|
-
.map((row) => renderRow(row, labelWidth, maximum, width, options))
|
|
36
|
-
.join("\n");
|
|
34
|
+
return rows.map((row) => renderRow(row, labelWidth, maximum, width, options)).join("\n");
|
|
37
35
|
}
|
|
38
36
|
|
|
39
37
|
/**
|
|
@@ -49,13 +47,14 @@ function normaliseRows(rows) {
|
|
|
49
47
|
return [];
|
|
50
48
|
}
|
|
51
49
|
|
|
52
|
-
return rows.filter(
|
|
53
|
-
row
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
50
|
+
return rows.filter(
|
|
51
|
+
(row) =>
|
|
52
|
+
row !== null &&
|
|
53
|
+
typeof row === "object" &&
|
|
54
|
+
typeof row.label === "string" &&
|
|
55
|
+
row.label !== "" &&
|
|
56
|
+
Number.isFinite(row.value),
|
|
57
|
+
);
|
|
59
58
|
}
|
|
60
59
|
|
|
61
60
|
/**
|
|
@@ -101,9 +100,10 @@ function renderRow(row, labelWidth, maximum, width, options) {
|
|
|
101
100
|
}
|
|
102
101
|
|
|
103
102
|
const renderedLabel = foreground(label, chartColours.label, options);
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
: foreground(bar.slice(0, barWidth), barColour(row), options);
|
|
103
|
+
|
|
104
|
+
const renderedBar =
|
|
105
|
+
barWidth === 0 ? "" : foreground(bar.slice(0, barWidth), barColour(row), options);
|
|
106
|
+
|
|
107
107
|
const barPadding = bar.slice(barWidth);
|
|
108
108
|
const renderedValue = foreground(String(row.value), chartColours.value, options);
|
|
109
109
|
|
package/src/primitives/panel.js
CHANGED
|
@@ -34,22 +34,16 @@ export function panel(options = {}) {
|
|
|
34
34
|
const lines = options.lines ?? [];
|
|
35
35
|
|
|
36
36
|
if (options.profile === profiles.CI) {
|
|
37
|
-
return [
|
|
38
|
-
...(options.title === undefined ? [] : [options.title]),
|
|
39
|
-
...lines,
|
|
40
|
-
].join("\n");
|
|
37
|
+
return [...(options.title === undefined ? [] : [options.title]), ...lines].join("\n");
|
|
41
38
|
}
|
|
42
39
|
|
|
43
40
|
const width = Math.max(options.panelWidth ?? defaultWidth, 4);
|
|
44
|
-
const panelLines = [
|
|
45
|
-
"",
|
|
46
|
-
...(options.title === undefined ? [] : [options.title]),
|
|
47
|
-
...lines,
|
|
48
|
-
"",
|
|
49
|
-
];
|
|
41
|
+
const panelLines = ["", ...(options.title === undefined ? [] : [options.title]), ...lines, ""];
|
|
50
42
|
|
|
51
43
|
return panelLines
|
|
52
|
-
.map((line, index) =>
|
|
44
|
+
.map((line, index) =>
|
|
45
|
+
renderLine(line, width, index === 1 && options.title !== undefined, options),
|
|
46
|
+
)
|
|
53
47
|
.join("\n");
|
|
54
48
|
}
|
|
55
49
|
|
package/src/primitives/status.js
CHANGED
|
@@ -22,9 +22,10 @@ import { getResultSymbol, getResultToken, resultTypes } from "../theme/results.j
|
|
|
22
22
|
export function status(resultType = resultTypes.UNKNOWN, detail = "", options = {}) {
|
|
23
23
|
const token = getResultToken(resultType);
|
|
24
24
|
const symbol = getResultSymbol(resultType, options);
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
: token.label;
|
|
25
|
+
|
|
26
|
+
const label =
|
|
27
|
+
typeof options.label === "string" && options.label !== "" ? options.label : token.label;
|
|
28
|
+
|
|
28
29
|
const prefix = `${symbol} ${label}`;
|
|
29
30
|
const detailText = detail === "" ? "" : ` ${detail}`;
|
|
30
31
|
|
|
@@ -67,12 +67,14 @@ export function stepProgress(options = {}) {
|
|
|
67
67
|
|
|
68
68
|
const current = normaliseCurrent(options.current, steps.length);
|
|
69
69
|
|
|
70
|
-
return steps
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
70
|
+
return steps
|
|
71
|
+
.map((label, index) => {
|
|
72
|
+
const state = stateForIndex(index, current);
|
|
73
|
+
const count = `${index + 1}/${steps.length}`;
|
|
74
|
+
|
|
75
|
+
return `${count} ${step(label, state, options)}`;
|
|
76
|
+
})
|
|
77
|
+
.join("\n");
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
/**
|
package/src/primitives/table.js
CHANGED
|
@@ -34,8 +34,11 @@ export function table(options = {}) {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
const widths = columnWidths(columns, rows);
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
|
|
38
|
+
const naturalWidth =
|
|
39
|
+
widths.reduce((total, width) => total + width, 0) +
|
|
40
|
+
columnSeparator.length * (columns.length - 1);
|
|
41
|
+
|
|
39
42
|
const availableWidth = Number.isFinite(options.width) ? options.width : defaultWidth;
|
|
40
43
|
|
|
41
44
|
if (naturalWidth > availableWidth) {
|
|
@@ -58,14 +61,15 @@ function normaliseColumns(columns) {
|
|
|
58
61
|
return [];
|
|
59
62
|
}
|
|
60
63
|
|
|
61
|
-
return columns.filter(
|
|
62
|
-
column
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
return columns.filter(
|
|
65
|
+
(column) =>
|
|
66
|
+
column !== null &&
|
|
67
|
+
typeof column === "object" &&
|
|
68
|
+
typeof column.key === "string" &&
|
|
69
|
+
column.key !== "" &&
|
|
70
|
+
typeof column.label === "string" &&
|
|
71
|
+
column.label !== "",
|
|
72
|
+
);
|
|
69
73
|
}
|
|
70
74
|
|
|
71
75
|
/**
|
|
@@ -95,10 +99,9 @@ function normaliseRows(rows) {
|
|
|
95
99
|
* Column widths.
|
|
96
100
|
*/
|
|
97
101
|
function columnWidths(columns, rows) {
|
|
98
|
-
return columns.map((column) =>
|
|
99
|
-
column.label.length,
|
|
100
|
-
|
|
101
|
-
));
|
|
102
|
+
return columns.map((column) =>
|
|
103
|
+
Math.max(column.label.length, ...rows.map((row) => cellValue(row[column.key]).length)),
|
|
104
|
+
);
|
|
102
105
|
}
|
|
103
106
|
|
|
104
107
|
/**
|
|
@@ -117,25 +120,28 @@ function columnWidths(columns, rows) {
|
|
|
117
120
|
*/
|
|
118
121
|
function renderTable(columns, rows, widths, options) {
|
|
119
122
|
const header = columns
|
|
120
|
-
.map((column, index) =>
|
|
121
|
-
padColumn(column.label, widths[index], index, columns.length),
|
|
122
|
-
|
|
123
|
-
))
|
|
123
|
+
.map((column, index) =>
|
|
124
|
+
renderHeader(padColumn(column.label, widths[index], index, columns.length), options),
|
|
125
|
+
)
|
|
124
126
|
.join(columnSeparator);
|
|
127
|
+
|
|
125
128
|
const ruleCharacter = options.unicode === false ? "-" : "─";
|
|
126
|
-
const rule = widths
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
129
|
+
const rule = widths.map((width) => ruleCharacter.repeat(width)).join(columnSeparator);
|
|
130
|
+
|
|
131
|
+
const renderedRule =
|
|
132
|
+
options.colour === true ? foreground(rule, tableColours.border, options) : rule;
|
|
133
|
+
|
|
134
|
+
const body = rows.map((row) =>
|
|
135
|
+
columns
|
|
136
|
+
.map((column, index) =>
|
|
137
|
+
renderCell(
|
|
138
|
+
padColumn(cellValue(row[column.key]), widths[index], index, columns.length),
|
|
139
|
+
index,
|
|
140
|
+
options,
|
|
141
|
+
),
|
|
142
|
+
)
|
|
143
|
+
.join(columnSeparator),
|
|
144
|
+
);
|
|
139
145
|
|
|
140
146
|
return [header, renderedRule, ...body].join("\n");
|
|
141
147
|
}
|
|
@@ -173,14 +179,17 @@ function padColumn(value, width, index, columnCount) {
|
|
|
173
179
|
function renderBlocks(columns, rows, options) {
|
|
174
180
|
const labelWidth = Math.max(...columns.map((column) => column.label.length));
|
|
175
181
|
|
|
176
|
-
return rows
|
|
177
|
-
.map((
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
182
|
+
return rows
|
|
183
|
+
.map((row) =>
|
|
184
|
+
columns
|
|
185
|
+
.map((column) => {
|
|
186
|
+
const label = renderHeader(column.label.padEnd(labelWidth, " "), options);
|
|
187
|
+
const value = renderCell(cellValue(row[column.key]), 0, options);
|
|
188
|
+
|
|
189
|
+
return `${label}${columnSeparator}${value}`;
|
|
190
|
+
})
|
|
191
|
+
.join("\n"),
|
|
192
|
+
)
|
|
184
193
|
.join("\n\n");
|
|
185
194
|
}
|
|
186
195
|
|
|
@@ -231,9 +240,7 @@ function renderCell(value, columnIndex, options) {
|
|
|
231
240
|
return value;
|
|
232
241
|
}
|
|
233
242
|
|
|
234
|
-
const colour = columnIndex === 0
|
|
235
|
-
? tableColours.primaryText
|
|
236
|
-
: tableColours.secondaryText;
|
|
243
|
+
const colour = columnIndex === 0 ? tableColours.primaryText : tableColours.secondaryText;
|
|
237
244
|
|
|
238
245
|
return foreground(value, colour, options);
|
|
239
246
|
}
|
|
@@ -30,27 +30,44 @@ export function createReporter(options = {}) {
|
|
|
30
30
|
...reporterDefaults,
|
|
31
31
|
...options,
|
|
32
32
|
};
|
|
33
|
+
|
|
33
34
|
const lines = [];
|
|
34
35
|
|
|
35
36
|
return {
|
|
36
|
-
divider: (label, detail = "", dividerOptions = {}) =>
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
divider: (label, detail = "", dividerOptions = {}) =>
|
|
38
|
+
append(
|
|
39
|
+
lines,
|
|
40
|
+
renderReporterDivider(label, detail, {
|
|
41
|
+
...reporterOptions,
|
|
42
|
+
...dividerOptions,
|
|
43
|
+
}),
|
|
44
|
+
),
|
|
45
|
+
group: (label, items = [], groupOptions = {}) =>
|
|
46
|
+
append(
|
|
47
|
+
lines,
|
|
48
|
+
renderGroup(label, items, {
|
|
49
|
+
...reporterOptions,
|
|
50
|
+
...groupOptions,
|
|
51
|
+
}),
|
|
52
|
+
),
|
|
44
53
|
lines,
|
|
45
54
|
render: () => lines.join("\n"),
|
|
46
|
-
section: (label, detail = "", sectionOptions = {}) =>
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
55
|
+
section: (label, detail = "", sectionOptions = {}) =>
|
|
56
|
+
append(
|
|
57
|
+
lines,
|
|
58
|
+
renderSection(label, detail, {
|
|
59
|
+
...reporterOptions,
|
|
60
|
+
...sectionOptions,
|
|
61
|
+
}),
|
|
62
|
+
),
|
|
63
|
+
status: (resultType, label, detail = "", statusOptions = {}) =>
|
|
64
|
+
append(
|
|
65
|
+
lines,
|
|
66
|
+
renderReporterStatus(resultType, label, detail, {
|
|
67
|
+
...reporterOptions,
|
|
68
|
+
...statusOptions,
|
|
69
|
+
}),
|
|
70
|
+
),
|
|
54
71
|
};
|
|
55
72
|
}
|
|
56
73
|
|
|
@@ -87,7 +104,10 @@ export function renderReporterDivider(label, detail = "", options = {}) {
|
|
|
87
104
|
*/
|
|
88
105
|
export function renderGroup(label, items = [], options = {}) {
|
|
89
106
|
const normalisedItems = normaliseItems(items);
|
|
90
|
-
|
|
107
|
+
|
|
108
|
+
const resultType =
|
|
109
|
+
options.result ?? getHighestSeverityResult(normalisedItems.map((item) => item.result));
|
|
110
|
+
|
|
91
111
|
const summary = options.summary ?? summariseItems(normalisedItems);
|
|
92
112
|
const groupLine = renderReporterStatus(resultType, label, summary, options);
|
|
93
113
|
|
|
@@ -193,9 +213,10 @@ function normaliseItems(items) {
|
|
|
193
213
|
.map((item) => ({
|
|
194
214
|
detail: isNonEmptyString(item.detail) ? item.detail : "",
|
|
195
215
|
label: isNonEmptyString(item.label) ? item.label : getResultToken(item.result).label,
|
|
196
|
-
result:
|
|
197
|
-
|
|
198
|
-
|
|
216
|
+
result:
|
|
217
|
+
getResultToken(item.result) === getResultToken(resultTypes.UNKNOWN)
|
|
218
|
+
? resultTypes.UNKNOWN
|
|
219
|
+
: item.result,
|
|
199
220
|
}));
|
|
200
221
|
}
|
|
201
222
|
|
package/src/theme/results.js
CHANGED