@lewishowles/cli-style 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -81,12 +81,7 @@ const statusExamples = [
81
81
  ];
82
82
 
83
83
  // Panel examples should expose every configured semantic accent.
84
- const panelToneExamples = [
85
- "info",
86
- "success",
87
- "warning",
88
- "danger",
89
- ];
84
+ const panelToneExamples = ["info", "success", "warning", "danger"];
90
85
 
91
86
  // Progress examples expose common completion states and semantic tones.
92
87
  const progressExamples = [
@@ -113,18 +108,10 @@ const progressExamples = [
113
108
  ];
114
109
 
115
110
  // Gallery variants available through the public renderer and CLI.
116
- export const galleryVariants = [
117
- "current",
118
- "no-colour",
119
- "no-unicode",
120
- "plain",
121
- ];
111
+ export const galleryVariants = ["current", "no-colour", "no-unicode", "plain"];
122
112
 
123
113
  // Gallery sections available for focused output.
124
- export const gallerySections = [
125
- "primitives",
126
- "patterns",
127
- ];
114
+ export const gallerySections = ["primitives", "patterns"];
128
115
 
129
116
  // Pattern fixtures available for focused output and interactive search.
130
117
  export const galleryFixtures = [
@@ -151,23 +138,23 @@ export const galleryFixtures = [
151
138
  * Gallery output.
152
139
  */
153
140
  export function renderGallery(options = {}, request = {}) {
154
- const galleryOptions = request.width === undefined
155
- ? options
156
- : {
157
- ...galleryWidthOptions(request.width),
158
- ...options,
159
- width: request.width,
160
- };
161
- const variants = request.variants === true || request.matrix === true
162
- ? galleryVariants
163
- : [request.variant ?? "current"];
141
+ const galleryOptions =
142
+ request.width === undefined
143
+ ? options
144
+ : {
145
+ ...galleryWidthOptions(request.width),
146
+ ...options,
147
+ width: request.width,
148
+ };
149
+
150
+ const variants =
151
+ request.variants === true || request.matrix === true
152
+ ? galleryVariants
153
+ : [request.variant ?? "current"];
164
154
 
165
155
  const output = [
166
156
  "CLI style gallery",
167
- ...variants.flatMap((variant) => [
168
- "",
169
- renderVariant(variant, galleryOptions, request),
170
- ]),
157
+ ...variants.flatMap((variant) => ["", renderVariant(variant, galleryOptions, request)]),
171
158
  ].join("\n");
172
159
 
173
160
  return request.width === undefined ? output : clampGalleryOutput(output, request.width);
@@ -268,7 +255,7 @@ function resolveVariant(variant, options) {
268
255
  * Sections with blank-line separators.
269
256
  */
270
257
  function joinSections(sections) {
271
- return sections.flatMap((section, index) => index === 0 ? [section] : ["", section]);
258
+ return sections.flatMap((section, index) => (index === 0 ? [section] : ["", section]));
272
259
  }
273
260
 
274
261
  /**
@@ -339,20 +326,19 @@ function clampLine(line, width) {
339
326
  * Pattern gallery output.
340
327
  */
341
328
  function renderPatterns(options, fixture) {
342
- const audit = auditFinding({
343
- evidence: [
344
- "Text contrast measures 3.2:1",
345
- "Body text requires at least 4.5:1",
346
- ],
347
- finding: "Muted text has insufficient contrast",
348
- location: "src/components/StatusCard.vue:42",
349
- recommendation: "Use the standard foreground token.",
350
- references: [
351
- "WCAG 2.2 SC 1.4.3",
352
- ],
353
- result: resultTypes.FAILED,
354
- title: "Accessibility audit",
355
- }, options);
329
+ const audit = auditFinding(
330
+ {
331
+ evidence: ["Text contrast measures 3.2:1", "Body text requires at least 4.5:1"],
332
+ finding: "Muted text has insufficient contrast",
333
+ location: "src/components/StatusCard.vue:42",
334
+ recommendation: "Use the standard foreground token.",
335
+ references: ["WCAG 2.2 SC 1.4.3"],
336
+ result: resultTypes.FAILED,
337
+ title: "Accessibility audit",
338
+ },
339
+ options,
340
+ );
341
+
356
342
  const compactTableData = {
357
343
  columns: [
358
344
  {
@@ -383,167 +369,188 @@ function renderPatterns(options, fixture) {
383
369
  summary: "2 packages checked",
384
370
  title: "Package updates",
385
371
  };
372
+
386
373
  const compactTable = compactDataTable(compactTableData, options);
374
+
387
375
  const narrowCompactTable = compactDataTable(compactTableData, {
388
376
  ...options,
389
377
  width: 20,
390
378
  });
391
- const confirmation = confirmationResult({
392
- action: "Delete project",
393
- detail: "Project and 12 files removed.",
394
- item: "Website refresh",
395
- state: "confirmed",
396
- }, options);
397
- const command = commandResult({
398
- command: "bun run test:unit",
399
- details: [
400
- "121 tests passed",
401
- "Log saved to .agent/diagnostics/test-unit.log",
402
- ],
403
- duration: "158 ms",
404
- exitCode: 0,
405
- result: resultTypes.SUCCESS,
406
- summary: "Unit tests passed",
407
- title: "Unit test command",
408
- }, options);
409
- const report = diagnosticReport({
410
- checks: [
379
+
380
+ const confirmation = confirmationResult(
381
+ {
382
+ action: "Delete project",
383
+ detail: "Project and 12 files removed.",
384
+ item: "Website refresh",
385
+ state: "confirmed",
386
+ },
387
+ options,
388
+ );
389
+
390
+ const command = commandResult(
391
+ {
392
+ command: "bun run test:unit",
393
+ details: ["121 tests passed", "Log saved to .agent/diagnostics/test-unit.log"],
394
+ duration: "158 ms",
395
+ exitCode: 0,
396
+ result: resultTypes.SUCCESS,
397
+ summary: "Unit tests passed",
398
+ title: "Unit test command",
399
+ },
400
+ options,
401
+ );
402
+
403
+ const report = diagnosticReport(
404
+ {
405
+ checks: [
406
+ {
407
+ detail: "184 tests",
408
+ name: "unit",
409
+ result: resultTypes.SUCCESS,
410
+ },
411
+ {
412
+ detail: "2 failures",
413
+ name: "type-check",
414
+ result: resultTypes.FAILED,
415
+ },
416
+ ],
417
+ findings: [
418
+ {
419
+ message: "Coverage below target",
420
+ result: resultTypes.WARNING,
421
+ },
422
+ ],
423
+ nextActions: ["Review failing checks", "Re-run diagnostics"],
424
+ skippedChecks: [
425
+ {
426
+ name: "e2e",
427
+ reason: "Browser unavailable",
428
+ },
429
+ ],
430
+ title: "Project diagnostics",
431
+ },
432
+ options,
433
+ );
434
+
435
+ const reporter = createReporter(options);
436
+
437
+ reporter.divider("Project setup", "Claude + Codex");
438
+ reporter.group(
439
+ "Project files",
440
+ [
411
441
  {
412
- detail: "184 tests",
413
- name: "unit",
414
- result: resultTypes.SUCCESS,
442
+ detail: "already exists",
443
+ label: "AGENTS.md",
444
+ result: resultTypes.UNCHANGED,
415
445
  },
416
446
  {
417
- detail: "2 failures",
418
- name: "type-check",
419
- result: resultTypes.FAILED,
447
+ detail: "already exists",
448
+ label: "WORKSPACE.md",
449
+ result: resultTypes.UNCHANGED,
420
450
  },
421
451
  ],
422
- findings: [
452
+ {
453
+ summary: "2 already present",
454
+ },
455
+ );
456
+ reporter.group(
457
+ "Agent scripts",
458
+ [
423
459
  {
424
- message: "Coverage below target",
425
- result: resultTypes.WARNING,
460
+ detail: "already linked",
461
+ label: "project-diagnostics.py",
462
+ result: resultTypes.UNCHANGED,
463
+ },
464
+ {
465
+ detail: "already linked",
466
+ label: "generated-file-guard.py",
467
+ result: resultTypes.UNCHANGED,
426
468
  },
427
- ],
428
- nextActions: [
429
- "Review failing checks",
430
- "Re-run diagnostics",
431
- ],
432
- skippedChecks: [
433
469
  {
434
- name: "e2e",
435
- reason: "Browser unavailable",
470
+ detail: "already linked",
471
+ label: "repo-context.py",
472
+ result: resultTypes.UNCHANGED,
473
+ },
474
+ {
475
+ detail: "already linked",
476
+ label: "change-impact.py",
477
+ result: resultTypes.UNCHANGED,
436
478
  },
437
479
  ],
438
- title: "Project diagnostics",
439
- }, options);
440
- const reporter = createReporter(options);
441
-
442
- reporter.divider("Project setup", "Claude + Codex");
443
- reporter.group("Project files", [
444
480
  {
445
- detail: "already exists",
446
- label: "AGENTS.md",
447
- result: resultTypes.UNCHANGED,
448
- },
449
- {
450
- detail: "already exists",
451
- label: "WORKSPACE.md",
452
- result: resultTypes.UNCHANGED,
453
- },
454
- ], {
455
- summary: "2 already present",
456
- });
457
- reporter.group("Agent scripts", [
458
- {
459
- detail: "already linked",
460
- label: "project-diagnostics.py",
461
- result: resultTypes.UNCHANGED,
462
- },
463
- {
464
- detail: "already linked",
465
- label: "generated-file-guard.py",
466
- result: resultTypes.UNCHANGED,
481
+ summary: "4 already linked",
467
482
  },
483
+ );
484
+ reporter.group(
485
+ "Claude files",
486
+ [
487
+ {
488
+ detail: "already exists",
489
+ label: ".claude/",
490
+ result: resultTypes.UNCHANGED,
491
+ },
492
+ {
493
+ detail: "already up to date",
494
+ label: ".claude/.claudeignore",
495
+ result: resultTypes.UNCHANGED,
496
+ },
497
+ ],
468
498
  {
469
- detail: "already linked",
470
- label: "repo-context.py",
471
- result: resultTypes.UNCHANGED,
499
+ summary: "2 already current",
472
500
  },
501
+ );
502
+ reporter.status(resultTypes.SUCCESS, "Done.");
503
+
504
+ const reporterOutput = reporter.render();
505
+
506
+ const nextStep = nextStepBlock(
473
507
  {
474
- detail: "already linked",
475
- label: "change-impact.py",
476
- result: resultTypes.UNCHANGED,
508
+ alternatives: ["Review the focused gallery fixture"],
509
+ commands: ["git status --short", "bun run test:unit"],
510
+ next: "Commit the completed pattern chunk",
511
+ reason: "Implementation and verification are complete.",
477
512
  },
478
- ], {
479
- summary: "4 already linked",
480
- });
481
- reporter.group("Claude files", [
513
+ options,
514
+ );
515
+
516
+ const summary = taskSummary(
482
517
  {
483
- detail: "already exists",
484
- label: ".claude/",
485
- result: resultTypes.UNCHANGED,
518
+ completed: ["Added command result renderer", "Added task summary renderer"],
519
+ remaining: ["Review gallery output"],
520
+ result: resultTypes.PARTIAL,
521
+ summary: "Implementation complete",
522
+ task: "Add command and task patterns",
523
+ title: "Pattern implementation",
486
524
  },
525
+ options,
526
+ );
527
+
528
+ const transcript = agentTranscript(
487
529
  {
488
- detail: "already up to date",
489
- label: ".claude/.claudeignore",
490
- result: resultTypes.UNCHANGED,
530
+ entries: [
531
+ {
532
+ content: "Run the focused unit tests.",
533
+ role: "user",
534
+ },
535
+ {
536
+ content: "Running project diagnostics.",
537
+ role: "agent",
538
+ },
539
+ {
540
+ content: ["128 tests passed", "0 tests failed"],
541
+ name: "project-diagnostics",
542
+ role: "tool",
543
+ },
544
+ {
545
+ content: "Tests pass. Nothing remains.",
546
+ role: "agent",
547
+ },
548
+ ],
549
+ title: "Focused verification",
491
550
  },
492
- ], {
493
- summary: "2 already current",
494
- });
495
- reporter.status(resultTypes.SUCCESS, "Done.");
551
+ options,
552
+ );
496
553
 
497
- const reporterOutput = reporter.render();
498
- const nextStep = nextStepBlock({
499
- alternatives: [
500
- "Review the focused gallery fixture",
501
- ],
502
- commands: [
503
- "git status --short",
504
- "bun run test:unit",
505
- ],
506
- next: "Commit the completed pattern chunk",
507
- reason: "Implementation and verification are complete.",
508
- }, options);
509
- const summary = taskSummary({
510
- completed: [
511
- "Added command result renderer",
512
- "Added task summary renderer",
513
- ],
514
- remaining: [
515
- "Review gallery output",
516
- ],
517
- result: resultTypes.PARTIAL,
518
- summary: "Implementation complete",
519
- task: "Add command and task patterns",
520
- title: "Pattern implementation",
521
- }, options);
522
- const transcript = agentTranscript({
523
- entries: [
524
- {
525
- content: "Run the focused unit tests.",
526
- role: "user",
527
- },
528
- {
529
- content: "Running project diagnostics.",
530
- role: "agent",
531
- },
532
- {
533
- content: [
534
- "128 tests passed",
535
- "0 tests failed",
536
- ],
537
- name: "project-diagnostics",
538
- role: "tool",
539
- },
540
- {
541
- content: "Tests pass. Nothing remains.",
542
- role: "agent",
543
- },
544
- ],
545
- title: "Focused verification",
546
- }, options);
547
554
  const fixtures = {
548
555
  "agent-transcript": ["Agent transcript", transcript],
549
556
  "audit-finding": ["Audit finding", audit],
@@ -553,7 +560,7 @@ function renderPatterns(options, fixture) {
553
560
  "confirmation-result": ["Confirmation result", confirmation],
554
561
  "diagnostic-report": ["Diagnostic report", report],
555
562
  "next-step-block": ["Next-step block", nextStep],
556
- "reporter": ["Reporter", reporterOutput],
563
+ reporter: ["Reporter", reporterOutput],
557
564
  "task-summary": ["Task summary", summary],
558
565
  };
559
566
 
@@ -610,10 +617,7 @@ function renderPatterns(options, fixture) {
610
617
  * Framed fixture output.
611
618
  */
612
619
  function renderPatternFixture(fixture, options) {
613
- return [
614
- fixture[0],
615
- frameExample(fixture[1], options),
616
- ].join("\n");
620
+ return [fixture[0], frameExample(fixture[1], options)].join("\n");
617
621
  }
618
622
 
619
623
  /**
@@ -631,7 +635,7 @@ function frameExample(output, options) {
631
635
 
632
636
  return output
633
637
  .split("\n")
634
- .map((line) => line === "" ? rail : `${rail} ${line}`)
638
+ .map((line) => (line === "" ? rail : `${rail} ${line}`))
635
639
  .join("\n");
636
640
  }
637
641
 
@@ -780,12 +784,14 @@ function renderBarChartExample(options) {
780
784
  * Progress examples.
781
785
  */
782
786
  function renderProgressExamples(options) {
783
- return progressExamples.map((example) => progressBar({
784
- ...options,
785
- max: 100,
786
- tone: example.tone,
787
- value: example.value,
788
- }));
787
+ return progressExamples.map((example) =>
788
+ progressBar({
789
+ ...options,
790
+ max: 100,
791
+ tone: example.tone,
792
+ value: example.value,
793
+ }),
794
+ );
789
795
  }
790
796
 
791
797
  /**
@@ -797,12 +803,14 @@ function renderProgressExamples(options) {
797
803
  * Panel examples.
798
804
  */
799
805
  function renderPanelExamples(options) {
800
- return panelToneExamples.map((tone) => panel({
801
- ...options,
802
- lines: [`tone: ${tone}`],
803
- title: tone,
804
- tone,
805
- }));
806
+ return panelToneExamples.map((tone) =>
807
+ panel({
808
+ ...options,
809
+ lines: [`tone: ${tone}`],
810
+ title: tone,
811
+ tone,
812
+ }),
813
+ );
806
814
  }
807
815
 
808
816
  /**
package/src/index.js CHANGED
@@ -10,11 +10,7 @@ export {
10
10
  parseAdapterPathRequest,
11
11
  renderAdapterPath,
12
12
  } from "./cli/adapter-path-command.js";
13
- export {
14
- parseRenderRequest,
15
- rendererNames,
16
- renderJsonInput,
17
- } from "./cli/render-command.js";
13
+ export { parseRenderRequest, rendererNames, renderJsonInput } from "./cli/render-command.js";
18
14
  export {
19
15
  galleryFixtures,
20
16
  gallerySections,
@@ -1,11 +1,6 @@
1
1
  import { profiles } from "../profiles/profiles.js";
2
2
  import { chip } from "../primitives/chip.js";
3
- import {
4
- isNonEmptyString,
5
- isRecord,
6
- normaliseStringList,
7
- renderTitle,
8
- } from "./helpers.js";
3
+ import { isNonEmptyString, isRecord, normaliseStringList, renderTitle } from "./helpers.js";
9
4
 
10
5
  // Transcript roles map machine-facing values to stable textual labels and tones.
11
6
  const transcriptRoles = {
@@ -66,10 +61,9 @@ export function agentTranscript(transcript, options = {}) {
66
61
  return renderTitle(title, options);
67
62
  }
68
63
 
69
- return [
70
- renderTitle(title, options),
71
- ...entries.map((entry) => renderEntry(entry, options)),
72
- ].join("\n\n");
64
+ return [renderTitle(title, options), ...entries.map((entry) => renderEntry(entry, options))].join(
65
+ "\n\n",
66
+ );
73
67
  }
74
68
 
75
69
  /**
@@ -134,10 +128,7 @@ function renderEntry(entry, options) {
134
128
  return `${renderedLabel} ${entry.content[0]}`;
135
129
  }
136
130
 
137
- return [
138
- renderedLabel,
139
- ...entry.content.map((line) => ` ${line}`),
140
- ].join("\n");
131
+ return [renderedLabel, ...entry.content.map((line) => ` ${line}`)].join("\n");
141
132
  }
142
133
 
143
134
  /**
@@ -154,12 +145,7 @@ function renderEntry(entry, options) {
154
145
  */
155
146
  function renderAgentEntry(label, content, role) {
156
147
  if (role === "tool") {
157
- return [
158
- `## ${label}`,
159
- "```text",
160
- ...content,
161
- "```",
162
- ].join("\n");
148
+ return [`## ${label}`, "```text", ...content, "```"].join("\n");
163
149
  }
164
150
 
165
151
  return [`## ${label}`, ...content].join("\n");
@@ -50,6 +50,7 @@ export function auditFinding(finding, options = {}) {
50
50
  const summary = isNonEmptyString(finding.finding) ? finding.finding : "";
51
51
  const evidence = normaliseStringList(finding.evidence).map((item) => `- ${item}`);
52
52
  const references = normaliseStringList(finding.references).map((item) => `- ${item}`);
53
+
53
54
  const sections = [
54
55
  status(finding.result ?? resultTypes.UNKNOWN, summary, options),
55
56
  renderLocation(finding.location, options),
@@ -50,6 +50,7 @@ export function commandResult(result, options = {}) {
50
50
  const metadata = renderMetadata(result, options);
51
51
  const details = normaliseStringList(result.details).map((detail) => `- ${detail}`);
52
52
  const summary = isNonEmptyString(result.summary) ? result.summary : "";
53
+
53
54
  const sections = [
54
55
  status(result.result ?? resultTypes.UNKNOWN, summary, options),
55
56
  metadata,
@@ -83,9 +84,11 @@ function renderMetadata(result, options) {
83
84
  }
84
85
 
85
86
  return entries
86
- .map(([label, value]) => row(label, value, {
87
- ...options,
88
- labelWidth: 9,
89
- }))
87
+ .map(([label, value]) =>
88
+ row(label, value, {
89
+ ...options,
90
+ labelWidth: 9,
91
+ }),
92
+ )
90
93
  .join("\n");
91
94
  }
@@ -1,10 +1,6 @@
1
1
  import { profiles } from "../profiles/profiles.js";
2
2
  import { table } from "../primitives/table.js";
3
- import {
4
- isNonEmptyString,
5
- isRecord,
6
- renderTitle,
7
- } from "./helpers.js";
3
+ import { isNonEmptyString, isRecord, renderTitle } from "./helpers.js";
8
4
 
9
5
  // Compact data tables use a stable title when the caller does not provide one.
10
6
  const defaultTitle = "Data";
@@ -38,14 +34,17 @@ export function compactDataTable(data, options = {}) {
38
34
 
39
35
  const title = isNonEmptyString(data.title) ? data.title : defaultTitle;
40
36
  const summary = isNonEmptyString(data.summary) ? data.summary : "";
37
+
41
38
  const renderedTable = table({
42
39
  ...options,
43
40
  columns: data.columns,
44
41
  rows: data.rows,
45
42
  });
46
- const heading = summary === ""
47
- ? renderTitle(title, options)
48
- : [renderTitle(title, options), summary].join("\n");
43
+
44
+ const heading =
45
+ summary === ""
46
+ ? renderTitle(title, options)
47
+ : [renderTitle(title, options), summary].join("\n");
49
48
 
50
49
  if (renderedTable === "") {
51
50
  return "";
@@ -1,13 +1,7 @@
1
1
  import { profiles } from "../profiles/profiles.js";
2
2
  import { status } from "../primitives/status.js";
3
3
  import { resultTypes } from "../theme/results.js";
4
- import {
5
- formatLabel,
6
- isNonEmptyString,
7
- isRecord,
8
- renderSection,
9
- renderTitle,
10
- } from "./helpers.js";
4
+ import { formatLabel, isNonEmptyString, isRecord, renderSection, renderTitle } from "./helpers.js";
11
5
 
12
6
  // Confirmation states map workflow outcomes onto shared result semantics.
13
7
  const confirmationStates = {
@@ -67,15 +61,15 @@ export function confirmationResult(confirmation, options = {}) {
67
61
  return "";
68
62
  }
69
63
 
70
- const title = isNonEmptyString(confirmation.title)
71
- ? confirmation.title
72
- : defaultTitle;
64
+ const title = isNonEmptyString(confirmation.title) ? confirmation.title : defaultTitle;
73
65
  const action = isNonEmptyString(confirmation.action) ? confirmation.action : "";
74
66
  const summary = formatLabel(action, confirmation.item, options);
75
67
  const state = confirmationStates[confirmation.state] ?? fallbackState;
68
+
76
69
  const detail = isNonEmptyString(confirmation.detail)
77
70
  ? renderSection("Detail", [confirmation.detail], options, false)
78
71
  : "";
72
+
79
73
  const sections = [
80
74
  status(state.result, summary, {
81
75
  ...options,