@lewishowles/cli-style 0.5.0 → 0.6.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 +12 -0
- package/README.md +370 -92
- package/adapters/bash/cli-style.sh +35 -2
- package/package.json +1 -1
- package/src/cli/render-command.js +6 -0
- package/src/gallery/render-gallery.js +7 -1
- package/src/index.js +1 -0
- package/src/primitives/row.js +94 -5
- package/src/primitives/span.js +33 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.6.0 - 2026-07-03
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added result-state support to `row()` and `cli_style_row`, so label/value rows can render failed, warning, success, or other status cues while keeping column alignment.
|
|
10
|
+
- Added the `span()` inline emphasis primitive and `cli_style_span` Bash helper for highlighting commands, files, or values inside existing line renderers such as `hint()`.
|
|
11
|
+
- Added `span` support to `cli-style render` and the public JavaScript export.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- Reworked the README around integration paths, available renderers, primitive data shapes, patterns, options, profiles, result types, and gallery commands.
|
|
16
|
+
|
|
5
17
|
## 0.5.0 - 2026-07-02
|
|
6
18
|
|
|
7
19
|
### Added
|
package/README.md
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# CLI style
|
|
2
2
|
|
|
3
|
-
Shared terminal output styles for tools, diagnostics, and package CLIs.
|
|
3
|
+
Shared terminal output styles for local tools, diagnostics, agent-facing reports, and package CLIs.
|
|
4
|
+
|
|
5
|
+
Use it directly from JavaScript, or call the same renderers from Bash and Python through the `cli-style` binary.
|
|
4
6
|
|
|
5
7
|
## Requirements
|
|
6
8
|
|
|
7
9
|
- Bun and Node.js 20 or newer for JavaScript projects and local binary builds
|
|
8
|
-
- The standalone binary for Bash or Python projects that should not install Bun
|
|
10
|
+
- The standalone `cli-style` binary for Bash or Python projects that should not install Bun
|
|
9
11
|
|
|
10
12
|
## Install
|
|
11
13
|
|
|
@@ -21,7 +23,29 @@ Bash and Python projects call the `cli-style` binary through thin adapters. If t
|
|
|
21
23
|
export PATH="/path/to/cli-style/bin:$PATH"
|
|
22
24
|
```
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
Build a standalone binary when a repo should not need Bun at runtime:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
bun run build:binary
|
|
30
|
+
bun run smoke:binary
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Create a binary install tarball:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
bun run package:binary
|
|
37
|
+
tar -tzf dist/release/cli-style-*.tar.gz
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The package contains `bin/cli-style` and `adapters/`.
|
|
41
|
+
|
|
42
|
+
## Choose an integration
|
|
43
|
+
|
|
44
|
+
All renderers return text. JavaScript imports functions directly. CLI, Bash, and Python integrations pass JSON-compatible data to a named renderer.
|
|
45
|
+
|
|
46
|
+
### JavaScript
|
|
47
|
+
|
|
48
|
+
Create the `ui` instance at the CLI entrypoint, then pass `ui.options` to renderers. That keeps flag, environment, stream, colour, Unicode, and width detection in one place.
|
|
25
49
|
|
|
26
50
|
```js
|
|
27
51
|
import { createCliStyle, status } from "@lewishowles/cli-style";
|
|
@@ -42,54 +66,9 @@ ui.print(output);
|
|
|
42
66
|
|
|
43
67
|
Renderer methods return strings. `ui.print()`, `ui.write()`, and CLI commands handle stdout and stderr.
|
|
44
68
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
## Reporter
|
|
48
|
-
|
|
49
|
-
Use a reporter when a script needs grouped output rather than one row per file.
|
|
50
|
-
|
|
51
|
-
```js
|
|
52
|
-
import { createReporter } from "@lewishowles/cli-style";
|
|
53
|
-
|
|
54
|
-
const reporter = createReporter({
|
|
55
|
-
colour: false,
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
reporter.divider("Project setup", "Claude + Codex");
|
|
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
|
-
],
|
|
71
|
-
{
|
|
72
|
-
summary: "2 already linked",
|
|
73
|
-
},
|
|
74
|
-
);
|
|
75
|
-
reporter.status("success", "Done.");
|
|
76
|
-
|
|
77
|
-
console.log(reporter.render());
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
Use `reporter.divider()` for major phases. Use `reporter.section()` when you want a normal `info` status row inside a phase. Panels are for framed content blocks, not headings.
|
|
81
|
-
|
|
82
|
-
Groups can include verbose detail rows when a caller opts in:
|
|
83
|
-
|
|
84
|
-
```js
|
|
85
|
-
const reporter = createReporter({
|
|
86
|
-
verbose: process.env.AGENTS_VERBOSE === "1",
|
|
87
|
-
});
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
## CLI
|
|
69
|
+
### CLI render
|
|
91
70
|
|
|
92
|
-
Use `cli-style render <renderer>` when a script needs rendered text from JSON input
|
|
71
|
+
Use `cli-style render <renderer>` when a script needs rendered text from JSON input:
|
|
93
72
|
|
|
94
73
|
```bash
|
|
95
74
|
cli-style render status --profile diagnostic <<'JSON'
|
|
@@ -101,35 +80,11 @@ cli-style render status --profile diagnostic <<'JSON'
|
|
|
101
80
|
JSON
|
|
102
81
|
```
|
|
103
82
|
|
|
104
|
-
|
|
105
|
-
cli-style render task-summary --plain <<'JSON'
|
|
106
|
-
{
|
|
107
|
-
"title": "Task summary",
|
|
108
|
-
"completed": ["Added render command"],
|
|
109
|
-
"remaining": ["Add adapters"]
|
|
110
|
-
}
|
|
111
|
-
JSON
|
|
112
|
-
```
|
|
83
|
+
Use renderer names from the tables below, for example `status`, `row`, `diagnostic-report`, or `task-summary`.
|
|
113
84
|
|
|
114
|
-
|
|
85
|
+
### Bash
|
|
115
86
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
```bash
|
|
119
|
-
bun run build:binary
|
|
120
|
-
bun run smoke:binary
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
Create a binary install tarball:
|
|
124
|
-
|
|
125
|
-
```bash
|
|
126
|
-
bun run package:binary
|
|
127
|
-
tar -tzf dist/release/cli-style-*.tar.gz
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
The package contains `bin/cli-style` and `adapters/`. Install it into a repo-local tools directory, then call that repo-local `bin/cli-style` from scripts.
|
|
131
|
-
|
|
132
|
-
## Bash
|
|
87
|
+
Source the Bash adapter, then use convenience helpers for common primitive renderers:
|
|
133
88
|
|
|
134
89
|
```bash
|
|
135
90
|
source "$(cli-style adapter-path bash)"
|
|
@@ -139,17 +94,13 @@ detail="/path/to/project"
|
|
|
139
94
|
|
|
140
95
|
cli_style_status success "$label" "$detail" --profile diagnostic
|
|
141
96
|
cli_style_row "Workspace" "$detail" --plain
|
|
142
|
-
|
|
97
|
+
cli_style_row "Bundle" "is 22.3 KB, above the 12.0 KB budget" failed --plain
|
|
98
|
+
command="$(cli_style_span "npm run docs:readme" info --plain)"
|
|
99
|
+
cli_style_hint "Run $command before release" --plain
|
|
143
100
|
cli_style_divider "Project setup" --plain
|
|
144
101
|
```
|
|
145
102
|
|
|
146
|
-
|
|
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
|
|
152
|
-
```
|
|
103
|
+
The convenience functions build JSON internally, so callers do not need to escape quotes, backslashes, or paths by hand.
|
|
153
104
|
|
|
154
105
|
Set `CLI_STYLE_BIN` when `cli-style` is not on `PATH`:
|
|
155
106
|
|
|
@@ -157,20 +108,25 @@ Set `CLI_STYLE_BIN` when `cli-style` is not on `PATH`:
|
|
|
157
108
|
CLI_STYLE_BIN="/path/to/cli-style" cli_style_status success "Build passed" "" --plain
|
|
158
109
|
```
|
|
159
110
|
|
|
160
|
-
|
|
111
|
+
Use `cli_style_render` for literal or prebuilt JSON, especially for structured patterns:
|
|
161
112
|
|
|
162
113
|
```bash
|
|
163
|
-
cli_style_render
|
|
114
|
+
cli_style_render diagnostic-report --profile diagnostic <<'JSON'
|
|
164
115
|
{
|
|
165
|
-
"
|
|
166
|
-
"
|
|
116
|
+
"title": "Release checks",
|
|
117
|
+
"checks": [
|
|
118
|
+
{
|
|
119
|
+
"name": "README",
|
|
120
|
+
"result": "success"
|
|
121
|
+
}
|
|
122
|
+
]
|
|
167
123
|
}
|
|
168
124
|
JSON
|
|
169
125
|
```
|
|
170
126
|
|
|
171
|
-
|
|
127
|
+
### Python
|
|
172
128
|
|
|
173
|
-
Add the adapter directory to `PYTHONPATH`, then import the helper:
|
|
129
|
+
Add the adapter directory to `PYTHONPATH`, then import the generic `render()` helper:
|
|
174
130
|
|
|
175
131
|
```bash
|
|
176
132
|
export PYTHONPATH="$(cli-style adapter-path python):$PYTHONPATH"
|
|
@@ -206,6 +162,308 @@ output = render(
|
|
|
206
162
|
)
|
|
207
163
|
```
|
|
208
164
|
|
|
165
|
+
## Available renderers
|
|
166
|
+
|
|
167
|
+
Renderer names are stable for `cli-style render`, `cli_style_render`, and Python `render()`. JavaScript uses camel-case function names.
|
|
168
|
+
|
|
169
|
+
| Renderer | JavaScript | Bash helper | Purpose |
|
|
170
|
+
| --------------- | ------------------------------------- | ------------------------------------ | --------------------------------------------------------------- |
|
|
171
|
+
| `status` | `status(resultType, detail, options)` | `cli_style_status type label detail` | Result line with symbol, label, and detail. |
|
|
172
|
+
| `row` | `row(label, value, options)` | `cli_style_row label value [result]` | Aligned label/value row, optionally marked with a result state. |
|
|
173
|
+
| `span` | `span(value, tone, options)` | `cli_style_span value [tone]` | Inline colour or weight for a word, command, file, or value. |
|
|
174
|
+
| `hint` | `hint(message, options)` | `cli_style_hint message` | Informational hint line. |
|
|
175
|
+
| `divider` | `divider(options)` | `cli_style_divider label` | Section divider with an optional label. |
|
|
176
|
+
| `chip` | `chip(label, tone, options)` | Use `cli_style_render` | Compact labelled state. |
|
|
177
|
+
| `panel` | `panel(options)` | Use `cli_style_render` | Framed content block for grouped output. |
|
|
178
|
+
| `table` | `table(options)` | Use `cli_style_render` | Tabular records with narrow-width fallback. |
|
|
179
|
+
| `progress-bar` | `progressBar(options)` | Use `cli_style_render` | Single progress value with numeric text. |
|
|
180
|
+
| `bar-chart` | `barChart(options)` | Use `cli_style_render` | Multi-row bar chart. |
|
|
181
|
+
| `step` | `step(label, state, options)` | Use `cli_style_render` | One workflow step. |
|
|
182
|
+
| `step-progress` | `stepProgress(options)` | Use `cli_style_render` | Numbered workflow steps. |
|
|
183
|
+
| `empty-state` | `emptyState(title, detail, options)` | Use `cli_style_render` | Empty result message. |
|
|
184
|
+
| `error-block` | `errorBlock(title, lines, options)` | Use `cli_style_render` | Structured error block. |
|
|
185
|
+
|
|
186
|
+
Python uses `render("<renderer>", data, ...)` for every renderer.
|
|
187
|
+
|
|
188
|
+
## Primitive data shapes
|
|
189
|
+
|
|
190
|
+
Use these shapes with `cli-style render`, `cli_style_render`, or Python `render()`. JavaScript callers pass the listed function arguments directly.
|
|
191
|
+
|
|
192
|
+
### `status`
|
|
193
|
+
|
|
194
|
+
```json
|
|
195
|
+
{
|
|
196
|
+
"type": "success",
|
|
197
|
+
"label": "Build passed",
|
|
198
|
+
"detail": "184 tests"
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
- `type`: one of the result types listed below
|
|
203
|
+
- `label`: optional label override
|
|
204
|
+
- `detail`: optional text after the label
|
|
205
|
+
|
|
206
|
+
### `row`
|
|
207
|
+
|
|
208
|
+
```json
|
|
209
|
+
{
|
|
210
|
+
"label": "Bundle",
|
|
211
|
+
"value": "is 22.3 KB, above the 12.0 KB budget",
|
|
212
|
+
"result": "failed",
|
|
213
|
+
"labelWidth": 8
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
- `label`: left-hand label
|
|
218
|
+
- `value`: right-hand value
|
|
219
|
+
- `result`: optional result type for a symbol and tone
|
|
220
|
+
- `labelWidth`: optional minimum label width
|
|
221
|
+
- `labelColour`, `valueColour`, `separator`: optional presentation overrides
|
|
222
|
+
|
|
223
|
+
### `span`
|
|
224
|
+
|
|
225
|
+
```json
|
|
226
|
+
{
|
|
227
|
+
"value": "npm run docs:readme",
|
|
228
|
+
"tone": "info"
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
- `value`: inline text to emphasise
|
|
233
|
+
- `tone`: optional tone, defaulting to `info`
|
|
234
|
+
- `weight`: optional ANSI style, defaulting to `bold`
|
|
235
|
+
|
|
236
|
+
### `hint`
|
|
237
|
+
|
|
238
|
+
```json
|
|
239
|
+
{
|
|
240
|
+
"message": "Run npm run docs:readme before release"
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Pre-render a `span` when only part of the hint needs emphasis.
|
|
245
|
+
|
|
246
|
+
### Structured primitives
|
|
247
|
+
|
|
248
|
+
| Renderer | Main fields |
|
|
249
|
+
| --------------- | -------------------------------------- |
|
|
250
|
+
| `divider` | `label`, `dividerWidth`, `character` |
|
|
251
|
+
| `chip` | `label`, `tone` |
|
|
252
|
+
| `panel` | `title`, `lines`, `tone`, `panelWidth` |
|
|
253
|
+
| `table` | `columns`, `rows`, `width` |
|
|
254
|
+
| `progress-bar` | `value`, `max`, `barWidth`, `tone` |
|
|
255
|
+
| `bar-chart` | `rows`, `barWidth` |
|
|
256
|
+
| `step` | `label`, `state` |
|
|
257
|
+
| `step-progress` | `steps`, `current` |
|
|
258
|
+
| `empty-state` | `title`, `detail` |
|
|
259
|
+
| `error-block` | `title`, `lines` |
|
|
260
|
+
|
|
261
|
+
For visual examples, run `cli-style gallery --section primitives`.
|
|
262
|
+
|
|
263
|
+
## Patterns
|
|
264
|
+
|
|
265
|
+
Patterns are built-in composite renderers for common report shapes. They use the same lower-level primitives internally, but accept structured objects so callers do not need to assemble panels, statuses, tables, and rows by hand.
|
|
266
|
+
|
|
267
|
+
| Renderer | JavaScript | Purpose | Main fields |
|
|
268
|
+
| --------------------- | ------------------------------------------- | ------------------------------------- | ------------------------------------------------------------------------------------ |
|
|
269
|
+
| `diagnostic-report` | `diagnosticReport(report, options)` | Aggregate check report. | `title`, `checks`, `findings`, `skippedChecks`, `nextActions` |
|
|
270
|
+
| `command-result` | `commandResult(result, options)` | Single command outcome with metadata. | `title`, `result`, `summary`, `command`, `exitCode`, `duration`, `details` |
|
|
271
|
+
| `audit-finding` | `auditFinding(finding, options)` | Single audit finding. | `title`, `result`, `finding`, `location`, `evidence`, `recommendation`, `references` |
|
|
272
|
+
| `task-summary` | `taskSummary(summary, options)` | Completed and remaining task summary. | `title`, `task`, `result`, `summary`, `completed`, `remaining` |
|
|
273
|
+
| `agent-transcript` | `agentTranscript(transcript, options)` | User, agent, and tool transcript. | `title`, `entries` |
|
|
274
|
+
| `confirmation-result` | `confirmationResult(confirmation, options)` | Confirmation outcome with detail. | `title`, `state`, `action`, `item`, `detail` |
|
|
275
|
+
| `next-step-block` | `nextStepBlock(nextStep, options)` | Next-step guidance. | `title`, `next`, `reason`, `commands`, `alternatives` |
|
|
276
|
+
| `compact-data-table` | `compactDataTable(data, options)` | Titled compact data table. | `title`, `summary`, `columns`, `rows` |
|
|
277
|
+
|
|
278
|
+
For visual examples, run `cli-style gallery --section patterns`.
|
|
279
|
+
|
|
280
|
+
### Pattern example
|
|
281
|
+
|
|
282
|
+
JavaScript:
|
|
283
|
+
|
|
284
|
+
```js
|
|
285
|
+
import { createCliStyle, diagnosticReport } from "@lewishowles/cli-style";
|
|
286
|
+
|
|
287
|
+
const ui = createCliStyle({
|
|
288
|
+
argv: process.argv.slice(2),
|
|
289
|
+
env: process.env,
|
|
290
|
+
stdout: process.stdout,
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
const output = diagnosticReport(
|
|
294
|
+
{
|
|
295
|
+
title: "Release checks",
|
|
296
|
+
checks: [
|
|
297
|
+
{
|
|
298
|
+
name: "README",
|
|
299
|
+
result: "success",
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
detail: "object-BmsQavd_.js over budget",
|
|
303
|
+
name: "Package size",
|
|
304
|
+
result: "failed",
|
|
305
|
+
},
|
|
306
|
+
],
|
|
307
|
+
findings: [
|
|
308
|
+
{
|
|
309
|
+
message: "dist/object-BmsQavd_.js is 22.3 KB, above the 12.0 KB budget",
|
|
310
|
+
result: "failed",
|
|
311
|
+
},
|
|
312
|
+
],
|
|
313
|
+
nextActions: ["Reduce package size, or raise the budget with a release-note-worthy reason."],
|
|
314
|
+
},
|
|
315
|
+
ui.options,
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
ui.print(output);
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
CLI:
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
cli-style render diagnostic-report --profile diagnostic <<'JSON'
|
|
325
|
+
{
|
|
326
|
+
"title": "Release checks",
|
|
327
|
+
"checks": [
|
|
328
|
+
{
|
|
329
|
+
"name": "README",
|
|
330
|
+
"result": "success"
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
"name": "Package size",
|
|
334
|
+
"result": "failed",
|
|
335
|
+
"detail": "object-BmsQavd_.js over budget"
|
|
336
|
+
}
|
|
337
|
+
],
|
|
338
|
+
"findings": [
|
|
339
|
+
{
|
|
340
|
+
"result": "failed",
|
|
341
|
+
"message": "dist/object-BmsQavd_.js is 22.3 KB, above the 12.0 KB budget"
|
|
342
|
+
}
|
|
343
|
+
],
|
|
344
|
+
"nextActions": [
|
|
345
|
+
"Reduce package size, or raise the budget with a release-note-worthy reason."
|
|
346
|
+
]
|
|
347
|
+
}
|
|
348
|
+
JSON
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
Python:
|
|
352
|
+
|
|
353
|
+
```python
|
|
354
|
+
from cli_style import render
|
|
355
|
+
|
|
356
|
+
output = render(
|
|
357
|
+
"diagnostic-report",
|
|
358
|
+
{
|
|
359
|
+
"title": "Release checks",
|
|
360
|
+
"checks": [
|
|
361
|
+
{
|
|
362
|
+
"name": "README",
|
|
363
|
+
"result": "success",
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
"name": "Package size",
|
|
367
|
+
"result": "failed",
|
|
368
|
+
"detail": "object-BmsQavd_.js over budget",
|
|
369
|
+
},
|
|
370
|
+
],
|
|
371
|
+
"findings": [
|
|
372
|
+
{
|
|
373
|
+
"result": "failed",
|
|
374
|
+
"message": "dist/object-BmsQavd_.js is 22.3 KB, above the 12.0 KB budget",
|
|
375
|
+
},
|
|
376
|
+
],
|
|
377
|
+
"nextActions": [
|
|
378
|
+
"Reduce package size, or raise the budget with a release-note-worthy reason.",
|
|
379
|
+
],
|
|
380
|
+
},
|
|
381
|
+
profile="diagnostic",
|
|
382
|
+
)
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
## Reporter
|
|
386
|
+
|
|
387
|
+
Use a reporter when a JavaScript script needs grouped output built over time.
|
|
388
|
+
|
|
389
|
+
```js
|
|
390
|
+
import { createReporter } from "@lewishowles/cli-style";
|
|
391
|
+
|
|
392
|
+
const reporter = createReporter({
|
|
393
|
+
colour: false,
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
reporter.divider("Project setup", "Claude + Codex");
|
|
397
|
+
reporter.group(
|
|
398
|
+
"Agent scripts",
|
|
399
|
+
[
|
|
400
|
+
{
|
|
401
|
+
label: "project-diagnostics.py",
|
|
402
|
+
result: "unchanged",
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
label: "repo-context.py",
|
|
406
|
+
result: "unchanged",
|
|
407
|
+
},
|
|
408
|
+
],
|
|
409
|
+
{
|
|
410
|
+
summary: "2 already linked",
|
|
411
|
+
},
|
|
412
|
+
);
|
|
413
|
+
reporter.status("success", "Done.");
|
|
414
|
+
|
|
415
|
+
console.log(reporter.render());
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
Use `reporter.divider()` for major phases. Use `reporter.section()` for a normal `info` status row inside a phase. Panels are for framed content blocks, not headings.
|
|
419
|
+
|
|
420
|
+
Groups can include verbose detail rows when a caller opts in:
|
|
421
|
+
|
|
422
|
+
```js
|
|
423
|
+
const reporter = createReporter({
|
|
424
|
+
verbose: process.env.AGENTS_VERBOSE === "1",
|
|
425
|
+
});
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
## Options and flags
|
|
429
|
+
|
|
430
|
+
### JavaScript options
|
|
431
|
+
|
|
432
|
+
Most renderers accept the same output options:
|
|
433
|
+
|
|
434
|
+
| Option | Purpose |
|
|
435
|
+
| --------- | ------------------------------------------------------------------------- |
|
|
436
|
+
| `profile` | Output profile: `human`, `agent`, `diagnostic`, `ci`, `plain`, or `json`. |
|
|
437
|
+
| `colour` | Whether ANSI colour should be applied. |
|
|
438
|
+
| `unicode` | Whether Unicode symbols should be used. |
|
|
439
|
+
| `width` | Available output width for renderers that adapt layout. |
|
|
440
|
+
|
|
441
|
+
Use `createCliStyle()` to resolve these options from `argv`, `env`, `stdout`, CI, TTY, `NO_COLOR`, and terminal capability.
|
|
442
|
+
|
|
443
|
+
### CLI and Bash flags
|
|
444
|
+
|
|
445
|
+
| Flag | Purpose |
|
|
446
|
+
| ---------------------------- | -------------------------------------- |
|
|
447
|
+
| `--profile <name>` | Render with a named profile. |
|
|
448
|
+
| `--plain` | Disable colour and Unicode decoration. |
|
|
449
|
+
| `--no-colour` / `--no-color` | Disable ANSI colour. |
|
|
450
|
+
| `--no-unicode` | Use ASCII symbols. |
|
|
451
|
+
| `--width <columns>` | Override detected output width. |
|
|
452
|
+
|
|
453
|
+
`cli-style render` rejects `--json`. JSON profile behaviour is for library calls and machine-readable commands.
|
|
454
|
+
|
|
455
|
+
### Python options
|
|
456
|
+
|
|
457
|
+
| Argument | Purpose |
|
|
458
|
+
| ------------ | ------------------------------------------------ |
|
|
459
|
+
| `profile` | Render with a named profile. |
|
|
460
|
+
| `width` | Override detected output width. |
|
|
461
|
+
| `plain` | Disable colour and Unicode decoration. |
|
|
462
|
+
| `no_colour` | Disable ANSI colour. |
|
|
463
|
+
| `no_unicode` | Use ASCII symbols. |
|
|
464
|
+
| `binary` | Path or command name for the `cli-style` binary. |
|
|
465
|
+
| `extra_args` | Extra CLI arguments to pass through. |
|
|
466
|
+
|
|
209
467
|
## Profiles
|
|
210
468
|
|
|
211
469
|
| Profile | Behaviour |
|
|
@@ -217,11 +475,31 @@ output = render(
|
|
|
217
475
|
| `plain` | No ANSI, no Unicode dependency, simple text layout. |
|
|
218
476
|
| `json` | Machine-readable only; library calls return structured data unchanged. |
|
|
219
477
|
|
|
478
|
+
## Result types and tones
|
|
479
|
+
|
|
480
|
+
Result types drive symbols, labels, priority, and semantic colour:
|
|
481
|
+
|
|
482
|
+
| Result | Label | Unicode | ASCII | Tone |
|
|
483
|
+
| ----------- | --------- | ------- | ----- | --------- |
|
|
484
|
+
| `success` | Success | `✓` | `OK` | `success` |
|
|
485
|
+
| `info` | Info | `→` | `>` | `info` |
|
|
486
|
+
| `warning` | Warning | `⚠` | `!` | `warning` |
|
|
487
|
+
| `partial` | Partial | `◐` | `!` | `warning` |
|
|
488
|
+
| `unchanged` | Unchanged | `↪` | `-` | `muted` |
|
|
489
|
+
| `skipped` | Skipped | `–` | `-` | `muted` |
|
|
490
|
+
| `unknown` | Unknown | `?` | `?` | `muted` |
|
|
491
|
+
| `failed` | Failed | `×` | `x` | `danger` |
|
|
492
|
+
|
|
493
|
+
Tone values used by public APIs include `info`, `success`, `warning`, `danger`, and `muted`.
|
|
494
|
+
|
|
220
495
|
## Gallery
|
|
221
496
|
|
|
497
|
+
Use the gallery to review current renderer output:
|
|
498
|
+
|
|
222
499
|
```bash
|
|
223
500
|
cli-style gallery
|
|
224
501
|
cli-style gallery no-colour
|
|
502
|
+
cli-style gallery --section primitives
|
|
225
503
|
cli-style gallery --section patterns
|
|
226
504
|
cli-style gallery --fixture audit-finding
|
|
227
505
|
cli-style gallery --profile agent
|
|
@@ -229,4 +507,4 @@ cli-style gallery --width 64
|
|
|
229
507
|
cli-style gallery --variants
|
|
230
508
|
```
|
|
231
509
|
|
|
232
|
-
Use
|
|
510
|
+
Use `--interactive` to select a section or fixture with `fzf` when it is installed.
|
|
@@ -116,24 +116,57 @@ cli_style_status() {
|
|
|
116
116
|
# Row label.
|
|
117
117
|
# @param {string} value
|
|
118
118
|
# Row value.
|
|
119
|
+
# @param {string} result
|
|
120
|
+
# Optional result type before render flags.
|
|
119
121
|
# @param {string} ...
|
|
120
122
|
# Render flags passed through to `cli-style render`.
|
|
121
123
|
cli_style_row() {
|
|
122
124
|
local label="${1:-}"
|
|
123
125
|
local value="${2:-}"
|
|
126
|
+
local result=""
|
|
124
127
|
local json
|
|
125
128
|
|
|
126
|
-
if (($# >=
|
|
129
|
+
if (($# >= 3)) && [[ "${3:-}" != --* ]]; then
|
|
130
|
+
result="${3:-}"
|
|
131
|
+
shift 3
|
|
132
|
+
elif (($# >= 2)); then
|
|
127
133
|
shift 2
|
|
128
134
|
else
|
|
129
135
|
set --
|
|
130
136
|
fi
|
|
131
137
|
|
|
132
|
-
json="{\"label\":$(cli_style_json_string "$label"),\"value\":$(cli_style_json_string "$value")}"
|
|
138
|
+
json="{\"label\":$(cli_style_json_string "$label"),\"value\":$(cli_style_json_string "$value"),\"result\":$(cli_style_json_string "$result")}"
|
|
133
139
|
|
|
134
140
|
cli_style_render_json row "$json" "$@"
|
|
135
141
|
}
|
|
136
142
|
|
|
143
|
+
# Render an inline span from Bash string values.
|
|
144
|
+
#
|
|
145
|
+
# @param {string} value
|
|
146
|
+
# Span text.
|
|
147
|
+
# @param {string} tone
|
|
148
|
+
# Optional colour tone.
|
|
149
|
+
# @param {string} ...
|
|
150
|
+
# Render flags passed through to `cli-style render`.
|
|
151
|
+
cli_style_span() {
|
|
152
|
+
local value="${1:-}"
|
|
153
|
+
local tone="info"
|
|
154
|
+
local json
|
|
155
|
+
|
|
156
|
+
if (($# >= 2)) && [[ "${2:-}" != --* ]]; then
|
|
157
|
+
tone="${2:-}"
|
|
158
|
+
shift 2
|
|
159
|
+
elif (($# >= 1)); then
|
|
160
|
+
shift
|
|
161
|
+
else
|
|
162
|
+
set --
|
|
163
|
+
fi
|
|
164
|
+
|
|
165
|
+
json="{\"value\":$(cli_style_json_string "$value"),\"tone\":$(cli_style_json_string "$tone")}"
|
|
166
|
+
|
|
167
|
+
cli_style_render_json span "$json" "$@"
|
|
168
|
+
}
|
|
169
|
+
|
|
137
170
|
# Render a divider from a Bash string value.
|
|
138
171
|
#
|
|
139
172
|
# @param {string} label
|
package/package.json
CHANGED
|
@@ -13,6 +13,7 @@ import { emptyState, errorBlock, hint } from "../primitives/feedback.js";
|
|
|
13
13
|
import { panel } from "../primitives/panel.js";
|
|
14
14
|
import { progressBar } from "../primitives/progress-bar.js";
|
|
15
15
|
import { row } from "../primitives/row.js";
|
|
16
|
+
import { span } from "../primitives/span.js";
|
|
16
17
|
import { status } from "../primitives/status.js";
|
|
17
18
|
import { step, stepProgress } from "../primitives/step-progress.js";
|
|
18
19
|
import { table } from "../primitives/table.js";
|
|
@@ -72,6 +73,11 @@ const renderers = {
|
|
|
72
73
|
...input,
|
|
73
74
|
...options,
|
|
74
75
|
}),
|
|
76
|
+
span: (input, options) =>
|
|
77
|
+
span(input.value, input.tone, {
|
|
78
|
+
...input,
|
|
79
|
+
...options,
|
|
80
|
+
}),
|
|
75
81
|
status: (input, options) =>
|
|
76
82
|
status(input.type, input.detail, {
|
|
77
83
|
...input,
|
|
@@ -14,6 +14,7 @@ import { emptyState, errorBlock, hint } from "../primitives/feedback.js";
|
|
|
14
14
|
import { panel } from "../primitives/panel.js";
|
|
15
15
|
import { progressBar } from "../primitives/progress-bar.js";
|
|
16
16
|
import { row } from "../primitives/row.js";
|
|
17
|
+
import { span } from "../primitives/span.js";
|
|
17
18
|
import { status } from "../primitives/status.js";
|
|
18
19
|
import { step, stepProgress, stepStates } from "../primitives/step-progress.js";
|
|
19
20
|
import { table } from "../primitives/table.js";
|
|
@@ -676,7 +677,7 @@ function renderPrimitives(options) {
|
|
|
676
677
|
}),
|
|
677
678
|
"",
|
|
678
679
|
"Feedback",
|
|
679
|
-
hint("
|
|
680
|
+
hint(`Run ${span("npm run docs:readme", "info", options)}`, options),
|
|
680
681
|
emptyState("No results", "tone: muted", options),
|
|
681
682
|
errorBlock("Failed", ["tone: danger"], {
|
|
682
683
|
...options,
|
|
@@ -709,6 +710,11 @@ function renderPrimitives(options) {
|
|
|
709
710
|
...options,
|
|
710
711
|
labelWidth: 8,
|
|
711
712
|
}),
|
|
713
|
+
row("Bundle", "is 22.3 KB, above the 12.0 KB budget", {
|
|
714
|
+
...options,
|
|
715
|
+
labelWidth: 8,
|
|
716
|
+
result: resultTypes.FAILED,
|
|
717
|
+
}),
|
|
712
718
|
].join("\n");
|
|
713
719
|
}
|
|
714
720
|
|
package/src/index.js
CHANGED
|
@@ -35,6 +35,7 @@ export { emptyState, errorBlock, hint } from "./primitives/feedback.js";
|
|
|
35
35
|
export { panel } from "./primitives/panel.js";
|
|
36
36
|
export { progressBar } from "./primitives/progress-bar.js";
|
|
37
37
|
export { row } from "./primitives/row.js";
|
|
38
|
+
export { span } from "./primitives/span.js";
|
|
38
39
|
export { status } from "./primitives/status.js";
|
|
39
40
|
export { step, stepProgress, stepStates } from "./primitives/step-progress.js";
|
|
40
41
|
export { table } from "./primitives/table.js";
|
package/src/primitives/row.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { foreground } from "../formatters/ansi.js";
|
|
2
|
+
import { getResultSymbol, getResultToken } from "../theme/results.js";
|
|
2
3
|
|
|
3
4
|
// Row labels use the same subdued tone as table headers by default.
|
|
4
5
|
const defaultLabelColour = "muted";
|
|
@@ -21,6 +22,8 @@ const defaultSeparator = " ";
|
|
|
21
22
|
* Colour token for the label.
|
|
22
23
|
* @param {number} options.labelWidth
|
|
23
24
|
* Minimum label width before the separator.
|
|
25
|
+
* @param {string} options.result
|
|
26
|
+
* Optional semantic result state for the row.
|
|
24
27
|
* @param {string} options.separator
|
|
25
28
|
* Text between label and value.
|
|
26
29
|
* @param {string} options.valueColour
|
|
@@ -29,8 +32,10 @@ const defaultSeparator = " ";
|
|
|
29
32
|
* Rendered row.
|
|
30
33
|
*/
|
|
31
34
|
export function row(label, value = "", options = {}) {
|
|
32
|
-
const
|
|
33
|
-
const
|
|
35
|
+
const resultToken = resolveResultToken(options);
|
|
36
|
+
const displayLabel = renderDisplayLabel(label, resultToken, options);
|
|
37
|
+
const labelWidth = Math.max(displayLabel.length, options.labelWidth ?? 0);
|
|
38
|
+
const labelText = displayLabel.padEnd(labelWidth, " ");
|
|
34
39
|
const renderedLabel = renderLabel(labelText, options);
|
|
35
40
|
|
|
36
41
|
if (value === "") {
|
|
@@ -43,6 +48,44 @@ export function row(label, value = "", options = {}) {
|
|
|
43
48
|
return `${renderedLabel}${separator}${renderedValue}`;
|
|
44
49
|
}
|
|
45
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Resolve the row's optional result token.
|
|
53
|
+
*
|
|
54
|
+
* @param {object} options
|
|
55
|
+
* Rendering options.
|
|
56
|
+
* @returns {object|null}
|
|
57
|
+
* Result token when a row result is provided.
|
|
58
|
+
*/
|
|
59
|
+
function resolveResultToken(options) {
|
|
60
|
+
if (typeof options.result !== "string" || options.result === "") {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return getResultToken(options.result);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Render the visible row label, including any result symbol.
|
|
69
|
+
*
|
|
70
|
+
* @param {string} label
|
|
71
|
+
* Label text to render.
|
|
72
|
+
* @param {object|null} resultToken
|
|
73
|
+
* Optional result token.
|
|
74
|
+
* @param {object} options
|
|
75
|
+
* Rendering options.
|
|
76
|
+
* @returns {string}
|
|
77
|
+
* Label text with any result prefix.
|
|
78
|
+
*/
|
|
79
|
+
function renderDisplayLabel(label, resultToken, options) {
|
|
80
|
+
if (resultToken === null) {
|
|
81
|
+
return label;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const symbol = getResultSymbol(options.result, options);
|
|
85
|
+
|
|
86
|
+
return `${symbol} ${label}`;
|
|
87
|
+
}
|
|
88
|
+
|
|
46
89
|
/**
|
|
47
90
|
* Render a row label.
|
|
48
91
|
*
|
|
@@ -58,7 +101,7 @@ function renderLabel(label, options) {
|
|
|
58
101
|
return label;
|
|
59
102
|
}
|
|
60
103
|
|
|
61
|
-
return foreground(label, options
|
|
104
|
+
return foreground(label, getLabelColour(options), options);
|
|
62
105
|
}
|
|
63
106
|
|
|
64
107
|
/**
|
|
@@ -72,9 +115,55 @@ function renderLabel(label, options) {
|
|
|
72
115
|
* Rendered value.
|
|
73
116
|
*/
|
|
74
117
|
function renderValue(value, options) {
|
|
75
|
-
if (options.colour !== true
|
|
118
|
+
if (options.colour !== true) {
|
|
119
|
+
return value;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const colour = getValueColour(options);
|
|
123
|
+
|
|
124
|
+
if (colour === undefined) {
|
|
76
125
|
return value;
|
|
77
126
|
}
|
|
78
127
|
|
|
79
|
-
return foreground(value,
|
|
128
|
+
return foreground(value, colour, options);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Resolve the colour token for the row label.
|
|
133
|
+
*
|
|
134
|
+
* @param {object} options
|
|
135
|
+
* Rendering options.
|
|
136
|
+
* @returns {string}
|
|
137
|
+
* Colour token for the label.
|
|
138
|
+
*/
|
|
139
|
+
function getLabelColour(options) {
|
|
140
|
+
if (options.labelColour !== undefined) {
|
|
141
|
+
return options.labelColour;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (typeof options.result === "string" && options.result !== "") {
|
|
145
|
+
return getResultToken(options.result).tone;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return defaultLabelColour;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Resolve the colour token for the row value.
|
|
153
|
+
*
|
|
154
|
+
* @param {object} options
|
|
155
|
+
* Rendering options.
|
|
156
|
+
* @returns {string|undefined}
|
|
157
|
+
* Colour token for the value.
|
|
158
|
+
*/
|
|
159
|
+
function getValueColour(options) {
|
|
160
|
+
if (options.valueColour !== undefined) {
|
|
161
|
+
return options.valueColour;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (typeof options.result === "string" && options.result !== "") {
|
|
165
|
+
return getResultToken(options.result).tone;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return undefined;
|
|
80
169
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { foreground, style } from "../formatters/ansi.js";
|
|
2
|
+
|
|
3
|
+
// Inline spans use the same informational tone as hints by default.
|
|
4
|
+
const defaultTone = "info";
|
|
5
|
+
|
|
6
|
+
// Inline emphasis should remain visible without forcing a block-level renderer.
|
|
7
|
+
const defaultWeight = "bold";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Render an inline text span.
|
|
11
|
+
*
|
|
12
|
+
* @param {string} value
|
|
13
|
+
* Text to render.
|
|
14
|
+
* @param {string} tone
|
|
15
|
+
* Colour tone for the span.
|
|
16
|
+
* @param {object} options
|
|
17
|
+
* Rendering options.
|
|
18
|
+
* @param {boolean} options.colour
|
|
19
|
+
* Whether ANSI colour should be applied.
|
|
20
|
+
* @param {string} options.weight
|
|
21
|
+
* Optional ANSI text weight.
|
|
22
|
+
* @returns {string}
|
|
23
|
+
* Rendered span.
|
|
24
|
+
*/
|
|
25
|
+
export function span(value = "", tone = defaultTone, options = {}) {
|
|
26
|
+
if (options.colour !== true) {
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const renderedValue = foreground(value, tone, options);
|
|
31
|
+
|
|
32
|
+
return style(renderedValue, options.weight ?? defaultWeight, options);
|
|
33
|
+
}
|