@lewishowles/cli-style 0.13.0 → 0.15.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 +72 -10
- package/adapters/python/LICENSE +21 -0
- package/adapters/python/README.md +29 -0
- package/adapters/python/cli_style/__init__.py +73 -0
- package/adapters/python/cli_style/_wrappers.py +503 -0
- package/adapters/python/cli_style/core.py +136 -0
- package/adapters/python/pyproject.toml +32 -0
- package/adapters/swift/Package.swift +16 -0
- package/adapters/swift/Sources/CliStyle/CliStyle.swift +223 -0
- package/adapters/swift/Sources/CliStyle/Wrappers.swift +582 -0
- package/package.json +4 -2
- package/scripts/build-binary.sh +17 -3
- package/scripts/generate-adapters.js +485 -0
- package/scripts/package-binary.sh +6 -1
- package/src/catalogue/renderer-catalogue.js +958 -52
- package/src/cli/adapter-path-command.js +3 -3
- package/src/cli/render-command.js +6 -0
- package/src/gallery/render-gallery.js +40 -0
- package/src/index.js +3 -0
- package/src/patterns/audit-finding.js +4 -4
- package/src/patterns/command-result.js +2 -2
- package/src/patterns/diagnostic-report.js +6 -3
- package/src/patterns/helpers.js +11 -4
- package/src/patterns/next-step-block.js +2 -4
- package/src/patterns/task-summary.js +4 -4
- package/src/primitives/bullet-list.js +150 -0
- package/src/theme/bullets.js +41 -0
- package/adapters/python/cli_style.py +0 -428
- package/adapters/swift/CliStyle.swift +0 -482
|
@@ -1,428 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# Render caller data through the shared cli-style binary.
|
|
3
|
-
|
|
4
|
-
from __future__ import annotations
|
|
5
|
-
|
|
6
|
-
import json
|
|
7
|
-
import os
|
|
8
|
-
import shutil
|
|
9
|
-
import subprocess
|
|
10
|
-
import sys
|
|
11
|
-
from pathlib import Path
|
|
12
|
-
from typing import Any, Sequence
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class CliStyleError(RuntimeError):
|
|
16
|
-
"""Base error for cli-style adapter failures."""
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class CliStyleNotFoundError(CliStyleError):
|
|
20
|
-
"""Raised when the cli-style binary cannot be found."""
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class CliStyleRenderError(CliStyleError):
|
|
24
|
-
"""Raised when cli-style rejects a render request."""
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
# Resolve the child environment while preserving explicit caller controls.
|
|
28
|
-
def resolve_subprocess_environment() -> dict[str, str]:
|
|
29
|
-
environment = os.environ.copy()
|
|
30
|
-
|
|
31
|
-
if (
|
|
32
|
-
sys.stdout.isatty()
|
|
33
|
-
and "FORCE_COLOR" not in environment
|
|
34
|
-
and "NO_COLOR" not in environment
|
|
35
|
-
):
|
|
36
|
-
environment["FORCE_COLOR"] = "1"
|
|
37
|
-
|
|
38
|
-
return environment
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
# Render caller data through `cli-style render`.
|
|
42
|
-
#
|
|
43
|
-
# @param {str} renderer
|
|
44
|
-
# Stable renderer name accepted by `cli-style render`.
|
|
45
|
-
# @param {dict} data
|
|
46
|
-
# JSON-serialisable renderer input.
|
|
47
|
-
def render(
|
|
48
|
-
renderer: str,
|
|
49
|
-
data: dict[str, Any],
|
|
50
|
-
*,
|
|
51
|
-
binary: str = "cli-style",
|
|
52
|
-
profile: str | None = None,
|
|
53
|
-
width: int | None = None,
|
|
54
|
-
plain: bool = False,
|
|
55
|
-
no_colour: bool = False,
|
|
56
|
-
no_unicode: bool = False,
|
|
57
|
-
extra_args: Sequence[str] | None = None,
|
|
58
|
-
) -> str:
|
|
59
|
-
if not isinstance(renderer, str) or renderer == "":
|
|
60
|
-
raise ValueError("renderer must be a non-empty string")
|
|
61
|
-
|
|
62
|
-
if not isinstance(data, dict):
|
|
63
|
-
raise TypeError("data must be a dict")
|
|
64
|
-
|
|
65
|
-
resolved_binary = resolve_binary(binary)
|
|
66
|
-
command = [resolved_binary, "render", renderer]
|
|
67
|
-
|
|
68
|
-
if profile is not None:
|
|
69
|
-
command.extend(["--profile", profile])
|
|
70
|
-
|
|
71
|
-
if width is not None:
|
|
72
|
-
command.extend(["--width", str(width)])
|
|
73
|
-
|
|
74
|
-
if plain:
|
|
75
|
-
command.append("--plain")
|
|
76
|
-
|
|
77
|
-
if no_colour:
|
|
78
|
-
command.append("--no-colour")
|
|
79
|
-
|
|
80
|
-
if no_unicode:
|
|
81
|
-
command.append("--no-unicode")
|
|
82
|
-
|
|
83
|
-
if extra_args is not None:
|
|
84
|
-
command.extend(extra_args)
|
|
85
|
-
|
|
86
|
-
result = subprocess.run(
|
|
87
|
-
command,
|
|
88
|
-
capture_output=True,
|
|
89
|
-
check=False,
|
|
90
|
-
env=resolve_subprocess_environment(),
|
|
91
|
-
input=json.dumps(data),
|
|
92
|
-
text=True,
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
if result.returncode != 0:
|
|
96
|
-
message = result.stderr.strip() or f"cli-style render failed with exit code {result.returncode}"
|
|
97
|
-
raise CliStyleRenderError(message)
|
|
98
|
-
|
|
99
|
-
return result.stdout.rstrip("\n")
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
# Render a result status line from Python values.
|
|
103
|
-
#
|
|
104
|
-
# @param {str} type
|
|
105
|
-
# Result type such as success, failed, warning, or skipped.
|
|
106
|
-
# @param {str} label
|
|
107
|
-
# Status label.
|
|
108
|
-
# @param {str} detail
|
|
109
|
-
# Optional detail text.
|
|
110
|
-
def status(
|
|
111
|
-
type: str,
|
|
112
|
-
label: str = "",
|
|
113
|
-
detail: str = "",
|
|
114
|
-
**kwargs: Any,
|
|
115
|
-
) -> str:
|
|
116
|
-
return render("status", {"type": type, "label": label, "detail": detail}, **kwargs)
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
# Render a labelled value row from Python values.
|
|
120
|
-
#
|
|
121
|
-
# @param {str} label
|
|
122
|
-
# Row label.
|
|
123
|
-
# @param {str} value
|
|
124
|
-
# Row value.
|
|
125
|
-
# @param {str} result
|
|
126
|
-
# Optional result type for a symbol and tone.
|
|
127
|
-
# @param {int|None} label_width
|
|
128
|
-
# Optional minimum label width.
|
|
129
|
-
# @param {str|None} label_colour
|
|
130
|
-
# Optional label colour token.
|
|
131
|
-
# @param {str|None} value_colour
|
|
132
|
-
# Optional value colour token.
|
|
133
|
-
# @param {str|None} separator
|
|
134
|
-
# Optional text between the label and value.
|
|
135
|
-
def row(
|
|
136
|
-
label: str,
|
|
137
|
-
value: str,
|
|
138
|
-
result: str = "",
|
|
139
|
-
label_width: int | None = None,
|
|
140
|
-
label_colour: str | None = None,
|
|
141
|
-
value_colour: str | None = None,
|
|
142
|
-
separator: str | None = None,
|
|
143
|
-
**kwargs: Any,
|
|
144
|
-
) -> str:
|
|
145
|
-
data = {
|
|
146
|
-
"label": label,
|
|
147
|
-
"value": value,
|
|
148
|
-
"result": result,
|
|
149
|
-
"labelWidth": label_width,
|
|
150
|
-
"separator": separator,
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if label_colour is not None:
|
|
154
|
-
data["labelColour"] = label_colour
|
|
155
|
-
|
|
156
|
-
if value_colour is not None:
|
|
157
|
-
data["valueColour"] = value_colour
|
|
158
|
-
|
|
159
|
-
return render("row", data, **kwargs)
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
# Render an aligned table from Python values.
|
|
163
|
-
#
|
|
164
|
-
# @param {list[dict[str, Any]]} columns
|
|
165
|
-
# Table column definitions, including per-column overflow settings.
|
|
166
|
-
# @param {list[dict[str, Any]]} rows
|
|
167
|
-
# Table row values.
|
|
168
|
-
# @param {bool|None} unicode
|
|
169
|
-
# Optional Unicode border preference.
|
|
170
|
-
# @param {int|None} width
|
|
171
|
-
# Optional table width.
|
|
172
|
-
def table(
|
|
173
|
-
columns: list[dict[str, Any]],
|
|
174
|
-
rows: list[dict[str, Any]],
|
|
175
|
-
*,
|
|
176
|
-
unicode: bool | None = None,
|
|
177
|
-
width: int | None = None,
|
|
178
|
-
**kwargs: Any,
|
|
179
|
-
) -> str:
|
|
180
|
-
return render("table", {
|
|
181
|
-
"columns": columns,
|
|
182
|
-
"rows": rows,
|
|
183
|
-
"unicode": unicode,
|
|
184
|
-
"width": width,
|
|
185
|
-
}, **kwargs)
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
# Render a group of labelled value rows from Python values.
|
|
189
|
-
#
|
|
190
|
-
# @param {list[dict[str, Any]]} rows
|
|
191
|
-
# Labelled values to render together.
|
|
192
|
-
# @param {int|None} label_width
|
|
193
|
-
# Optional minimum label width.
|
|
194
|
-
def row_group(
|
|
195
|
-
rows: list[dict[str, Any]],
|
|
196
|
-
*,
|
|
197
|
-
label_width: int | None = None,
|
|
198
|
-
**kwargs: Any,
|
|
199
|
-
) -> str:
|
|
200
|
-
return render("row-group", {
|
|
201
|
-
"rows": rows,
|
|
202
|
-
"labelWidth": label_width,
|
|
203
|
-
}, **kwargs)
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
# Render an inline span from Python values.
|
|
207
|
-
#
|
|
208
|
-
# @param {str} value
|
|
209
|
-
# Span text.
|
|
210
|
-
# @param {str} tone
|
|
211
|
-
# Optional colour tone, defaulting to info.
|
|
212
|
-
# @param {str|None} weight
|
|
213
|
-
# Optional ANSI text weight.
|
|
214
|
-
def span(
|
|
215
|
-
value: str,
|
|
216
|
-
tone: str = "info",
|
|
217
|
-
weight: str | None = None,
|
|
218
|
-
**kwargs: Any,
|
|
219
|
-
) -> str:
|
|
220
|
-
return render("span", {"value": value, "tone": tone, "weight": weight}, **kwargs)
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
# Render a divider from a Python value.
|
|
224
|
-
#
|
|
225
|
-
# @param {str} label
|
|
226
|
-
# Optional divider label.
|
|
227
|
-
# @param {int|None} divider_width
|
|
228
|
-
# Optional divider width.
|
|
229
|
-
# @param {str|None} divider_colour
|
|
230
|
-
# Optional divider colour token.
|
|
231
|
-
# @param {str|None} label_colour
|
|
232
|
-
# Optional label colour token.
|
|
233
|
-
def divider(
|
|
234
|
-
label: str = "",
|
|
235
|
-
divider_width: int | None = None,
|
|
236
|
-
divider_colour: str | None = None,
|
|
237
|
-
label_colour: str | None = None,
|
|
238
|
-
**kwargs: Any,
|
|
239
|
-
) -> str:
|
|
240
|
-
data = {
|
|
241
|
-
"label": label,
|
|
242
|
-
"dividerWidth": divider_width,
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
if divider_colour is not None:
|
|
246
|
-
data["dividerColour"] = divider_colour
|
|
247
|
-
|
|
248
|
-
if label_colour is not None:
|
|
249
|
-
data["labelColour"] = label_colour
|
|
250
|
-
|
|
251
|
-
return render("divider", data, **kwargs)
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
# Render a hint from a Python value.
|
|
255
|
-
#
|
|
256
|
-
# @param {str} message
|
|
257
|
-
# Hint message.
|
|
258
|
-
def hint(
|
|
259
|
-
message: str,
|
|
260
|
-
**kwargs: Any,
|
|
261
|
-
) -> str:
|
|
262
|
-
return render("hint", {"message": message}, **kwargs)
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
# Render a command result pattern from Python values.
|
|
266
|
-
#
|
|
267
|
-
# @param {str} result
|
|
268
|
-
# Result type such as success, failed, warning, or skipped.
|
|
269
|
-
# @param {str} summary
|
|
270
|
-
# Summary line.
|
|
271
|
-
# @param {str} command
|
|
272
|
-
# Optional command text.
|
|
273
|
-
# @param {int|None} exit_code
|
|
274
|
-
# Optional integer exit code.
|
|
275
|
-
# @param {str} duration
|
|
276
|
-
# Optional duration text.
|
|
277
|
-
# @param {str} detail
|
|
278
|
-
# Optional detail line.
|
|
279
|
-
def command_result(
|
|
280
|
-
result: str,
|
|
281
|
-
summary: str,
|
|
282
|
-
command: str = "",
|
|
283
|
-
exit_code: int | None = None,
|
|
284
|
-
duration: str = "",
|
|
285
|
-
detail: str = "",
|
|
286
|
-
**kwargs: Any,
|
|
287
|
-
) -> str:
|
|
288
|
-
return render("command-result", {
|
|
289
|
-
"result": result,
|
|
290
|
-
"summary": summary,
|
|
291
|
-
"command": command,
|
|
292
|
-
"exitCode": exit_code,
|
|
293
|
-
"duration": duration,
|
|
294
|
-
"details": [detail] if detail else [],
|
|
295
|
-
}, **kwargs)
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
# Render an audit finding pattern from Python values.
|
|
299
|
-
#
|
|
300
|
-
# @param {str} result
|
|
301
|
-
# Result type such as success, failed, warning, or skipped.
|
|
302
|
-
# @param {str} finding
|
|
303
|
-
# Finding summary.
|
|
304
|
-
# @param {str} location
|
|
305
|
-
# Optional source location.
|
|
306
|
-
# @param {str} recommendation
|
|
307
|
-
# Optional recommendation text.
|
|
308
|
-
# @param {str} evidence
|
|
309
|
-
# Optional evidence line.
|
|
310
|
-
# @param {str} reference
|
|
311
|
-
# Optional reference line.
|
|
312
|
-
def audit_finding(
|
|
313
|
-
result: str,
|
|
314
|
-
finding: str,
|
|
315
|
-
location: str = "",
|
|
316
|
-
recommendation: str = "",
|
|
317
|
-
evidence: str = "",
|
|
318
|
-
reference: str = "",
|
|
319
|
-
**kwargs: Any,
|
|
320
|
-
) -> str:
|
|
321
|
-
return render("audit-finding", {
|
|
322
|
-
"result": result,
|
|
323
|
-
"finding": finding,
|
|
324
|
-
"location": location,
|
|
325
|
-
"recommendation": recommendation,
|
|
326
|
-
"evidence": [evidence] if evidence else [],
|
|
327
|
-
"references": [reference] if reference else [],
|
|
328
|
-
}, **kwargs)
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
# Render a task summary pattern from Python values.
|
|
332
|
-
#
|
|
333
|
-
# @param {str} result
|
|
334
|
-
# Result type such as success, failed, warning, or skipped.
|
|
335
|
-
# @param {str} task
|
|
336
|
-
# Task label.
|
|
337
|
-
# @param {str} summary
|
|
338
|
-
# Optional summary detail.
|
|
339
|
-
# @param {str} completed
|
|
340
|
-
# Optional completed item.
|
|
341
|
-
# @param {str} remaining
|
|
342
|
-
# Optional remaining item.
|
|
343
|
-
def task_summary(
|
|
344
|
-
result: str,
|
|
345
|
-
task: str,
|
|
346
|
-
summary: str = "",
|
|
347
|
-
completed: str = "",
|
|
348
|
-
remaining: str = "",
|
|
349
|
-
**kwargs: Any,
|
|
350
|
-
) -> str:
|
|
351
|
-
return render("task-summary", {
|
|
352
|
-
"result": result,
|
|
353
|
-
"task": task,
|
|
354
|
-
"summary": summary,
|
|
355
|
-
"completed": [completed] if completed else [],
|
|
356
|
-
"remaining": [remaining] if remaining else [],
|
|
357
|
-
}, **kwargs)
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
# Render a confirmation result pattern from Python values.
|
|
361
|
-
#
|
|
362
|
-
# @param {str} state
|
|
363
|
-
# Confirmation state such as confirmed, cancelled, or skipped.
|
|
364
|
-
# @param {str} action
|
|
365
|
-
# Action label.
|
|
366
|
-
# @param {str} item
|
|
367
|
-
# Optional item label.
|
|
368
|
-
# @param {str} detail
|
|
369
|
-
# Optional detail text.
|
|
370
|
-
def confirmation_result(
|
|
371
|
-
state: str,
|
|
372
|
-
action: str,
|
|
373
|
-
item: str = "",
|
|
374
|
-
detail: str = "",
|
|
375
|
-
**kwargs: Any,
|
|
376
|
-
) -> str:
|
|
377
|
-
return render("confirmation-result", {
|
|
378
|
-
"state": state,
|
|
379
|
-
"action": action,
|
|
380
|
-
"item": item,
|
|
381
|
-
"detail": detail,
|
|
382
|
-
}, **kwargs)
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
# Render a next-step block pattern from Python values.
|
|
386
|
-
#
|
|
387
|
-
# @param {str} next_step
|
|
388
|
-
# Next step text.
|
|
389
|
-
# @param {str} reason
|
|
390
|
-
# Optional reason text.
|
|
391
|
-
# @param {str} command
|
|
392
|
-
# Optional command line.
|
|
393
|
-
# @param {str} alternative
|
|
394
|
-
# Optional alternative line.
|
|
395
|
-
def next_step_block(
|
|
396
|
-
next_step: str,
|
|
397
|
-
reason: str = "",
|
|
398
|
-
command: str = "",
|
|
399
|
-
alternative: str = "",
|
|
400
|
-
**kwargs: Any,
|
|
401
|
-
) -> str:
|
|
402
|
-
return render("next-step-block", {
|
|
403
|
-
"next": next_step,
|
|
404
|
-
"reason": reason,
|
|
405
|
-
"commands": [command] if command else [],
|
|
406
|
-
"alternatives": [alternative] if alternative else [],
|
|
407
|
-
}, **kwargs)
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
# Resolve a command name or executable path for subprocess use.
|
|
411
|
-
#
|
|
412
|
-
# @param {str} binary
|
|
413
|
-
# Binary path or command name.
|
|
414
|
-
def resolve_binary(binary: str) -> str:
|
|
415
|
-
if "/" in binary:
|
|
416
|
-
path = Path(binary)
|
|
417
|
-
|
|
418
|
-
if path.is_file() and os.access(path, os.X_OK):
|
|
419
|
-
return str(path)
|
|
420
|
-
|
|
421
|
-
raise CliStyleNotFoundError(f"cli-style binary not found: {binary}")
|
|
422
|
-
|
|
423
|
-
resolved = shutil.which(binary)
|
|
424
|
-
|
|
425
|
-
if resolved is None:
|
|
426
|
-
raise CliStyleNotFoundError(f"cli-style binary not found: {binary}")
|
|
427
|
-
|
|
428
|
-
return resolved
|