@lewishowles/cli-style 0.6.0 → 0.8.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 +24 -0
- package/README.md +121 -39
- package/adapters/bash/cli-style.sh +200 -0
- package/adapters/python/cli_style.py +215 -0
- package/adapters/swift/CliStyle.swift +407 -0
- package/package.json +1 -1
- package/src/cli/adapter-path-command.js +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.8.0 - 2026-07-05
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added Swift adapter with convenience functions for scalar primitives and per-script patterns: `CliStyle.status`, `CliStyle.row`, `CliStyle.span`, `CliStyle.hint`, `CliStyle.divider`, `CliStyle.commandResult`, `CliStyle.auditFinding`, `CliStyle.taskSummary`, `CliStyle.confirmationResult`, and `CliStyle.nextStepBlock`.
|
|
10
|
+
- Added `cli-style adapter-path swift` support for discovering the Swift adapter file path.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Extended the README renderer table with a Swift helper column covering all 20 renderers.
|
|
15
|
+
|
|
16
|
+
## 0.7.0 - 2026-07-05
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- Added Python adapter convenience functions for scalar pattern usage: `command_result`, `audit_finding`, `task_summary`, `confirmation_result`, and `next_step_block`.
|
|
21
|
+
- Added Python adapter convenience functions for common primitives: `status`, `row`, `span`, `hint`, and `divider`.
|
|
22
|
+
- Added Bash adapter convenience functions for scalar pattern usage: `cli_style_command_result`, `cli_style_audit_finding`, `cli_style_task_summary`, `cli_style_confirmation_result`, and `cli_style_next_step_block`.
|
|
23
|
+
- Added Bash adapter helpers for JSON string arrays and integer/null fields, so pattern wrappers can safely pass single-item list fields and exit codes.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- Extended the README renderer table with a Python helper column covering all 20 renderers, including convenience functions and `render()` fallbacks for aggregate patterns.
|
|
28
|
+
|
|
5
29
|
## 0.6.0 - 2026-07-03
|
|
6
30
|
|
|
7
31
|
### Added
|
package/README.md
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
Shared terminal output styles for local tools, diagnostics, agent-facing reports, and package CLIs.
|
|
4
4
|
|
|
5
|
-
Use it directly from JavaScript, or call the same renderers from Bash and
|
|
5
|
+
Use it directly from JavaScript, or call the same renderers from Bash, Python, and Swift through the `cli-style` binary.
|
|
6
6
|
|
|
7
7
|
## Requirements
|
|
8
8
|
|
|
9
9
|
- Bun and Node.js 20 or newer for JavaScript projects and local binary builds
|
|
10
|
-
- The standalone `cli-style` binary for Bash or
|
|
10
|
+
- The standalone `cli-style` binary for Bash, Python, or Swift projects that should not install Bun
|
|
11
11
|
|
|
12
12
|
## Install
|
|
13
13
|
|
|
@@ -17,7 +17,7 @@ JavaScript projects can install the package directly:
|
|
|
17
17
|
bun add @lewishowles/cli-style
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
Bash and
|
|
20
|
+
Bash, Python, and Swift projects call the `cli-style` binary through thin adapters. If the project already has Node or Bun tooling, add this package as a dev dependency and use the package binary. Otherwise, install or vendor the standalone binary and put it on `PATH`:
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
23
|
export PATH="/path/to/cli-style/bin:$PATH"
|
|
@@ -41,7 +41,7 @@ The package contains `bin/cli-style` and `adapters/`.
|
|
|
41
41
|
|
|
42
42
|
## Choose an integration
|
|
43
43
|
|
|
44
|
-
All renderers return text. JavaScript imports functions directly. CLI, Bash, and
|
|
44
|
+
All renderers return text. JavaScript imports functions directly. CLI, Bash, Python, and Swift integrations pass JSON-compatible data to a named renderer.
|
|
45
45
|
|
|
46
46
|
### JavaScript
|
|
47
47
|
|
|
@@ -84,7 +84,7 @@ Use renderer names from the tables below, for example `status`, `row`, `diagnost
|
|
|
84
84
|
|
|
85
85
|
### Bash
|
|
86
86
|
|
|
87
|
-
Source the Bash adapter, then use convenience helpers for common primitive renderers:
|
|
87
|
+
Source the Bash adapter, then use convenience helpers for common primitive and per-script pattern renderers:
|
|
88
88
|
|
|
89
89
|
```bash
|
|
90
90
|
source "$(cli-style adapter-path bash)"
|
|
@@ -98,6 +98,11 @@ cli_style_row "Bundle" "is 22.3 KB, above the 12.0 KB budget" failed --plain
|
|
|
98
98
|
command="$(cli_style_span "npm run docs:readme" info --plain)"
|
|
99
99
|
cli_style_hint "Run $command before release" --plain
|
|
100
100
|
cli_style_divider "Project setup" --plain
|
|
101
|
+
|
|
102
|
+
cli_style_command_result success "Build passed" "bun run test:unit" 0 "1.2s" "184 tests" --plain
|
|
103
|
+
cli_style_task_summary partial "Adopt cli-style" "Bash wrappers added" "Updated adapter" "Update scripts" --plain
|
|
104
|
+
cli_style_confirmation_result confirmed "Publish release" "v0.6.0" "Tag push starts npm publish" --plain
|
|
105
|
+
cli_style_next_step_block "Update helpers scripts" "Wrappers are now available" "scripts/setup.sh --check" "" --plain
|
|
101
106
|
```
|
|
102
107
|
|
|
103
108
|
The convenience functions build JSON internally, so callers do not need to escape quotes, backslashes, or paths by hand.
|
|
@@ -108,7 +113,7 @@ Set `CLI_STYLE_BIN` when `cli-style` is not on `PATH`:
|
|
|
108
113
|
CLI_STYLE_BIN="/path/to/cli-style" cli_style_status success "Build passed" "" --plain
|
|
109
114
|
```
|
|
110
115
|
|
|
111
|
-
Use `cli_style_render` for literal or prebuilt JSON, especially for
|
|
116
|
+
Use `cli_style_render` for literal or prebuilt JSON, especially for aggregate patterns or multi-item pattern fields:
|
|
112
117
|
|
|
113
118
|
```bash
|
|
114
119
|
cli_style_render diagnostic-report --profile diagnostic <<'JSON'
|
|
@@ -126,21 +131,37 @@ JSON
|
|
|
126
131
|
|
|
127
132
|
### Python
|
|
128
133
|
|
|
129
|
-
Add the adapter directory to `PYTHONPATH`, then import the generic `render()` helper:
|
|
134
|
+
Add the adapter directory to `PYTHONPATH`, then import the generic `render()` helper or convenience functions:
|
|
130
135
|
|
|
131
136
|
```bash
|
|
132
137
|
export PYTHONPATH="$(cli-style adapter-path python):$PYTHONPATH"
|
|
133
138
|
```
|
|
134
139
|
|
|
140
|
+
```python
|
|
141
|
+
from cli_style import status, row, span, hint, divider
|
|
142
|
+
|
|
143
|
+
kwargs = {"binary": "./bin/cli-style.js", "plain": True}
|
|
144
|
+
|
|
145
|
+
print(status("success", "Build passed", "184 tests", **kwargs))
|
|
146
|
+
print(row("Workspace", "/path/to/project", **kwargs))
|
|
147
|
+
print(row("Bundle", "over budget", "failed", **kwargs))
|
|
148
|
+
command = span("npm run docs:readme", "info", **kwargs)
|
|
149
|
+
print(hint(f"Run {command} before release", **kwargs))
|
|
150
|
+
print(divider("Project setup", **kwargs))
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Convenience functions build the renderer payload internally, so callers pass plain Python strings without hand-building JSON.
|
|
154
|
+
|
|
155
|
+
Pass `binary=` when `cli-style` is not on `PATH`. The generic `render()` helper remains for aggregate patterns or multi-item fields:
|
|
156
|
+
|
|
135
157
|
```python
|
|
136
158
|
from cli_style import render
|
|
137
159
|
|
|
138
160
|
output = render(
|
|
139
|
-
"
|
|
161
|
+
"diagnostic-report",
|
|
140
162
|
{
|
|
141
|
-
"
|
|
142
|
-
"
|
|
143
|
-
"detail": "184 tests",
|
|
163
|
+
"title": "Release checks",
|
|
164
|
+
"checks": [{"name": "README", "result": "success"}],
|
|
144
165
|
},
|
|
145
166
|
profile="diagnostic",
|
|
146
167
|
)
|
|
@@ -148,42 +169,75 @@ output = render(
|
|
|
148
169
|
print(output)
|
|
149
170
|
```
|
|
150
171
|
|
|
151
|
-
|
|
172
|
+
### Swift
|
|
152
173
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
174
|
+
Add the adapter file to your Swift project, then use the `CliStyle` enum for convenience functions or the generic `render` method:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
cli-style adapter-path swift
|
|
178
|
+
# Returns the path to CliStyle.swift for inclusion in your project
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
```swift
|
|
182
|
+
let options = CliStyleOptions(binary: "./bin/cli-style.js", isPlain: true)
|
|
183
|
+
|
|
184
|
+
print(try CliStyle.status(type: "success", label: "Build passed", detail: "184 tests", options: options))
|
|
185
|
+
print(try CliStyle.row(label: "Workspace", value: "/path/to/project", options: options))
|
|
186
|
+
print(try CliStyle.row(label: "Bundle", value: "over budget", result: "failed", options: options))
|
|
187
|
+
let command = try CliStyle.span(value: "npm run docs:readme", tone: "info", options: options)
|
|
188
|
+
print(try CliStyle.hint(message: "Run " + command + " before release", options: options))
|
|
189
|
+
print(try CliStyle.divider(label: "Project setup", options: options))
|
|
190
|
+
|
|
191
|
+
print(try CliStyle.commandResult(result: "success", summary: "Build passed", command: "bun run test:unit", exitCode: 0, duration: "1.2s", detail: "184 tests", options: options))
|
|
192
|
+
print(try CliStyle.taskSummary(result: "partial", task: "Adopt cli-style", summary: "Wrappers added", completed: "Updated adapter", remaining: "Update scripts", options: options))
|
|
193
|
+
print(try CliStyle.confirmationResult(state: "confirmed", action: "Publish release", item: "v0.6.0", detail: "Tag push starts npm publish", options: options))
|
|
194
|
+
print(try CliStyle.nextStepBlock(nextStep: "Update helpers scripts", reason: "Wrappers are now available", command: "scripts/setup.sh --check", alternative: "", options: options))
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Convenience functions build the renderer payload internally, so callers pass plain Swift strings without hand-building JSON.
|
|
198
|
+
|
|
199
|
+
Pass `binary:` in `CliStyleOptions` when `cli-style` is not on `PATH`. The generic `CliStyle.render(_:data:options:)` method remains for aggregate patterns or multi-item fields:
|
|
200
|
+
|
|
201
|
+
```swift
|
|
202
|
+
let output = try CliStyle.render(
|
|
203
|
+
"diagnostic-report",
|
|
204
|
+
data: [
|
|
205
|
+
"title": "Release checks",
|
|
206
|
+
"checks": [["name": "README", "result": "success"]],
|
|
207
|
+
],
|
|
208
|
+
options: CliStyleOptions(profile: "diagnostic")
|
|
162
209
|
)
|
|
210
|
+
|
|
211
|
+
print(output)
|
|
163
212
|
```
|
|
164
213
|
|
|
165
214
|
## Available renderers
|
|
166
215
|
|
|
167
216
|
Renderer names are stable for `cli-style render`, `cli_style_render`, and Python `render()`. JavaScript uses camel-case function names.
|
|
168
217
|
|
|
169
|
-
| Renderer
|
|
170
|
-
|
|
|
171
|
-
| `status`
|
|
172
|
-
| `row`
|
|
173
|
-
| `span`
|
|
174
|
-
| `hint`
|
|
175
|
-
| `divider`
|
|
176
|
-
| `
|
|
177
|
-
| `
|
|
178
|
-
| `
|
|
179
|
-
| `
|
|
180
|
-
| `
|
|
181
|
-
| `
|
|
182
|
-
| `
|
|
183
|
-
| `
|
|
184
|
-
| `
|
|
185
|
-
|
|
186
|
-
|
|
218
|
+
| Renderer | JavaScript | Bash helper | Python helper | Swift helper | Purpose |
|
|
219
|
+
| --------------------- | ------------------------------------- | ------------------------------------ | ---------------------------------- | -------------------------------------- | --------------------------------------------------------------- |
|
|
220
|
+
| `status` | `status(resultType, detail, options)` | `cli_style_status type label detail` | `status(type, label, detail)` | `CliStyle.status(type, label, detail)` | Result line with symbol, label, and detail. |
|
|
221
|
+
| `row` | `row(label, value, options)` | `cli_style_row label value [result]` | `row(label, value, result)` | `CliStyle.row(label, value, result)` | Aligned label/value row, optionally marked with a result state. |
|
|
222
|
+
| `span` | `span(value, tone, options)` | `cli_style_span value [tone]` | `span(value, tone)` | `CliStyle.span(value, tone)` | Inline colour or weight for a word, command, file, or value. |
|
|
223
|
+
| `hint` | `hint(message, options)` | `cli_style_hint message` | `hint(message)` | `CliStyle.hint(message)` | Informational hint line. |
|
|
224
|
+
| `divider` | `divider(options)` | `cli_style_divider label` | `divider(label)` | `CliStyle.divider(label)` | Section divider with an optional label. |
|
|
225
|
+
| `command-result` | `commandResult(options)` | `cli_style_command_result ...` | `command_result(...)` | `CliStyle.commandResult(...)` | Command execution outcome with exit code and duration. |
|
|
226
|
+
| `audit-finding` | `auditFinding(options)` | `cli_style_audit_finding ...` | `audit_finding(...)` | `CliStyle.auditFinding(...)` | Structured audit finding with evidence and recommendation. |
|
|
227
|
+
| `task-summary` | `taskSummary(options)` | `cli_style_task_summary ...` | `task_summary(...)` | `CliStyle.taskSummary(...)` | Task progress with completed and remaining items. |
|
|
228
|
+
| `confirmation-result` | `confirmationResult(options)` | `cli_style_confirmation_result ...` | `confirmation_result(...)` | `CliStyle.confirmationResult(...)` | Confirmation outcome with action and item. |
|
|
229
|
+
| `next-step-block` | `nextStepBlock(options)` | `cli_style_next_step_block ...` | `next_step_block(...)` | `CliStyle.nextStepBlock(...)` | Next-step guidance with command and alternative. |
|
|
230
|
+
| `chip` | `chip(label, tone, options)` | Use `cli_style_render` | Use `render("chip", ...)` | Use `CliStyle.render` | Compact labelled state. |
|
|
231
|
+
| `panel` | `panel(options)` | Use `cli_style_render` | Use `render("panel", ...)` | Use `CliStyle.render` | Framed content block for grouped output. |
|
|
232
|
+
| `table` | `table(options)` | Use `cli_style_render` | Use `render("table", ...)` | Use `CliStyle.render` | Tabular records with narrow-width fallback. |
|
|
233
|
+
| `progress-bar` | `progressBar(options)` | Use `cli_style_render` | Use `render("progress-bar", ...)` | Use `CliStyle.render` | Single progress value with numeric text. |
|
|
234
|
+
| `bar-chart` | `barChart(options)` | Use `cli_style_render` | Use `render("bar-chart", ...)` | Use `CliStyle.render` | Multi-row bar chart. |
|
|
235
|
+
| `step` | `step(label, state, options)` | Use `cli_style_render` | Use `render("step", ...)` | Use `CliStyle.render` | One workflow step. |
|
|
236
|
+
| `step-progress` | `stepProgress(options)` | Use `cli_style_render` | Use `render("step-progress", ...)` | Use `CliStyle.render` | Numbered workflow steps. |
|
|
237
|
+
| `empty-state` | `emptyState(title, detail, options)` | Use `cli_style_render` | Use `render("empty-state", ...)` | Use `CliStyle.render` | Empty result message. |
|
|
238
|
+
| `error-block` | `errorBlock(title, lines, options)` | Use `cli_style_render` | Use `render("error-block", ...)` | Use `CliStyle.render` | Structured error block. |
|
|
239
|
+
|
|
240
|
+
Use `render("<renderer>", data, ...)` (Python) or `CliStyle.render(_:data:options:)` (Swift) for aggregate patterns (`diagnostic-report`, `agent-transcript`, `compact-data-table`) or any renderer with multi-item fields.
|
|
187
241
|
|
|
188
242
|
## Primitive data shapes
|
|
189
243
|
|
|
@@ -277,6 +331,20 @@ Patterns are built-in composite renderers for common report shapes. They use the
|
|
|
277
331
|
|
|
278
332
|
For visual examples, run `cli-style gallery --section patterns`.
|
|
279
333
|
|
|
334
|
+
The Bash adapter also exposes scalar convenience wrappers for per-script usage:
|
|
335
|
+
|
|
336
|
+
| Bash helper | Renderer | Arguments before render flags |
|
|
337
|
+
| ------------------------------- | --------------------- | -------------------------------------------------------------------------- |
|
|
338
|
+
| `cli_style_command_result` | `command-result` | `result`, `summary`, `command`, `exit_code`, `duration`, `detail` |
|
|
339
|
+
| `cli_style_audit_finding` | `audit-finding` | `result`, `finding`, `location`, `recommendation`, `evidence`, `reference` |
|
|
340
|
+
| `cli_style_task_summary` | `task-summary` | `result`, `task`, `summary`, `completed`, `remaining` |
|
|
341
|
+
| `cli_style_confirmation_result` | `confirmation-result` | `state`, `action`, `item`, `detail` |
|
|
342
|
+
| `cli_style_next_step_block` | `next-step-block` | `next`, `reason`, `command`, `alternative` |
|
|
343
|
+
|
|
344
|
+
Pass an empty string for optional positional fields you want to skip. Use `cli_style_render` or `cli_style_render_json` for aggregate reports, tables, transcripts, and multi-item arrays.
|
|
345
|
+
|
|
346
|
+
Swift equivalents are available as `CliStyle.commandResult(...)`, `CliStyle.auditFinding(...)`, `CliStyle.taskSummary(...)`, `CliStyle.confirmationResult(...)`, and `CliStyle.nextStepBlock(...)`. Use `CliStyle.render` for aggregate reports, tables, transcripts, and multi-item arrays.
|
|
347
|
+
|
|
280
348
|
### Pattern example
|
|
281
349
|
|
|
282
350
|
JavaScript:
|
|
@@ -464,6 +532,20 @@ Use `createCliStyle()` to resolve these options from `argv`, `env`, `stdout`, CI
|
|
|
464
532
|
| `binary` | Path or command name for the `cli-style` binary. |
|
|
465
533
|
| `extra_args` | Extra CLI arguments to pass through. |
|
|
466
534
|
|
|
535
|
+
### Swift options
|
|
536
|
+
|
|
537
|
+
Swift options are passed via the `CliStyleOptions` struct:
|
|
538
|
+
|
|
539
|
+
| Property | Purpose |
|
|
540
|
+
| ------------- | ------------------------------------------------ |
|
|
541
|
+
| `binary` | Path or command name for the `cli-style` binary. |
|
|
542
|
+
| `profile` | Render with a named profile. |
|
|
543
|
+
| `width` | Override detected output width. |
|
|
544
|
+
| `isPlain` | Disable colour and Unicode decoration. |
|
|
545
|
+
| `isNoColour` | Disable ANSI colour. |
|
|
546
|
+
| `isNoUnicode` | Use ASCII symbols. |
|
|
547
|
+
| `extraArgs` | Extra CLI arguments to pass through. |
|
|
548
|
+
|
|
467
549
|
## Profiles
|
|
468
550
|
|
|
469
551
|
| Profile | Behaviour |
|
|
@@ -41,6 +41,41 @@ cli_style_json_string() {
|
|
|
41
41
|
printf '"%s"' "$value"
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
# Escape Bash strings for a JSON string array.
|
|
45
|
+
#
|
|
46
|
+
# @param {string} ...
|
|
47
|
+
# String values to encode. Empty values are skipped.
|
|
48
|
+
cli_style_json_string_array() {
|
|
49
|
+
local item
|
|
50
|
+
local separator=""
|
|
51
|
+
|
|
52
|
+
printf '['
|
|
53
|
+
|
|
54
|
+
for item in "$@"; do
|
|
55
|
+
if [[ "$item" != "" ]]; then
|
|
56
|
+
printf '%s' "$separator"
|
|
57
|
+
cli_style_json_string "$item"
|
|
58
|
+
separator=","
|
|
59
|
+
fi
|
|
60
|
+
done
|
|
61
|
+
|
|
62
|
+
printf ']'
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
# Render a Bash value as a JSON integer or null.
|
|
66
|
+
#
|
|
67
|
+
# @param {string} value
|
|
68
|
+
# Integer value to encode.
|
|
69
|
+
cli_style_json_integer_or_null() {
|
|
70
|
+
local value="${1:-}"
|
|
71
|
+
|
|
72
|
+
if [[ "$value" =~ ^-?[0-9]+$ ]]; then
|
|
73
|
+
printf '%s' "$value"
|
|
74
|
+
else
|
|
75
|
+
printf 'null'
|
|
76
|
+
fi
|
|
77
|
+
}
|
|
78
|
+
|
|
44
79
|
# Render a JSON object through the shared cli-style command.
|
|
45
80
|
#
|
|
46
81
|
# @param {string} renderer
|
|
@@ -208,3 +243,168 @@ cli_style_hint() {
|
|
|
208
243
|
|
|
209
244
|
cli_style_render_json hint "$json" "$@"
|
|
210
245
|
}
|
|
246
|
+
|
|
247
|
+
# Render a command result pattern from Bash string values.
|
|
248
|
+
#
|
|
249
|
+
# @param {string} result
|
|
250
|
+
# Result type such as success, failed, warning, or skipped.
|
|
251
|
+
# @param {string} summary
|
|
252
|
+
# Summary line.
|
|
253
|
+
# @param {string} command
|
|
254
|
+
# Optional command text.
|
|
255
|
+
# @param {string} exit_code
|
|
256
|
+
# Optional integer exit code.
|
|
257
|
+
# @param {string} duration
|
|
258
|
+
# Optional duration text.
|
|
259
|
+
# @param {string} detail
|
|
260
|
+
# Optional detail line.
|
|
261
|
+
# @param {string} ...
|
|
262
|
+
# Render flags passed through to `cli-style render`.
|
|
263
|
+
cli_style_command_result() {
|
|
264
|
+
local result="${1:-}"
|
|
265
|
+
local summary="${2:-}"
|
|
266
|
+
local command="${3:-}"
|
|
267
|
+
local exit_code="${4:-}"
|
|
268
|
+
local duration="${5:-}"
|
|
269
|
+
local detail="${6:-}"
|
|
270
|
+
local json
|
|
271
|
+
|
|
272
|
+
if (($# >= 6)); then
|
|
273
|
+
shift 6
|
|
274
|
+
else
|
|
275
|
+
set --
|
|
276
|
+
fi
|
|
277
|
+
|
|
278
|
+
json="{\"result\":$(cli_style_json_string "$result"),\"summary\":$(cli_style_json_string "$summary"),\"command\":$(cli_style_json_string "$command"),\"exitCode\":$(cli_style_json_integer_or_null "$exit_code"),\"duration\":$(cli_style_json_string "$duration"),\"details\":$(cli_style_json_string_array "$detail")}"
|
|
279
|
+
|
|
280
|
+
cli_style_render_json command-result "$json" "$@"
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
# Render an audit finding pattern from Bash string values.
|
|
284
|
+
#
|
|
285
|
+
# @param {string} result
|
|
286
|
+
# Result type such as success, failed, warning, or skipped.
|
|
287
|
+
# @param {string} finding
|
|
288
|
+
# Finding summary.
|
|
289
|
+
# @param {string} location
|
|
290
|
+
# Optional source location.
|
|
291
|
+
# @param {string} recommendation
|
|
292
|
+
# Optional recommendation text.
|
|
293
|
+
# @param {string} evidence
|
|
294
|
+
# Optional evidence line.
|
|
295
|
+
# @param {string} reference
|
|
296
|
+
# Optional reference line.
|
|
297
|
+
# @param {string} ...
|
|
298
|
+
# Render flags passed through to `cli-style render`.
|
|
299
|
+
cli_style_audit_finding() {
|
|
300
|
+
local result="${1:-}"
|
|
301
|
+
local finding="${2:-}"
|
|
302
|
+
local location="${3:-}"
|
|
303
|
+
local recommendation="${4:-}"
|
|
304
|
+
local evidence="${5:-}"
|
|
305
|
+
local reference="${6:-}"
|
|
306
|
+
local json
|
|
307
|
+
|
|
308
|
+
if (($# >= 6)); then
|
|
309
|
+
shift 6
|
|
310
|
+
else
|
|
311
|
+
set --
|
|
312
|
+
fi
|
|
313
|
+
|
|
314
|
+
json="{\"result\":$(cli_style_json_string "$result"),\"finding\":$(cli_style_json_string "$finding"),\"location\":$(cli_style_json_string "$location"),\"recommendation\":$(cli_style_json_string "$recommendation"),\"evidence\":$(cli_style_json_string_array "$evidence"),\"references\":$(cli_style_json_string_array "$reference")}"
|
|
315
|
+
|
|
316
|
+
cli_style_render_json audit-finding "$json" "$@"
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
# Render a task summary pattern from Bash string values.
|
|
320
|
+
#
|
|
321
|
+
# @param {string} result
|
|
322
|
+
# Result type such as success, failed, warning, or skipped.
|
|
323
|
+
# @param {string} task
|
|
324
|
+
# Task label.
|
|
325
|
+
# @param {string} summary
|
|
326
|
+
# Optional summary detail.
|
|
327
|
+
# @param {string} completed
|
|
328
|
+
# Optional completed item.
|
|
329
|
+
# @param {string} remaining
|
|
330
|
+
# Optional remaining item.
|
|
331
|
+
# @param {string} ...
|
|
332
|
+
# Render flags passed through to `cli-style render`.
|
|
333
|
+
cli_style_task_summary() {
|
|
334
|
+
local result="${1:-}"
|
|
335
|
+
local task="${2:-}"
|
|
336
|
+
local summary="${3:-}"
|
|
337
|
+
local completed="${4:-}"
|
|
338
|
+
local remaining="${5:-}"
|
|
339
|
+
local json
|
|
340
|
+
|
|
341
|
+
if (($# >= 5)); then
|
|
342
|
+
shift 5
|
|
343
|
+
else
|
|
344
|
+
set --
|
|
345
|
+
fi
|
|
346
|
+
|
|
347
|
+
json="{\"result\":$(cli_style_json_string "$result"),\"task\":$(cli_style_json_string "$task"),\"summary\":$(cli_style_json_string "$summary"),\"completed\":$(cli_style_json_string_array "$completed"),\"remaining\":$(cli_style_json_string_array "$remaining")}"
|
|
348
|
+
|
|
349
|
+
cli_style_render_json task-summary "$json" "$@"
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
# Render a confirmation result pattern from Bash string values.
|
|
353
|
+
#
|
|
354
|
+
# @param {string} state
|
|
355
|
+
# Confirmation state such as confirmed, cancelled, or skipped.
|
|
356
|
+
# @param {string} action
|
|
357
|
+
# Action label.
|
|
358
|
+
# @param {string} item
|
|
359
|
+
# Optional item label.
|
|
360
|
+
# @param {string} detail
|
|
361
|
+
# Optional detail text.
|
|
362
|
+
# @param {string} ...
|
|
363
|
+
# Render flags passed through to `cli-style render`.
|
|
364
|
+
cli_style_confirmation_result() {
|
|
365
|
+
local state="${1:-}"
|
|
366
|
+
local action="${2:-}"
|
|
367
|
+
local item="${3:-}"
|
|
368
|
+
local detail="${4:-}"
|
|
369
|
+
local json
|
|
370
|
+
|
|
371
|
+
if (($# >= 4)); then
|
|
372
|
+
shift 4
|
|
373
|
+
else
|
|
374
|
+
set --
|
|
375
|
+
fi
|
|
376
|
+
|
|
377
|
+
json="{\"state\":$(cli_style_json_string "$state"),\"action\":$(cli_style_json_string "$action"),\"item\":$(cli_style_json_string "$item"),\"detail\":$(cli_style_json_string "$detail")}"
|
|
378
|
+
|
|
379
|
+
cli_style_render_json confirmation-result "$json" "$@"
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
# Render a next-step block pattern from Bash string values.
|
|
383
|
+
#
|
|
384
|
+
# @param {string} next
|
|
385
|
+
# Next step text.
|
|
386
|
+
# @param {string} reason
|
|
387
|
+
# Optional reason text.
|
|
388
|
+
# @param {string} command
|
|
389
|
+
# Optional command line.
|
|
390
|
+
# @param {string} alternative
|
|
391
|
+
# Optional alternative line.
|
|
392
|
+
# @param {string} ...
|
|
393
|
+
# Render flags passed through to `cli-style render`.
|
|
394
|
+
cli_style_next_step_block() {
|
|
395
|
+
local next="${1:-}"
|
|
396
|
+
local reason="${2:-}"
|
|
397
|
+
local command="${3:-}"
|
|
398
|
+
local alternative="${4:-}"
|
|
399
|
+
local json
|
|
400
|
+
|
|
401
|
+
if (($# >= 4)); then
|
|
402
|
+
shift 4
|
|
403
|
+
else
|
|
404
|
+
set --
|
|
405
|
+
fi
|
|
406
|
+
|
|
407
|
+
json="{\"next\":$(cli_style_json_string "$next"),\"reason\":$(cli_style_json_string "$reason"),\"commands\":$(cli_style_json_string_array "$command"),\"alternatives\":$(cli_style_json_string_array "$alternative")}"
|
|
408
|
+
|
|
409
|
+
cli_style_render_json next-step-block "$json" "$@"
|
|
410
|
+
}
|
|
@@ -83,6 +83,221 @@ def render(
|
|
|
83
83
|
return result.stdout.rstrip("\n")
|
|
84
84
|
|
|
85
85
|
|
|
86
|
+
# Render a result status line from Python values.
|
|
87
|
+
#
|
|
88
|
+
# @param {str} type
|
|
89
|
+
# Result type such as success, failed, warning, or skipped.
|
|
90
|
+
# @param {str} label
|
|
91
|
+
# Status label.
|
|
92
|
+
# @param {str} detail
|
|
93
|
+
# Optional detail text.
|
|
94
|
+
def status(
|
|
95
|
+
type: str,
|
|
96
|
+
label: str = "",
|
|
97
|
+
detail: str = "",
|
|
98
|
+
**kwargs: Any,
|
|
99
|
+
) -> str:
|
|
100
|
+
return render("status", {"type": type, "label": label, "detail": detail}, **kwargs)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# Render a labelled value row from Python values.
|
|
104
|
+
#
|
|
105
|
+
# @param {str} label
|
|
106
|
+
# Row label.
|
|
107
|
+
# @param {str} value
|
|
108
|
+
# Row value.
|
|
109
|
+
# @param {str} result
|
|
110
|
+
# Optional result type for a symbol and tone.
|
|
111
|
+
def row(
|
|
112
|
+
label: str,
|
|
113
|
+
value: str,
|
|
114
|
+
result: str = "",
|
|
115
|
+
**kwargs: Any,
|
|
116
|
+
) -> str:
|
|
117
|
+
return render("row", {"label": label, "value": value, "result": result}, **kwargs)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
# Render an inline span from Python values.
|
|
121
|
+
#
|
|
122
|
+
# @param {str} value
|
|
123
|
+
# Span text.
|
|
124
|
+
# @param {str} tone
|
|
125
|
+
# Optional colour tone, defaulting to info.
|
|
126
|
+
def span(
|
|
127
|
+
value: str,
|
|
128
|
+
tone: str = "info",
|
|
129
|
+
**kwargs: Any,
|
|
130
|
+
) -> str:
|
|
131
|
+
return render("span", {"value": value, "tone": tone}, **kwargs)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
# Render a divider from a Python value.
|
|
135
|
+
#
|
|
136
|
+
# @param {str} label
|
|
137
|
+
# Optional divider label.
|
|
138
|
+
def divider(
|
|
139
|
+
label: str = "",
|
|
140
|
+
**kwargs: Any,
|
|
141
|
+
) -> str:
|
|
142
|
+
return render("divider", {"label": label}, **kwargs)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
# Render a hint from a Python value.
|
|
146
|
+
#
|
|
147
|
+
# @param {str} message
|
|
148
|
+
# Hint message.
|
|
149
|
+
def hint(
|
|
150
|
+
message: str,
|
|
151
|
+
**kwargs: Any,
|
|
152
|
+
) -> str:
|
|
153
|
+
return render("hint", {"message": message}, **kwargs)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
# Render a command result pattern from Python values.
|
|
157
|
+
#
|
|
158
|
+
# @param {str} result
|
|
159
|
+
# Result type such as success, failed, warning, or skipped.
|
|
160
|
+
# @param {str} summary
|
|
161
|
+
# Summary line.
|
|
162
|
+
# @param {str} command
|
|
163
|
+
# Optional command text.
|
|
164
|
+
# @param {int|None} exit_code
|
|
165
|
+
# Optional integer exit code.
|
|
166
|
+
# @param {str} duration
|
|
167
|
+
# Optional duration text.
|
|
168
|
+
# @param {str} detail
|
|
169
|
+
# Optional detail line.
|
|
170
|
+
def command_result(
|
|
171
|
+
result: str,
|
|
172
|
+
summary: str,
|
|
173
|
+
command: str = "",
|
|
174
|
+
exit_code: int | None = None,
|
|
175
|
+
duration: str = "",
|
|
176
|
+
detail: str = "",
|
|
177
|
+
**kwargs: Any,
|
|
178
|
+
) -> str:
|
|
179
|
+
return render("command-result", {
|
|
180
|
+
"result": result,
|
|
181
|
+
"summary": summary,
|
|
182
|
+
"command": command,
|
|
183
|
+
"exitCode": exit_code,
|
|
184
|
+
"duration": duration,
|
|
185
|
+
"details": [detail] if detail else [],
|
|
186
|
+
}, **kwargs)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
# Render an audit finding pattern from Python values.
|
|
190
|
+
#
|
|
191
|
+
# @param {str} result
|
|
192
|
+
# Result type such as success, failed, warning, or skipped.
|
|
193
|
+
# @param {str} finding
|
|
194
|
+
# Finding summary.
|
|
195
|
+
# @param {str} location
|
|
196
|
+
# Optional source location.
|
|
197
|
+
# @param {str} recommendation
|
|
198
|
+
# Optional recommendation text.
|
|
199
|
+
# @param {str} evidence
|
|
200
|
+
# Optional evidence line.
|
|
201
|
+
# @param {str} reference
|
|
202
|
+
# Optional reference line.
|
|
203
|
+
def audit_finding(
|
|
204
|
+
result: str,
|
|
205
|
+
finding: str,
|
|
206
|
+
location: str = "",
|
|
207
|
+
recommendation: str = "",
|
|
208
|
+
evidence: str = "",
|
|
209
|
+
reference: str = "",
|
|
210
|
+
**kwargs: Any,
|
|
211
|
+
) -> str:
|
|
212
|
+
return render("audit-finding", {
|
|
213
|
+
"result": result,
|
|
214
|
+
"finding": finding,
|
|
215
|
+
"location": location,
|
|
216
|
+
"recommendation": recommendation,
|
|
217
|
+
"evidence": [evidence] if evidence else [],
|
|
218
|
+
"references": [reference] if reference else [],
|
|
219
|
+
}, **kwargs)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
# Render a task summary pattern from Python values.
|
|
223
|
+
#
|
|
224
|
+
# @param {str} result
|
|
225
|
+
# Result type such as success, failed, warning, or skipped.
|
|
226
|
+
# @param {str} task
|
|
227
|
+
# Task label.
|
|
228
|
+
# @param {str} summary
|
|
229
|
+
# Optional summary detail.
|
|
230
|
+
# @param {str} completed
|
|
231
|
+
# Optional completed item.
|
|
232
|
+
# @param {str} remaining
|
|
233
|
+
# Optional remaining item.
|
|
234
|
+
def task_summary(
|
|
235
|
+
result: str,
|
|
236
|
+
task: str,
|
|
237
|
+
summary: str = "",
|
|
238
|
+
completed: str = "",
|
|
239
|
+
remaining: str = "",
|
|
240
|
+
**kwargs: Any,
|
|
241
|
+
) -> str:
|
|
242
|
+
return render("task-summary", {
|
|
243
|
+
"result": result,
|
|
244
|
+
"task": task,
|
|
245
|
+
"summary": summary,
|
|
246
|
+
"completed": [completed] if completed else [],
|
|
247
|
+
"remaining": [remaining] if remaining else [],
|
|
248
|
+
}, **kwargs)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
# Render a confirmation result pattern from Python values.
|
|
252
|
+
#
|
|
253
|
+
# @param {str} state
|
|
254
|
+
# Confirmation state such as confirmed, cancelled, or skipped.
|
|
255
|
+
# @param {str} action
|
|
256
|
+
# Action label.
|
|
257
|
+
# @param {str} item
|
|
258
|
+
# Optional item label.
|
|
259
|
+
# @param {str} detail
|
|
260
|
+
# Optional detail text.
|
|
261
|
+
def confirmation_result(
|
|
262
|
+
state: str,
|
|
263
|
+
action: str,
|
|
264
|
+
item: str = "",
|
|
265
|
+
detail: str = "",
|
|
266
|
+
**kwargs: Any,
|
|
267
|
+
) -> str:
|
|
268
|
+
return render("confirmation-result", {
|
|
269
|
+
"state": state,
|
|
270
|
+
"action": action,
|
|
271
|
+
"item": item,
|
|
272
|
+
"detail": detail,
|
|
273
|
+
}, **kwargs)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
# Render a next-step block pattern from Python values.
|
|
277
|
+
#
|
|
278
|
+
# @param {str} next_step
|
|
279
|
+
# Next step text.
|
|
280
|
+
# @param {str} reason
|
|
281
|
+
# Optional reason text.
|
|
282
|
+
# @param {str} command
|
|
283
|
+
# Optional command line.
|
|
284
|
+
# @param {str} alternative
|
|
285
|
+
# Optional alternative line.
|
|
286
|
+
def next_step_block(
|
|
287
|
+
next_step: str,
|
|
288
|
+
reason: str = "",
|
|
289
|
+
command: str = "",
|
|
290
|
+
alternative: str = "",
|
|
291
|
+
**kwargs: Any,
|
|
292
|
+
) -> str:
|
|
293
|
+
return render("next-step-block", {
|
|
294
|
+
"next": next_step,
|
|
295
|
+
"reason": reason,
|
|
296
|
+
"commands": [command] if command else [],
|
|
297
|
+
"alternatives": [alternative] if alternative else [],
|
|
298
|
+
}, **kwargs)
|
|
299
|
+
|
|
300
|
+
|
|
86
301
|
# Resolve a command name or executable path for subprocess use.
|
|
87
302
|
#
|
|
88
303
|
# @param {str} binary
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
// Error types for cli-style adapter failures.
|
|
4
|
+
enum CliStyleError: Error {
|
|
5
|
+
case notFound(String)
|
|
6
|
+
case renderFailed(String)
|
|
7
|
+
case invalidInput(String)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Options for controlling cli-style render behaviour.
|
|
11
|
+
struct CliStyleOptions {
|
|
12
|
+
// Binary path or command name.
|
|
13
|
+
var binary: String = "cli-style"
|
|
14
|
+
|
|
15
|
+
// Profile name such as human, agent, or json.
|
|
16
|
+
var profile: String? = nil
|
|
17
|
+
|
|
18
|
+
// Terminal width in characters.
|
|
19
|
+
var width: Int? = nil
|
|
20
|
+
|
|
21
|
+
// Force plain text output.
|
|
22
|
+
var isPlain: Bool = false
|
|
23
|
+
|
|
24
|
+
// Disable colour output.
|
|
25
|
+
var isNoColour: Bool = false
|
|
26
|
+
|
|
27
|
+
// Disable Unicode symbols.
|
|
28
|
+
var isNoUnicode: Bool = false
|
|
29
|
+
|
|
30
|
+
// Additional arguments passed through to the binary.
|
|
31
|
+
var extraArgs: [String] = []
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Render caller data through the shared cli-style binary.
|
|
35
|
+
enum CliStyle {
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
Render a dictionary through `cli-style render`.
|
|
39
|
+
|
|
40
|
+
- parameter renderer: Stable renderer name accepted by `cli-style render`.
|
|
41
|
+
- parameter data: JSON-serialisable renderer input.
|
|
42
|
+
- parameter options: Render options including binary path and output flags.
|
|
43
|
+
- returns: Rendered string output from cli-style.
|
|
44
|
+
- throws: `CliStyleError` if the binary is missing, input is invalid, or rendering fails.
|
|
45
|
+
*/
|
|
46
|
+
static func render(
|
|
47
|
+
_ renderer: String,
|
|
48
|
+
data: [String: Any],
|
|
49
|
+
options: CliStyleOptions = .init()
|
|
50
|
+
) throws -> String {
|
|
51
|
+
if renderer.isEmpty {
|
|
52
|
+
throw CliStyleError.invalidInput("renderer must be a non-empty string")
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let resolvedBinary = try resolveBinary(options.binary)
|
|
56
|
+
|
|
57
|
+
let jsonData: Data
|
|
58
|
+
|
|
59
|
+
do {
|
|
60
|
+
jsonData = try JSONSerialization.data(withJSONObject: data)
|
|
61
|
+
} catch {
|
|
62
|
+
throw CliStyleError.invalidInput("data is not JSON-serialisable")
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
var arguments = ["render", renderer]
|
|
66
|
+
|
|
67
|
+
if let profile = options.profile {
|
|
68
|
+
arguments += ["--profile", profile]
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if let width = options.width {
|
|
72
|
+
arguments += ["--width", String(width)]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if options.isPlain {
|
|
76
|
+
arguments.append("--plain")
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if options.isNoColour {
|
|
80
|
+
arguments.append("--no-colour")
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if options.isNoUnicode {
|
|
84
|
+
arguments.append("--no-unicode")
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
arguments += options.extraArgs
|
|
88
|
+
|
|
89
|
+
let process = Process()
|
|
90
|
+
process.executableURL = URL(fileURLWithPath: resolvedBinary)
|
|
91
|
+
process.arguments = arguments
|
|
92
|
+
|
|
93
|
+
let stdinPipe = Pipe()
|
|
94
|
+
let stdoutPipe = Pipe()
|
|
95
|
+
let stderrPipe = Pipe()
|
|
96
|
+
process.standardInput = stdinPipe
|
|
97
|
+
process.standardOutput = stdoutPipe
|
|
98
|
+
process.standardError = stderrPipe
|
|
99
|
+
|
|
100
|
+
do {
|
|
101
|
+
try process.run()
|
|
102
|
+
} catch {
|
|
103
|
+
throw CliStyleError.renderFailed("failed to launch cli-style: \(error.localizedDescription)")
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
stdinPipe.fileHandleForWriting.write(jsonData)
|
|
107
|
+
stdinPipe.fileHandleForWriting.closeFile()
|
|
108
|
+
|
|
109
|
+
process.waitUntilExit()
|
|
110
|
+
|
|
111
|
+
let stdoutData = stdoutPipe.fileHandleForReading.readDataToEndOfFile()
|
|
112
|
+
let stderrData = stderrPipe.fileHandleForReading.readDataToEndOfFile()
|
|
113
|
+
|
|
114
|
+
if process.terminationStatus != 0 {
|
|
115
|
+
let stderrText = String(data: stderrData, encoding: .utf8)?
|
|
116
|
+
.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
|
117
|
+
let message = stderrText.isEmpty
|
|
118
|
+
? "cli-style render failed with exit code \(process.terminationStatus)"
|
|
119
|
+
: stderrText
|
|
120
|
+
|
|
121
|
+
throw CliStyleError.renderFailed(message)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
let stdoutText = String(data: stdoutData, encoding: .utf8) ?? ""
|
|
125
|
+
return stdoutText.trimmingCharacters(in: .newlines)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
Render a result status line.
|
|
130
|
+
|
|
131
|
+
- parameter type: Result type such as success, failed, warning, or skipped.
|
|
132
|
+
- parameter label: Status label.
|
|
133
|
+
- parameter detail: Optional detail text.
|
|
134
|
+
- parameter options: Render options.
|
|
135
|
+
- returns: Rendered status line.
|
|
136
|
+
- throws: `CliStyleError` on failure.
|
|
137
|
+
*/
|
|
138
|
+
static func status(
|
|
139
|
+
type: String,
|
|
140
|
+
label: String = "",
|
|
141
|
+
detail: String = "",
|
|
142
|
+
options: CliStyleOptions = .init()
|
|
143
|
+
) throws -> String {
|
|
144
|
+
try render("status", data: [
|
|
145
|
+
"type": type,
|
|
146
|
+
"label": label,
|
|
147
|
+
"detail": detail,
|
|
148
|
+
], options: options)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
Render a labelled value row.
|
|
153
|
+
|
|
154
|
+
- parameter label: Row label.
|
|
155
|
+
- parameter value: Row value.
|
|
156
|
+
- parameter result: Optional result type for a symbol and tone.
|
|
157
|
+
- parameter options: Render options.
|
|
158
|
+
- returns: Rendered row line.
|
|
159
|
+
- throws: `CliStyleError` on failure.
|
|
160
|
+
*/
|
|
161
|
+
static func row(
|
|
162
|
+
label: String,
|
|
163
|
+
value: String,
|
|
164
|
+
result: String = "",
|
|
165
|
+
options: CliStyleOptions = .init()
|
|
166
|
+
) throws -> String {
|
|
167
|
+
try render("row", data: [
|
|
168
|
+
"label": label,
|
|
169
|
+
"value": value,
|
|
170
|
+
"result": result,
|
|
171
|
+
], options: options)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
Render an inline span.
|
|
176
|
+
|
|
177
|
+
- parameter value: Span text.
|
|
178
|
+
- parameter tone: Optional colour tone, defaulting to info.
|
|
179
|
+
- parameter options: Render options.
|
|
180
|
+
- returns: Rendered span string.
|
|
181
|
+
- throws: `CliStyleError` on failure.
|
|
182
|
+
*/
|
|
183
|
+
static func span(
|
|
184
|
+
value: String,
|
|
185
|
+
tone: String = "info",
|
|
186
|
+
options: CliStyleOptions = .init()
|
|
187
|
+
) throws -> String {
|
|
188
|
+
try render("span", data: [
|
|
189
|
+
"value": value,
|
|
190
|
+
"tone": tone,
|
|
191
|
+
], options: options)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
Render a divider.
|
|
196
|
+
|
|
197
|
+
- parameter label: Optional divider label.
|
|
198
|
+
- parameter options: Render options.
|
|
199
|
+
- returns: Rendered divider line.
|
|
200
|
+
- throws: `CliStyleError` on failure.
|
|
201
|
+
*/
|
|
202
|
+
static func divider(
|
|
203
|
+
label: String = "",
|
|
204
|
+
options: CliStyleOptions = .init()
|
|
205
|
+
) throws -> String {
|
|
206
|
+
try render("divider", data: [
|
|
207
|
+
"label": label,
|
|
208
|
+
], options: options)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
Render a hint.
|
|
213
|
+
|
|
214
|
+
- parameter message: Hint message.
|
|
215
|
+
- parameter options: Render options.
|
|
216
|
+
- returns: Rendered hint line.
|
|
217
|
+
- throws: `CliStyleError` on failure.
|
|
218
|
+
*/
|
|
219
|
+
static func hint(
|
|
220
|
+
message: String,
|
|
221
|
+
options: CliStyleOptions = .init()
|
|
222
|
+
) throws -> String {
|
|
223
|
+
try render("hint", data: [
|
|
224
|
+
"message": message,
|
|
225
|
+
], options: options)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
Render a command result pattern.
|
|
230
|
+
|
|
231
|
+
- parameter result: Result type such as success, failed, warning, or skipped.
|
|
232
|
+
- parameter summary: Summary line.
|
|
233
|
+
- parameter command: Optional command text.
|
|
234
|
+
- parameter exitCode: Optional integer exit code.
|
|
235
|
+
- parameter duration: Optional duration text.
|
|
236
|
+
- parameter detail: Optional detail line.
|
|
237
|
+
- parameter options: Render options.
|
|
238
|
+
- returns: Rendered command result block.
|
|
239
|
+
- throws: `CliStyleError` on failure.
|
|
240
|
+
*/
|
|
241
|
+
static func commandResult(
|
|
242
|
+
result: String,
|
|
243
|
+
summary: String,
|
|
244
|
+
command: String = "",
|
|
245
|
+
exitCode: Int? = nil,
|
|
246
|
+
duration: String = "",
|
|
247
|
+
detail: String = "",
|
|
248
|
+
options: CliStyleOptions = .init()
|
|
249
|
+
) throws -> String {
|
|
250
|
+
var data: [String: Any] = [
|
|
251
|
+
"result": result,
|
|
252
|
+
"summary": summary,
|
|
253
|
+
"command": command,
|
|
254
|
+
"duration": duration,
|
|
255
|
+
"details": detail.isEmpty ? [] : [detail],
|
|
256
|
+
]
|
|
257
|
+
|
|
258
|
+
// JSONSerialization needs NSNull for explicit null values.
|
|
259
|
+
if let exitCode = exitCode {
|
|
260
|
+
data["exitCode"] = exitCode
|
|
261
|
+
} else {
|
|
262
|
+
data["exitCode"] = NSNull()
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return try render("command-result", data: data, options: options)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
Render an audit finding pattern.
|
|
270
|
+
|
|
271
|
+
- parameter result: Result type such as success, failed, warning, or skipped.
|
|
272
|
+
- parameter finding: Finding summary.
|
|
273
|
+
- parameter location: Optional source location.
|
|
274
|
+
- parameter recommendation: Optional recommendation text.
|
|
275
|
+
- parameter evidence: Optional evidence line.
|
|
276
|
+
- parameter reference: Optional reference line.
|
|
277
|
+
- parameter options: Render options.
|
|
278
|
+
- returns: Rendered audit finding block.
|
|
279
|
+
- throws: `CliStyleError` on failure.
|
|
280
|
+
*/
|
|
281
|
+
static func auditFinding(
|
|
282
|
+
result: String,
|
|
283
|
+
finding: String,
|
|
284
|
+
location: String = "",
|
|
285
|
+
recommendation: String = "",
|
|
286
|
+
evidence: String = "",
|
|
287
|
+
reference: String = "",
|
|
288
|
+
options: CliStyleOptions = .init()
|
|
289
|
+
) throws -> String {
|
|
290
|
+
try render("audit-finding", data: [
|
|
291
|
+
"result": result,
|
|
292
|
+
"finding": finding,
|
|
293
|
+
"location": location,
|
|
294
|
+
"recommendation": recommendation,
|
|
295
|
+
"evidence": evidence.isEmpty ? [] : [evidence],
|
|
296
|
+
"references": reference.isEmpty ? [] : [reference],
|
|
297
|
+
], options: options)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
Render a task summary pattern.
|
|
302
|
+
|
|
303
|
+
- parameter result: Result type such as success, failed, warning, or skipped.
|
|
304
|
+
- parameter task: Task label.
|
|
305
|
+
- parameter summary: Optional summary detail.
|
|
306
|
+
- parameter completed: Optional completed item.
|
|
307
|
+
- parameter remaining: Optional remaining item.
|
|
308
|
+
- parameter options: Render options.
|
|
309
|
+
- returns: Rendered task summary block.
|
|
310
|
+
- throws: `CliStyleError` on failure.
|
|
311
|
+
*/
|
|
312
|
+
static func taskSummary(
|
|
313
|
+
result: String,
|
|
314
|
+
task: String,
|
|
315
|
+
summary: String = "",
|
|
316
|
+
completed: String = "",
|
|
317
|
+
remaining: String = "",
|
|
318
|
+
options: CliStyleOptions = .init()
|
|
319
|
+
) throws -> String {
|
|
320
|
+
try render("task-summary", data: [
|
|
321
|
+
"result": result,
|
|
322
|
+
"task": task,
|
|
323
|
+
"summary": summary,
|
|
324
|
+
"completed": completed.isEmpty ? [] : [completed],
|
|
325
|
+
"remaining": remaining.isEmpty ? [] : [remaining],
|
|
326
|
+
], options: options)
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
Render a confirmation result pattern.
|
|
331
|
+
|
|
332
|
+
- parameter state: Confirmation state such as confirmed, cancelled, or skipped.
|
|
333
|
+
- parameter action: Action label.
|
|
334
|
+
- parameter item: Optional item label.
|
|
335
|
+
- parameter detail: Optional detail text.
|
|
336
|
+
- parameter options: Render options.
|
|
337
|
+
- returns: Rendered confirmation result block.
|
|
338
|
+
- throws: `CliStyleError` on failure.
|
|
339
|
+
*/
|
|
340
|
+
static func confirmationResult(
|
|
341
|
+
state: String,
|
|
342
|
+
action: String,
|
|
343
|
+
item: String = "",
|
|
344
|
+
detail: String = "",
|
|
345
|
+
options: CliStyleOptions = .init()
|
|
346
|
+
) throws -> String {
|
|
347
|
+
try render("confirmation-result", data: [
|
|
348
|
+
"state": state,
|
|
349
|
+
"action": action,
|
|
350
|
+
"item": item,
|
|
351
|
+
"detail": detail,
|
|
352
|
+
], options: options)
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
Render a next-step block pattern.
|
|
357
|
+
|
|
358
|
+
- parameter nextStep: Next step text.
|
|
359
|
+
- parameter reason: Optional reason text.
|
|
360
|
+
- parameter command: Optional command line.
|
|
361
|
+
- parameter alternative: Optional alternative line.
|
|
362
|
+
- parameter options: Render options.
|
|
363
|
+
- returns: Rendered next-step block.
|
|
364
|
+
- throws: `CliStyleError` on failure.
|
|
365
|
+
*/
|
|
366
|
+
static func nextStepBlock(
|
|
367
|
+
nextStep: String,
|
|
368
|
+
reason: String = "",
|
|
369
|
+
command: String = "",
|
|
370
|
+
alternative: String = "",
|
|
371
|
+
options: CliStyleOptions = .init()
|
|
372
|
+
) throws -> String {
|
|
373
|
+
try render("next-step-block", data: [
|
|
374
|
+
"next": nextStep,
|
|
375
|
+
"reason": reason,
|
|
376
|
+
"commands": command.isEmpty ? [] : [command],
|
|
377
|
+
"alternatives": alternative.isEmpty ? [] : [alternative],
|
|
378
|
+
], options: options)
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
Resolve a command name or executable path for subprocess use.
|
|
383
|
+
|
|
384
|
+
- parameter binary: Binary path or command name.
|
|
385
|
+
- returns: Resolved executable path.
|
|
386
|
+
- throws: `CliStyleError.notFound` if the binary cannot be found.
|
|
387
|
+
*/
|
|
388
|
+
static func resolveBinary(_ binary: String) throws -> String {
|
|
389
|
+
if binary.contains("/") {
|
|
390
|
+
if FileManager.default.isExecutableFile(atPath: binary) {
|
|
391
|
+
return binary
|
|
392
|
+
}
|
|
393
|
+
throw CliStyleError.notFound("cli-style binary not found: \(binary)")
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
let path = ProcessInfo.processInfo.environment["PATH"] ?? ""
|
|
397
|
+
|
|
398
|
+
for directory in path.split(separator: ":") {
|
|
399
|
+
let candidate = "\(directory)/\(binary)"
|
|
400
|
+
if FileManager.default.isExecutableFile(atPath: candidate) {
|
|
401
|
+
return candidate
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
throw CliStyleError.notFound("cli-style binary not found: \(binary)")
|
|
406
|
+
}
|
|
407
|
+
}
|
package/package.json
CHANGED
|
@@ -12,6 +12,10 @@ const adapterPaths = {
|
|
|
12
12
|
releasePath: "adapters/python",
|
|
13
13
|
sourceUrl: new URL("../../adapters/python", import.meta.url),
|
|
14
14
|
},
|
|
15
|
+
swift: {
|
|
16
|
+
releasePath: "adapters/swift/CliStyle.swift",
|
|
17
|
+
sourceUrl: new URL("../../adapters/swift/CliStyle.swift", import.meta.url),
|
|
18
|
+
},
|
|
15
19
|
};
|
|
16
20
|
|
|
17
21
|
// Adapter names accepted by the adapter-path command.
|