@lewishowles/cli-style 0.4.0 → 0.5.0
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 +21 -0
- package/README.md +41 -20
- package/adapters/bash/cli-style.sh +127 -0
- 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/colours.js +1 -1
- package/src/theme/results.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.5.0 - 2026-07-02
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added Bash adapter convenience functions for common renderers: `cli_style_status`, `cli_style_row`, `cli_style_divider`, and `cli_style_hint`.
|
|
10
|
+
- Added Bash adapter escaping for dynamic string values, so scripts can pass quotes, backslashes, paths, and messages without hand-building JSON.
|
|
11
|
+
- Added README examples for JS CLI entrypoint wiring with `createCliStyle({ argv, env, stdout })` and helper-first Bash adapter usage.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- Lightened the muted colour token so secondary text stays readable on dark surfaces.
|
|
16
|
+
|
|
17
|
+
## 0.4.1 - 2026-07-01
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- Updated grouped reporter summaries so skipped rows outrank success rows while warnings and failures still take precedence.
|
|
22
|
+
- Added the project-local vite-plus pre-commit hook setup so commits run the existing staged checks.
|
|
23
|
+
|
|
3
24
|
## 0.4.0 - 2026-06-30
|
|
4
25
|
|
|
5
26
|
### Added
|
package/README.md
CHANGED
|
@@ -27,8 +27,9 @@ export PATH="/path/to/cli-style/bin:$PATH"
|
|
|
27
27
|
import { createCliStyle, status } from "@lewishowles/cli-style";
|
|
28
28
|
|
|
29
29
|
const ui = createCliStyle({
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
argv: process.argv.slice(2),
|
|
31
|
+
env: process.env,
|
|
32
|
+
stdout: process.stdout,
|
|
32
33
|
});
|
|
33
34
|
|
|
34
35
|
const output = status("success", "184 tests", {
|
|
@@ -41,6 +42,8 @@ ui.print(output);
|
|
|
41
42
|
|
|
42
43
|
Renderer methods return strings. `ui.print()`, `ui.write()`, and CLI commands handle stdout and stderr.
|
|
43
44
|
|
|
45
|
+
Create the `ui` instance at the CLI entrypoint, then pass it to command handlers. That keeps flag, environment, stream, colour, Unicode, and width detection in one place.
|
|
46
|
+
|
|
44
47
|
## Reporter
|
|
45
48
|
|
|
46
49
|
Use a reporter when a script needs grouped output rather than one row per file.
|
|
@@ -53,18 +56,22 @@ const reporter = createReporter({
|
|
|
53
56
|
});
|
|
54
57
|
|
|
55
58
|
reporter.divider("Project setup", "Claude + Codex");
|
|
56
|
-
reporter.group(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
reporter.group(
|
|
60
|
+
"Agent scripts",
|
|
61
|
+
[
|
|
62
|
+
{
|
|
63
|
+
label: "project-diagnostics.py",
|
|
64
|
+
result: "unchanged",
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
label: "repo-context.py",
|
|
68
|
+
result: "unchanged",
|
|
69
|
+
},
|
|
70
|
+
],
|
|
61
71
|
{
|
|
62
|
-
|
|
63
|
-
result: "unchanged",
|
|
72
|
+
summary: "2 already linked",
|
|
64
73
|
},
|
|
65
|
-
|
|
66
|
-
summary: "2 already linked",
|
|
67
|
-
});
|
|
74
|
+
);
|
|
68
75
|
reporter.status("success", "Done.");
|
|
69
76
|
|
|
70
77
|
console.log(reporter.render());
|
|
@@ -127,19 +134,33 @@ The package contains `bin/cli-style` and `adapters/`. Install it into a repo-loc
|
|
|
127
134
|
```bash
|
|
128
135
|
source "$(cli-style adapter-path bash)"
|
|
129
136
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
+
label='Build "passed"'
|
|
138
|
+
detail="/path/to/project"
|
|
139
|
+
|
|
140
|
+
cli_style_status success "$label" "$detail" --profile diagnostic
|
|
141
|
+
cli_style_row "Workspace" "$detail" --plain
|
|
142
|
+
cli_style_hint "Run \"check\" before release" --plain
|
|
143
|
+
cli_style_divider "Project setup" --plain
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Use the convenience functions for dynamic Bash values. They build JSON internally, so callers do not need to escape quotes, backslashes, or paths by hand.
|
|
147
|
+
|
|
148
|
+
`cli_style_status` accepts `type`, `label`, `detail`, then render flags. Pass an empty detail when you only need a label:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
cli_style_status success "Done" "" --plain
|
|
137
152
|
```
|
|
138
153
|
|
|
139
154
|
Set `CLI_STYLE_BIN` when `cli-style` is not on `PATH`:
|
|
140
155
|
|
|
141
156
|
```bash
|
|
142
|
-
CLI_STYLE_BIN="/path/to/cli-style"
|
|
157
|
+
CLI_STYLE_BIN="/path/to/cli-style" cli_style_status success "Build passed" "" --plain
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Keep `cli_style_render` for literal or prebuilt JSON:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
cli_style_render status --plain <<'JSON'
|
|
143
164
|
{
|
|
144
165
|
"type": "success",
|
|
145
166
|
"label": "Build passed"
|
|
@@ -23,6 +23,40 @@ cli_style_has_binary() {
|
|
|
23
23
|
command -v "$binary" >/dev/null 2>&1
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
# Escape a Bash string for JSON string values.
|
|
27
|
+
#
|
|
28
|
+
# @param {string} value
|
|
29
|
+
# String value to encode.
|
|
30
|
+
cli_style_json_string() {
|
|
31
|
+
local value="${1:-}"
|
|
32
|
+
|
|
33
|
+
value="${value//\\/\\\\}"
|
|
34
|
+
value="${value//\"/\\\"}"
|
|
35
|
+
value="${value//$'\b'/\\b}"
|
|
36
|
+
value="${value//$'\f'/\\f}"
|
|
37
|
+
value="${value//$'\n'/\\n}"
|
|
38
|
+
value="${value//$'\r'/\\r}"
|
|
39
|
+
value="${value//$'\t'/\\t}"
|
|
40
|
+
|
|
41
|
+
printf '"%s"' "$value"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# Render a JSON object through the shared cli-style command.
|
|
45
|
+
#
|
|
46
|
+
# @param {string} renderer
|
|
47
|
+
# Stable renderer name accepted by `cli-style render`.
|
|
48
|
+
# @param {string} json
|
|
49
|
+
# Prebuilt JSON object.
|
|
50
|
+
# @param {string} ...
|
|
51
|
+
# Render flags passed through to `cli-style render`.
|
|
52
|
+
cli_style_render_json() {
|
|
53
|
+
local renderer="${1:-}"
|
|
54
|
+
local json="${2:-}"
|
|
55
|
+
|
|
56
|
+
shift 2
|
|
57
|
+
printf '%s\n' "$json" | cli_style_render "$renderer" "$@"
|
|
58
|
+
}
|
|
59
|
+
|
|
26
60
|
# Render stdin JSON through the shared cli-style command.
|
|
27
61
|
#
|
|
28
62
|
# @param {string} renderer
|
|
@@ -48,3 +82,96 @@ cli_style_render() {
|
|
|
48
82
|
|
|
49
83
|
"$binary" render "$renderer" "$@"
|
|
50
84
|
}
|
|
85
|
+
|
|
86
|
+
# Render a result status line from Bash string values.
|
|
87
|
+
#
|
|
88
|
+
# @param {string} type
|
|
89
|
+
# Result type such as success, failed, warning, or skipped.
|
|
90
|
+
# @param {string} label
|
|
91
|
+
# Status label.
|
|
92
|
+
# @param {string} detail
|
|
93
|
+
# Optional detail text.
|
|
94
|
+
# @param {string} ...
|
|
95
|
+
# Render flags passed through to `cli-style render`.
|
|
96
|
+
cli_style_status() {
|
|
97
|
+
local type="${1:-}"
|
|
98
|
+
local label="${2:-}"
|
|
99
|
+
local detail="${3:-}"
|
|
100
|
+
local json
|
|
101
|
+
|
|
102
|
+
if (($# >= 3)); then
|
|
103
|
+
shift 3
|
|
104
|
+
else
|
|
105
|
+
set --
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
json="{\"type\":$(cli_style_json_string "$type"),\"label\":$(cli_style_json_string "$label"),\"detail\":$(cli_style_json_string "$detail")}"
|
|
109
|
+
|
|
110
|
+
cli_style_render_json status "$json" "$@"
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
# Render a labelled value row from Bash string values.
|
|
114
|
+
#
|
|
115
|
+
# @param {string} label
|
|
116
|
+
# Row label.
|
|
117
|
+
# @param {string} value
|
|
118
|
+
# Row value.
|
|
119
|
+
# @param {string} ...
|
|
120
|
+
# Render flags passed through to `cli-style render`.
|
|
121
|
+
cli_style_row() {
|
|
122
|
+
local label="${1:-}"
|
|
123
|
+
local value="${2:-}"
|
|
124
|
+
local json
|
|
125
|
+
|
|
126
|
+
if (($# >= 2)); then
|
|
127
|
+
shift 2
|
|
128
|
+
else
|
|
129
|
+
set --
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
json="{\"label\":$(cli_style_json_string "$label"),\"value\":$(cli_style_json_string "$value")}"
|
|
133
|
+
|
|
134
|
+
cli_style_render_json row "$json" "$@"
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
# Render a divider from a Bash string value.
|
|
138
|
+
#
|
|
139
|
+
# @param {string} label
|
|
140
|
+
# Optional divider label.
|
|
141
|
+
# @param {string} ...
|
|
142
|
+
# Render flags passed through to `cli-style render`.
|
|
143
|
+
cli_style_divider() {
|
|
144
|
+
local label="${1:-}"
|
|
145
|
+
local json
|
|
146
|
+
|
|
147
|
+
if (($# >= 1)); then
|
|
148
|
+
shift
|
|
149
|
+
else
|
|
150
|
+
set --
|
|
151
|
+
fi
|
|
152
|
+
|
|
153
|
+
json="{\"label\":$(cli_style_json_string "$label")}"
|
|
154
|
+
|
|
155
|
+
cli_style_render_json divider "$json" "$@"
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
# Render a hint from a Bash string value.
|
|
159
|
+
#
|
|
160
|
+
# @param {string} message
|
|
161
|
+
# Hint message.
|
|
162
|
+
# @param {string} ...
|
|
163
|
+
# Render flags passed through to `cli-style render`.
|
|
164
|
+
cli_style_hint() {
|
|
165
|
+
local message="${1:-}"
|
|
166
|
+
local json
|
|
167
|
+
|
|
168
|
+
if (($# >= 1)); then
|
|
169
|
+
shift
|
|
170
|
+
else
|
|
171
|
+
set --
|
|
172
|
+
fi
|
|
173
|
+
|
|
174
|
+
json="{\"message\":$(cli_style_json_string "$message")}"
|
|
175
|
+
|
|
176
|
+
cli_style_render_json hint "$json" "$@"
|
|
177
|
+
}
|
package/examples/diagnostic.mjs
CHANGED
|
@@ -22,9 +22,11 @@ const report = {
|
|
|
22
22
|
title: "Diagnostics",
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
console.log(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
console.log(
|
|
26
|
+
diagnosticReport(report, {
|
|
27
|
+
colour: false,
|
|
28
|
+
profile: "diagnostic",
|
|
29
|
+
unicode: true,
|
|
30
|
+
width: 80,
|
|
31
|
+
}),
|
|
32
|
+
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lewishowles/cli-style",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Shared terminal output styles for Lewis Howles tools, diagnostics, and package CLIs.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"check": "vp check --fix",
|
|
33
33
|
"cli-style:gallery": "bun ./bin/cli-style.js gallery",
|
|
34
34
|
"package:binary": "scripts/package-binary.sh",
|
|
35
|
+
"prepare": "vp config",
|
|
35
36
|
"smoke:binary": "./dist/bin/cli-style render status --plain < tests/fixtures/render/status-success.json",
|
|
36
37
|
"test:unit": "bun test"
|
|
37
38
|
},
|
|
@@ -1,19 +1,10 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
galleryFixtures,
|
|
5
|
-
gallerySections,
|
|
6
|
-
galleryVariants,
|
|
7
|
-
} from "../gallery/render-gallery.js";
|
|
3
|
+
import { galleryFixtures, gallerySections, galleryVariants } from "../gallery/render-gallery.js";
|
|
8
4
|
import { isProfile, profiles } from "../profiles/profiles.js";
|
|
9
5
|
|
|
10
6
|
// Global rendering flags accepted after `gallery` for focused review commands.
|
|
11
|
-
const globalRenderingFlags = new Set([
|
|
12
|
-
"--no-color",
|
|
13
|
-
"--no-colour",
|
|
14
|
-
"--no-unicode",
|
|
15
|
-
"--plain",
|
|
16
|
-
]);
|
|
7
|
+
const globalRenderingFlags = new Set(["--no-color", "--no-colour", "--no-unicode", "--plain"]);
|
|
17
8
|
|
|
18
9
|
/**
|
|
19
10
|
* Parse gallery command arguments.
|
|
@@ -25,6 +16,7 @@ const globalRenderingFlags = new Set([
|
|
|
25
16
|
*/
|
|
26
17
|
export function parseGalleryRequest(args = []) {
|
|
27
18
|
let hasVariant = false;
|
|
19
|
+
|
|
28
20
|
const request = {
|
|
29
21
|
fixture: undefined,
|
|
30
22
|
interactive: false,
|
|
@@ -96,15 +88,13 @@ export function selectInteractiveGalleryRequest(request) {
|
|
|
96
88
|
...gallerySections.map((section) => `section:${section}`),
|
|
97
89
|
...galleryFixtures.map((fixture) => `fixture:${fixture}`),
|
|
98
90
|
];
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
"--layout=reverse",
|
|
102
|
-
"--prompt=Gallery > ",
|
|
103
|
-
], {
|
|
91
|
+
|
|
92
|
+
const selection = spawnSync("fzf", ["--height=60%", "--layout=reverse", "--prompt=Gallery > "], {
|
|
104
93
|
encoding: "utf8",
|
|
105
94
|
input: choices.join("\n"),
|
|
106
95
|
stdio: ["pipe", "pipe", "inherit"],
|
|
107
96
|
});
|
|
97
|
+
|
|
108
98
|
const value = selection.status === 0 ? selection.stdout.trim() : "";
|
|
109
99
|
|
|
110
100
|
if (value === "") {
|
|
@@ -22,73 +22,81 @@ import { isProfile, profiles } from "../profiles/profiles.js";
|
|
|
22
22
|
const renderers = {
|
|
23
23
|
"agent-transcript": (input, options) => agentTranscript(input, options),
|
|
24
24
|
"audit-finding": (input, options) => auditFinding(input, options),
|
|
25
|
-
"bar-chart": (input, options) =>
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
"bar-chart": (input, options) =>
|
|
26
|
+
barChart({
|
|
27
|
+
...input,
|
|
28
|
+
...options,
|
|
29
|
+
}),
|
|
30
|
+
chip: (input, options) =>
|
|
31
|
+
chip(input.label, input.tone, {
|
|
32
|
+
...input,
|
|
33
|
+
...options,
|
|
34
|
+
}),
|
|
33
35
|
"command-result": (input, options) => commandResult(input, options),
|
|
34
36
|
"compact-data-table": (input, options) => compactDataTable(input, options),
|
|
35
37
|
"confirmation-result": (input, options) => confirmationResult(input, options),
|
|
36
38
|
"diagnostic-report": (input, options) => diagnosticReport(input, options),
|
|
37
|
-
divider: (input, options) =>
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
39
|
+
divider: (input, options) =>
|
|
40
|
+
divider({
|
|
41
|
+
...input,
|
|
42
|
+
...options,
|
|
43
|
+
}),
|
|
44
|
+
"empty-state": (input, options) =>
|
|
45
|
+
emptyState(input.title, input.detail, {
|
|
46
|
+
...input,
|
|
47
|
+
...options,
|
|
48
|
+
}),
|
|
49
|
+
"error-block": (input, options) =>
|
|
50
|
+
errorBlock(input.title, input.lines, {
|
|
51
|
+
...input,
|
|
52
|
+
...options,
|
|
53
|
+
}),
|
|
54
|
+
hint: (input, options) =>
|
|
55
|
+
hint(input.message, {
|
|
56
|
+
...input,
|
|
57
|
+
...options,
|
|
58
|
+
}),
|
|
53
59
|
"next-step-block": (input, options) => nextStepBlock(input, options),
|
|
54
|
-
panel: (input, options) =>
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
60
|
+
panel: (input, options) =>
|
|
61
|
+
panel({
|
|
62
|
+
...input,
|
|
63
|
+
...options,
|
|
64
|
+
}),
|
|
65
|
+
"progress-bar": (input, options) =>
|
|
66
|
+
progressBar({
|
|
67
|
+
...input,
|
|
68
|
+
...options,
|
|
69
|
+
}),
|
|
70
|
+
row: (input, options) =>
|
|
71
|
+
row(input.label, input.value, {
|
|
72
|
+
...input,
|
|
73
|
+
...options,
|
|
74
|
+
}),
|
|
75
|
+
status: (input, options) =>
|
|
76
|
+
status(input.type, input.detail, {
|
|
77
|
+
...input,
|
|
78
|
+
...options,
|
|
79
|
+
}),
|
|
80
|
+
step: (input, options) =>
|
|
81
|
+
step(input.label, input.state, {
|
|
82
|
+
...input,
|
|
83
|
+
...options,
|
|
84
|
+
}),
|
|
85
|
+
"step-progress": (input, options) =>
|
|
86
|
+
stepProgress({
|
|
87
|
+
...input,
|
|
88
|
+
...options,
|
|
89
|
+
}),
|
|
90
|
+
table: (input, options) =>
|
|
91
|
+
table({
|
|
92
|
+
...input,
|
|
93
|
+
...options,
|
|
94
|
+
}),
|
|
82
95
|
"task-summary": (input, options) => taskSummary(input, options),
|
|
83
96
|
};
|
|
84
97
|
|
|
85
98
|
// Global rendering flags accepted after `render <renderer>`.
|
|
86
|
-
const globalRenderingFlags = new Set([
|
|
87
|
-
"--no-color",
|
|
88
|
-
"--no-colour",
|
|
89
|
-
"--no-unicode",
|
|
90
|
-
"--plain",
|
|
91
|
-
]);
|
|
99
|
+
const globalRenderingFlags = new Set(["--no-color", "--no-colour", "--no-unicode", "--plain"]);
|
|
92
100
|
|
|
93
101
|
// Renderer names exposed for help text and tests.
|
|
94
102
|
export const rendererNames = Object.keys(renderers);
|
package/src/create-cli-style.js
CHANGED
|
@@ -28,10 +28,11 @@ export function createCliStyle(options = {}) {
|
|
|
28
28
|
print: (value) => {
|
|
29
29
|
console.log(value);
|
|
30
30
|
},
|
|
31
|
-
reporter: (reporterOptions = {}) =>
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
reporter: (reporterOptions = {}) =>
|
|
32
|
+
createReporter({
|
|
33
|
+
...resolvedOptions,
|
|
34
|
+
...reporterOptions,
|
|
35
|
+
}),
|
|
35
36
|
write: (value) => {
|
|
36
37
|
process.stdout.write(value);
|
|
37
38
|
},
|