@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,482 +0,0 @@
|
|
|
1
|
-
import Foundation
|
|
2
|
-
#if canImport(Darwin)
|
|
3
|
-
import Darwin
|
|
4
|
-
#elseif canImport(Glibc)
|
|
5
|
-
import Glibc
|
|
6
|
-
#endif
|
|
7
|
-
|
|
8
|
-
// Error types for cli-style adapter failures.
|
|
9
|
-
enum CliStyleError: Error {
|
|
10
|
-
case notFound(String)
|
|
11
|
-
case renderFailed(String)
|
|
12
|
-
case invalidInput(String)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// Options for controlling cli-style render behaviour.
|
|
16
|
-
struct CliStyleOptions {
|
|
17
|
-
// Binary path or command name.
|
|
18
|
-
var binary: String = "cli-style"
|
|
19
|
-
|
|
20
|
-
// Profile name such as human, agent, or json.
|
|
21
|
-
var profile: String? = nil
|
|
22
|
-
|
|
23
|
-
// Terminal width in characters.
|
|
24
|
-
var width: Int? = nil
|
|
25
|
-
|
|
26
|
-
// Force plain text output.
|
|
27
|
-
var isPlain: Bool = false
|
|
28
|
-
|
|
29
|
-
// Disable colour output.
|
|
30
|
-
var isNoColour: Bool = false
|
|
31
|
-
|
|
32
|
-
// Disable Unicode symbols.
|
|
33
|
-
var isNoUnicode: Bool = false
|
|
34
|
-
|
|
35
|
-
// Additional arguments passed through to the binary.
|
|
36
|
-
var extraArgs: [String] = []
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Render caller data through the shared cli-style binary.
|
|
40
|
-
enum CliStyle {
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
Render a dictionary through `cli-style render`.
|
|
44
|
-
|
|
45
|
-
- parameter renderer: Stable renderer name accepted by `cli-style render`.
|
|
46
|
-
- parameter data: JSON-serialisable renderer input.
|
|
47
|
-
- parameter options: Render options including binary path and output flags.
|
|
48
|
-
- returns: Rendered string output from cli-style.
|
|
49
|
-
- throws: `CliStyleError` if the binary is missing, input is invalid, or rendering fails.
|
|
50
|
-
*/
|
|
51
|
-
static func render(
|
|
52
|
-
_ renderer: String,
|
|
53
|
-
data: [String: Any],
|
|
54
|
-
options: CliStyleOptions = .init()
|
|
55
|
-
) throws -> String {
|
|
56
|
-
if renderer.isEmpty {
|
|
57
|
-
throw CliStyleError.invalidInput("renderer must be a non-empty string")
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
let resolvedBinary = try resolveBinary(options.binary)
|
|
61
|
-
|
|
62
|
-
let jsonData: Data
|
|
63
|
-
|
|
64
|
-
do {
|
|
65
|
-
jsonData = try JSONSerialization.data(withJSONObject: data)
|
|
66
|
-
} catch {
|
|
67
|
-
throw CliStyleError.invalidInput("data is not JSON-serialisable")
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
var arguments = ["render", renderer]
|
|
71
|
-
|
|
72
|
-
if let profile = options.profile {
|
|
73
|
-
arguments += ["--profile", profile]
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if let width = options.width {
|
|
77
|
-
arguments += ["--width", String(width)]
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if options.isPlain {
|
|
81
|
-
arguments.append("--plain")
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if options.isNoColour {
|
|
85
|
-
arguments.append("--no-colour")
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if options.isNoUnicode {
|
|
89
|
-
arguments.append("--no-unicode")
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
arguments += options.extraArgs
|
|
93
|
-
|
|
94
|
-
let process = Process()
|
|
95
|
-
process.executableURL = URL(fileURLWithPath: resolvedBinary)
|
|
96
|
-
process.arguments = arguments
|
|
97
|
-
process.environment = resolveProcessEnvironment()
|
|
98
|
-
|
|
99
|
-
let stdinPipe = Pipe()
|
|
100
|
-
let stdoutPipe = Pipe()
|
|
101
|
-
let stderrPipe = Pipe()
|
|
102
|
-
process.standardInput = stdinPipe
|
|
103
|
-
process.standardOutput = stdoutPipe
|
|
104
|
-
process.standardError = stderrPipe
|
|
105
|
-
|
|
106
|
-
do {
|
|
107
|
-
try process.run()
|
|
108
|
-
} catch {
|
|
109
|
-
throw CliStyleError.renderFailed("failed to launch cli-style: \(error.localizedDescription)")
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
stdinPipe.fileHandleForWriting.write(jsonData)
|
|
113
|
-
stdinPipe.fileHandleForWriting.closeFile()
|
|
114
|
-
|
|
115
|
-
process.waitUntilExit()
|
|
116
|
-
|
|
117
|
-
let stdoutData = stdoutPipe.fileHandleForReading.readDataToEndOfFile()
|
|
118
|
-
let stderrData = stderrPipe.fileHandleForReading.readDataToEndOfFile()
|
|
119
|
-
|
|
120
|
-
if process.terminationStatus != 0 {
|
|
121
|
-
let stderrText = String(data: stderrData, encoding: .utf8)?
|
|
122
|
-
.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
|
123
|
-
let message = stderrText.isEmpty
|
|
124
|
-
? "cli-style render failed with exit code \(process.terminationStatus)"
|
|
125
|
-
: stderrText
|
|
126
|
-
|
|
127
|
-
throw CliStyleError.renderFailed(message)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
let stdoutText = String(data: stdoutData, encoding: .utf8) ?? ""
|
|
131
|
-
return stdoutText.trimmingCharacters(in: .newlines)
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Resolve the child environment for a captured render.
|
|
136
|
-
*/
|
|
137
|
-
private static func resolveProcessEnvironment() -> [String: String] {
|
|
138
|
-
var environment = ProcessInfo.processInfo.environment
|
|
139
|
-
|
|
140
|
-
if isatty(STDOUT_FILENO) == 1 &&
|
|
141
|
-
environment["FORCE_COLOR"] == nil &&
|
|
142
|
-
environment["NO_COLOR"] == nil {
|
|
143
|
-
environment["FORCE_COLOR"] = "1"
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return environment
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
Render a result status line.
|
|
151
|
-
|
|
152
|
-
- parameter type: Result type such as success, failed, warning, or skipped.
|
|
153
|
-
- parameter label: Status label.
|
|
154
|
-
- parameter detail: Optional detail text.
|
|
155
|
-
- parameter options: Render options.
|
|
156
|
-
- returns: Rendered status line.
|
|
157
|
-
- throws: `CliStyleError` on failure.
|
|
158
|
-
*/
|
|
159
|
-
static func status(
|
|
160
|
-
type: String,
|
|
161
|
-
label: String = "",
|
|
162
|
-
detail: String = "",
|
|
163
|
-
options: CliStyleOptions = .init()
|
|
164
|
-
) throws -> String {
|
|
165
|
-
try render("status", data: [
|
|
166
|
-
"type": type,
|
|
167
|
-
"label": label,
|
|
168
|
-
"detail": detail,
|
|
169
|
-
], options: options)
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
Render a labelled value row.
|
|
174
|
-
|
|
175
|
-
- parameter label: Row label.
|
|
176
|
-
- parameter value: Row value.
|
|
177
|
-
- parameter result: Optional result type for a symbol and tone.
|
|
178
|
-
- parameter labelWidth: Optional minimum label width.
|
|
179
|
-
- parameter labelColour: Optional label colour token.
|
|
180
|
-
- parameter valueColour: Optional value colour token.
|
|
181
|
-
- parameter separator: Optional text between the label and value.
|
|
182
|
-
- parameter options: Render options.
|
|
183
|
-
- returns: Rendered row line.
|
|
184
|
-
- throws: `CliStyleError` on failure.
|
|
185
|
-
*/
|
|
186
|
-
static func row(
|
|
187
|
-
label: String,
|
|
188
|
-
value: String,
|
|
189
|
-
result: String = "",
|
|
190
|
-
labelWidth: Int? = nil,
|
|
191
|
-
labelColour: String? = nil,
|
|
192
|
-
valueColour: String? = nil,
|
|
193
|
-
separator: String? = nil,
|
|
194
|
-
options: CliStyleOptions = .init()
|
|
195
|
-
) throws -> String {
|
|
196
|
-
var data: [String: Any] = [
|
|
197
|
-
"label": label,
|
|
198
|
-
"value": value,
|
|
199
|
-
"result": result,
|
|
200
|
-
]
|
|
201
|
-
|
|
202
|
-
if let labelWidth = labelWidth {
|
|
203
|
-
data["labelWidth"] = labelWidth
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
if let labelColour = labelColour {
|
|
207
|
-
data["labelColour"] = labelColour
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
if let valueColour = valueColour {
|
|
211
|
-
data["valueColour"] = valueColour
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
if let separator = separator {
|
|
215
|
-
data["separator"] = separator
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
return try render("row", data: data, options: options)
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
Render an inline span.
|
|
223
|
-
|
|
224
|
-
- parameter value: Span text.
|
|
225
|
-
- parameter tone: Optional colour tone, defaulting to info.
|
|
226
|
-
- parameter weight: Optional ANSI text weight.
|
|
227
|
-
- parameter options: Render options.
|
|
228
|
-
- returns: Rendered span string.
|
|
229
|
-
- throws: `CliStyleError` on failure.
|
|
230
|
-
*/
|
|
231
|
-
static func span(
|
|
232
|
-
value: String,
|
|
233
|
-
tone: String = "info",
|
|
234
|
-
weight: String? = nil,
|
|
235
|
-
options: CliStyleOptions = .init()
|
|
236
|
-
) throws -> String {
|
|
237
|
-
var data: [String: Any] = [
|
|
238
|
-
"value": value,
|
|
239
|
-
"tone": tone,
|
|
240
|
-
]
|
|
241
|
-
|
|
242
|
-
if let weight = weight {
|
|
243
|
-
data["weight"] = weight
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
return try render("span", data: data, options: options)
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
Render a divider.
|
|
251
|
-
|
|
252
|
-
- parameter label: Optional divider label.
|
|
253
|
-
- parameter dividerWidth: Optional divider width.
|
|
254
|
-
- parameter dividerColour: Optional divider colour token.
|
|
255
|
-
- parameter labelColour: Optional label colour token.
|
|
256
|
-
- parameter options: Render options.
|
|
257
|
-
- returns: Rendered divider line.
|
|
258
|
-
- throws: `CliStyleError` on failure.
|
|
259
|
-
*/
|
|
260
|
-
static func divider(
|
|
261
|
-
label: String = "",
|
|
262
|
-
dividerWidth: Int? = nil,
|
|
263
|
-
dividerColour: String? = nil,
|
|
264
|
-
labelColour: String? = nil,
|
|
265
|
-
options: CliStyleOptions = .init()
|
|
266
|
-
) throws -> String {
|
|
267
|
-
var data: [String: Any] = [
|
|
268
|
-
"label": label,
|
|
269
|
-
]
|
|
270
|
-
|
|
271
|
-
if let dividerWidth = dividerWidth {
|
|
272
|
-
data["dividerWidth"] = dividerWidth
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
if let dividerColour = dividerColour {
|
|
276
|
-
data["dividerColour"] = dividerColour
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
if let labelColour = labelColour {
|
|
280
|
-
data["labelColour"] = labelColour
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
return try render("divider", data: data, options: options)
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
Render a hint.
|
|
288
|
-
|
|
289
|
-
- parameter message: Hint message.
|
|
290
|
-
- parameter options: Render options.
|
|
291
|
-
- returns: Rendered hint line.
|
|
292
|
-
- throws: `CliStyleError` on failure.
|
|
293
|
-
*/
|
|
294
|
-
static func hint(
|
|
295
|
-
message: String,
|
|
296
|
-
options: CliStyleOptions = .init()
|
|
297
|
-
) throws -> String {
|
|
298
|
-
try render("hint", data: [
|
|
299
|
-
"message": message,
|
|
300
|
-
], options: options)
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
Render a command result pattern.
|
|
305
|
-
|
|
306
|
-
- parameter result: Result type such as success, failed, warning, or skipped.
|
|
307
|
-
- parameter summary: Summary line.
|
|
308
|
-
- parameter command: Optional command text.
|
|
309
|
-
- parameter exitCode: Optional integer exit code.
|
|
310
|
-
- parameter duration: Optional duration text.
|
|
311
|
-
- parameter detail: Optional detail line.
|
|
312
|
-
- parameter options: Render options.
|
|
313
|
-
- returns: Rendered command result block.
|
|
314
|
-
- throws: `CliStyleError` on failure.
|
|
315
|
-
*/
|
|
316
|
-
static func commandResult(
|
|
317
|
-
result: String,
|
|
318
|
-
summary: String,
|
|
319
|
-
command: String = "",
|
|
320
|
-
exitCode: Int? = nil,
|
|
321
|
-
duration: String = "",
|
|
322
|
-
detail: String = "",
|
|
323
|
-
options: CliStyleOptions = .init()
|
|
324
|
-
) throws -> String {
|
|
325
|
-
var data: [String: Any] = [
|
|
326
|
-
"result": result,
|
|
327
|
-
"summary": summary,
|
|
328
|
-
"command": command,
|
|
329
|
-
"duration": duration,
|
|
330
|
-
"details": detail.isEmpty ? [] : [detail],
|
|
331
|
-
]
|
|
332
|
-
|
|
333
|
-
// JSONSerialization needs NSNull for explicit null values.
|
|
334
|
-
if let exitCode = exitCode {
|
|
335
|
-
data["exitCode"] = exitCode
|
|
336
|
-
} else {
|
|
337
|
-
data["exitCode"] = NSNull()
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
return try render("command-result", data: data, options: options)
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
/**
|
|
344
|
-
Render an audit finding pattern.
|
|
345
|
-
|
|
346
|
-
- parameter result: Result type such as success, failed, warning, or skipped.
|
|
347
|
-
- parameter finding: Finding summary.
|
|
348
|
-
- parameter location: Optional source location.
|
|
349
|
-
- parameter recommendation: Optional recommendation text.
|
|
350
|
-
- parameter evidence: Optional evidence line.
|
|
351
|
-
- parameter reference: Optional reference line.
|
|
352
|
-
- parameter options: Render options.
|
|
353
|
-
- returns: Rendered audit finding block.
|
|
354
|
-
- throws: `CliStyleError` on failure.
|
|
355
|
-
*/
|
|
356
|
-
static func auditFinding(
|
|
357
|
-
result: String,
|
|
358
|
-
finding: String,
|
|
359
|
-
location: String = "",
|
|
360
|
-
recommendation: String = "",
|
|
361
|
-
evidence: String = "",
|
|
362
|
-
reference: String = "",
|
|
363
|
-
options: CliStyleOptions = .init()
|
|
364
|
-
) throws -> String {
|
|
365
|
-
try render("audit-finding", data: [
|
|
366
|
-
"result": result,
|
|
367
|
-
"finding": finding,
|
|
368
|
-
"location": location,
|
|
369
|
-
"recommendation": recommendation,
|
|
370
|
-
"evidence": evidence.isEmpty ? [] : [evidence],
|
|
371
|
-
"references": reference.isEmpty ? [] : [reference],
|
|
372
|
-
], options: options)
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
Render a task summary pattern.
|
|
377
|
-
|
|
378
|
-
- parameter result: Result type such as success, failed, warning, or skipped.
|
|
379
|
-
- parameter task: Task label.
|
|
380
|
-
- parameter summary: Optional summary detail.
|
|
381
|
-
- parameter completed: Optional completed item.
|
|
382
|
-
- parameter remaining: Optional remaining item.
|
|
383
|
-
- parameter options: Render options.
|
|
384
|
-
- returns: Rendered task summary block.
|
|
385
|
-
- throws: `CliStyleError` on failure.
|
|
386
|
-
*/
|
|
387
|
-
static func taskSummary(
|
|
388
|
-
result: String,
|
|
389
|
-
task: String,
|
|
390
|
-
summary: String = "",
|
|
391
|
-
completed: String = "",
|
|
392
|
-
remaining: String = "",
|
|
393
|
-
options: CliStyleOptions = .init()
|
|
394
|
-
) throws -> String {
|
|
395
|
-
try render("task-summary", data: [
|
|
396
|
-
"result": result,
|
|
397
|
-
"task": task,
|
|
398
|
-
"summary": summary,
|
|
399
|
-
"completed": completed.isEmpty ? [] : [completed],
|
|
400
|
-
"remaining": remaining.isEmpty ? [] : [remaining],
|
|
401
|
-
], options: options)
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
/**
|
|
405
|
-
Render a confirmation result pattern.
|
|
406
|
-
|
|
407
|
-
- parameter state: Confirmation state such as confirmed, cancelled, or skipped.
|
|
408
|
-
- parameter action: Action label.
|
|
409
|
-
- parameter item: Optional item label.
|
|
410
|
-
- parameter detail: Optional detail text.
|
|
411
|
-
- parameter options: Render options.
|
|
412
|
-
- returns: Rendered confirmation result block.
|
|
413
|
-
- throws: `CliStyleError` on failure.
|
|
414
|
-
*/
|
|
415
|
-
static func confirmationResult(
|
|
416
|
-
state: String,
|
|
417
|
-
action: String,
|
|
418
|
-
item: String = "",
|
|
419
|
-
detail: String = "",
|
|
420
|
-
options: CliStyleOptions = .init()
|
|
421
|
-
) throws -> String {
|
|
422
|
-
try render("confirmation-result", data: [
|
|
423
|
-
"state": state,
|
|
424
|
-
"action": action,
|
|
425
|
-
"item": item,
|
|
426
|
-
"detail": detail,
|
|
427
|
-
], options: options)
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
/**
|
|
431
|
-
Render a next-step block pattern.
|
|
432
|
-
|
|
433
|
-
- parameter nextStep: Next step text.
|
|
434
|
-
- parameter reason: Optional reason text.
|
|
435
|
-
- parameter command: Optional command line.
|
|
436
|
-
- parameter alternative: Optional alternative line.
|
|
437
|
-
- parameter options: Render options.
|
|
438
|
-
- returns: Rendered next-step block.
|
|
439
|
-
- throws: `CliStyleError` on failure.
|
|
440
|
-
*/
|
|
441
|
-
static func nextStepBlock(
|
|
442
|
-
nextStep: String,
|
|
443
|
-
reason: String = "",
|
|
444
|
-
command: String = "",
|
|
445
|
-
alternative: String = "",
|
|
446
|
-
options: CliStyleOptions = .init()
|
|
447
|
-
) throws -> String {
|
|
448
|
-
try render("next-step-block", data: [
|
|
449
|
-
"next": nextStep,
|
|
450
|
-
"reason": reason,
|
|
451
|
-
"commands": command.isEmpty ? [] : [command],
|
|
452
|
-
"alternatives": alternative.isEmpty ? [] : [alternative],
|
|
453
|
-
], options: options)
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
/**
|
|
457
|
-
Resolve a command name or executable path for subprocess use.
|
|
458
|
-
|
|
459
|
-
- parameter binary: Binary path or command name.
|
|
460
|
-
- returns: Resolved executable path.
|
|
461
|
-
- throws: `CliStyleError.notFound` if the binary cannot be found.
|
|
462
|
-
*/
|
|
463
|
-
static func resolveBinary(_ binary: String) throws -> String {
|
|
464
|
-
if binary.contains("/") {
|
|
465
|
-
if FileManager.default.isExecutableFile(atPath: binary) {
|
|
466
|
-
return binary
|
|
467
|
-
}
|
|
468
|
-
throw CliStyleError.notFound("cli-style binary not found: \(binary)")
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
let path = ProcessInfo.processInfo.environment["PATH"] ?? ""
|
|
472
|
-
|
|
473
|
-
for directory in path.split(separator: ":") {
|
|
474
|
-
let candidate = "\(directory)/\(binary)"
|
|
475
|
-
if FileManager.default.isExecutableFile(atPath: candidate) {
|
|
476
|
-
return candidate
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
throw CliStyleError.notFound("cli-style binary not found: \(binary)")
|
|
481
|
-
}
|
|
482
|
-
}
|