@greenarmor/ges 1.4.0 → 1.4.2

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.
@@ -5,6 +5,7 @@ import { getPacksForProjectType, getAllPacks } from "@greenarmor/ges-policy-engi
5
5
  import { generateScoreFile, formatScoreOutput } from "@greenarmor/ges-scoring-engine";
6
6
  import { runAudit, runAuditIncremental, deduplicateFindings } from "@greenarmor/ges-audit-engine";
7
7
  import { showNextStepsMenu } from "../utils/next-steps.js";
8
+ import { banner, divider, blank, success, warn, info, severityBadge, BOLD, DIM, CYAN, RED, YELLOW, GRAY } from "../utils/ui.js";
8
9
  import * as fs from "node:fs";
9
10
  import * as path from "node:path";
10
11
  export const auditCommand = new Command("audit")
@@ -15,9 +16,10 @@ export const auditCommand = new Command("audit")
15
16
  .action(async (options) => {
16
17
  const root = ensureGESInitialized();
17
18
  const config = readJsonFile(path.join(root, ".ges", "config.json"));
18
- console.log("\n GESF Compliance Audit");
19
- console.log(" ────────────────────\n");
20
- console.log(" Scanning project files...");
19
+ if (!options.json) {
20
+ banner("GESF Compliance Audit", options.incremental ? "Incremental scan" : "Full project scan");
21
+ info("Scanning project files...");
22
+ }
21
23
  let rawFindings;
22
24
  let scannedFiles;
23
25
  if (options.incremental) {
@@ -27,16 +29,19 @@ export const auditCommand = new Command("audit")
27
29
  writeJsonFile(cachePath, result.newCache);
28
30
  rawFindings = result.findings;
29
31
  scannedFiles = result.scannedFiles;
30
- console.log(` Scanned ${scannedFiles} files (${result.changedFiles} changed)`);
32
+ if (!options.json)
33
+ success("Scan complete", `${scannedFiles} files (${result.changedFiles} changed)`);
31
34
  }
32
35
  else {
33
36
  const result = runAudit(root);
34
37
  rawFindings = result.findings;
35
38
  scannedFiles = result.scannedFiles;
36
- console.log(` Scanned ${scannedFiles} files`);
39
+ if (!options.json)
40
+ success("Scan complete", `${scannedFiles} files`);
37
41
  }
38
42
  const findings = deduplicateFindings(rawFindings);
39
- console.log("");
43
+ if (!options.json)
44
+ blank();
40
45
  const configFrameworks = (config?.frameworks || ["GDPR", "OWASP"]);
41
46
  const projectPacks = getPacksForProjectType(config?.project_type || "generic-web-application");
42
47
  const packIds = new Set(projectPacks.map(p => p.id));
@@ -76,7 +81,8 @@ export const auditCommand = new Command("audit")
76
81
  if (overrides.length > 0) {
77
82
  const naCount = overrides.filter(o => o.status === "not-applicable").length;
78
83
  const passCount = overrides.filter(o => o.status === "pass").length;
79
- console.log(` Control overrides: ${naCount} not-applicable, ${passCount} pre-verified\n`);
84
+ if (!options.json)
85
+ info("Control overrides", `${naCount} not-applicable, ${passCount} pre-verified`);
80
86
  }
81
87
  if (options.json) {
82
88
  console.log(JSON.stringify({ findings, score: scoreData }, null, 2));
@@ -84,34 +90,37 @@ export const auditCommand = new Command("audit")
84
90
  process.exit(1);
85
91
  return;
86
92
  }
87
- console.log(" ── Findings ─────────────────────\n");
88
- console.log(` Total findings: ${findings.length}`);
89
- console.log(` Critical: ${critical.length} High: ${high.length} Medium: ${medium.length} Low: ${low.length}\n`);
93
+ console.log(` ${BOLD("Findings")}`);
94
+ divider(40);
95
+ console.log(` ${DIM("Total")} ${findings.length}`);
96
+ console.log(` ${RED(`Critical ${critical.length}`)} ${RED(`High ${high.length}`)} ${YELLOW(`Medium ${medium.length}`)} ${CYAN(`Low ${low.length}`)}\n`);
90
97
  if (findings.length > 0) {
91
98
  const grouped = groupByCategory(findings);
92
99
  for (const [category, categoryFindings] of Object.entries(grouped)) {
93
- console.log(` [${category.toUpperCase()}]`);
100
+ console.log(` ${BOLD(category.toUpperCase())}`);
94
101
  for (const f of categoryFindings.slice(0, 10)) {
95
- const sev = f.severity === "critical" ? "CRIT" : f.severity === "high" ? "HIGH" : f.severity === "medium" ? "MED " : "LOW ";
96
- const loc = f.file !== "project" ? ` (${f.file}${f.line ? ":" + f.line : ""})` : "";
97
- console.log(` [${sev}] ${f.title}${loc}`);
102
+ const loc = f.file !== "project" ? ` ${DIM(`(${f.file}${f.line ? ":" + f.line : ""})`)}` : "";
103
+ console.log(` ${severityBadge(f.severity).padEnd(10)} ${f.title}${loc}`);
98
104
  if (f.evidence && f.file !== "project") {
99
- console.log(` ${f.evidence.slice(0, 100)}`);
105
+ console.log(` ${GRAY(f.evidence.slice(0, 100))}`);
100
106
  }
101
107
  }
102
108
  if (categoryFindings.length > 10) {
103
- console.log(` ... and ${categoryFindings.length - 10} more`);
109
+ console.log(` ${DIM(`... and ${categoryFindings.length - 10} more`)}`);
104
110
  }
105
111
  console.log("");
106
112
  }
107
113
  }
108
114
  else {
109
- console.log("No security or compliance issues found in source code.\n");
115
+ success("No security or compliance issues found in source code.");
116
+ blank();
110
117
  }
111
- console.log(" ── Compliance Score ──────────────");
118
+ console.log(` ${BOLD("Compliance Score")}`);
119
+ divider(40);
112
120
  console.log(formatScoreOutput(scoreData));
113
121
  if (critical.length > 0) {
114
- console.log(" !! Critical issues must be resolved before deployment. !!\n");
122
+ warn("Critical issues must be resolved before deployment.");
123
+ blank();
115
124
  }
116
125
  recordActivity(root, {
117
126
  source: "cli",
@@ -3,13 +3,13 @@ import { findProjectRoot, readJsonFile } from "../utils/project.js";
3
3
  import { CLI_VERSION } from "../utils/version.js";
4
4
  import { GES_DIR, loadGovernanceRecords, verifyGovernanceRecord } from "@greenarmor/ges-core";
5
5
  import { showNextStepsMenu } from "../utils/next-steps.js";
6
+ import { banner, success, warn, error, blank, progressBar, BOLD, DIM, GREEN, RED, YELLOW } from "../utils/ui.js";
6
7
  import * as fs from "node:fs";
7
8
  import * as path from "node:path";
8
9
  export const doctorCommand = new Command("doctor")
9
10
  .description("Diagnose GESF configuration and health")
10
11
  .action(async () => {
11
- console.log("\n GESF Doctor - Diagnostic Check");
12
- console.log(" ─────────────────────────────\n");
12
+ banner("GESF Doctor", "Diagnostic health check");
13
13
  const checks = [];
14
14
  const root = findProjectRoot();
15
15
  if (root) {
@@ -82,11 +82,23 @@ export const doctorCommand = new Command("doctor")
82
82
  }
83
83
  }
84
84
  checks.push({ name: "GESF Version", status: "OK", detail: CLI_VERSION });
85
+ const okCount = checks.filter(c => c.status === "OK").length;
86
+ const warnCount = checks.filter(c => c.status === "WARN" || c.status === "MISSING").length;
87
+ const failCount = checks.filter(c => c.status === "FAIL").length;
88
+ console.log(` ${BOLD("Health Score")} ${progressBar(okCount, checks.length, 24)}`);
89
+ console.log(` ${DIM("Checks")} ${GREEN(`${okCount} ok`)} ${YELLOW(`${warnCount} warn`)} ${RED(`${failCount} fail`)}`);
90
+ blank();
85
91
  for (const check of checks) {
86
- const icon = check.status === "OK" ? "✓" : check.status === "WARN" ? "!" : "✗";
87
- const line = ` [${icon}] ${check.name}`;
88
- console.log(line + (check.detail ? ` - ${check.detail}` : ""));
92
+ if (check.status === "OK") {
93
+ success(check.name, check.detail);
94
+ }
95
+ else if (check.status === "WARN" || check.status === "MISSING") {
96
+ warn(check.name, check.detail);
97
+ }
98
+ else {
99
+ error(check.name, check.detail);
100
+ }
89
101
  }
90
- console.log("");
102
+ blank();
91
103
  await showNextStepsMenu("doctor");
92
104
  });
@@ -1,6 +1,7 @@
1
1
  import { Command } from "commander";
2
2
  import { ensureGESInitialized } from "../utils/project.js";
3
3
  import { input, select } from "../utils/prompts.js";
4
+ import { banner, divider, blank, success, info, kv, statusBadge, severityBadge, BOLD, DIM, GREEN, RED, YELLOW, GRAY, } from "../utils/ui.js";
4
5
  import { loadGovernanceRecords, createGovernanceRecord, addGovernanceRecord, findGovernanceRecord, setGovernanceApproval, addGovernanceEvidence, createEvidenceRef, verifyGovernanceRecord, deleteGovernanceRecord, setGovernanceRiskAssessment, setGovernancePolicyBasis, setGovernanceReviewCycle, setGovernanceDataInventory, setGovernanceComplianceLinks, setGovernanceCommittee, } from "@greenarmor/ges-core";
5
6
  import { recordActivity } from "@greenarmor/ges-core";
6
7
  const STATUS_BADGE = {
@@ -19,17 +20,17 @@ const RISK_COLOR = {
19
20
  critical: "CRITICAL",
20
21
  };
21
22
  function printRecordSummary(record) {
22
- const badge = STATUS_BADGE[record.status] || "?";
23
- console.log(` ${badge} ${record.id} ${record.system_name}`);
24
- console.log(` Type: ${record.system_type} | Risk: ${RISK_COLOR[record.risk_level] || record.risk_level} | Status: ${record.status}`);
23
+ console.log(` ${statusBadge(record.status)} ${BOLD(record.system_name)}`);
24
+ console.log(` ${DIM("ID")} ${record.id}`);
25
+ console.log(` ${DIM("Type")} ${record.system_type} ${GRAY("|")} ${DIM("Risk")} ${severityBadge(record.risk_level)}`);
25
26
  if (record.approval) {
26
- console.log(` Approved by: ${record.approval.approver_name} (${record.approval.approver_role})`);
27
- console.log(` Valid: ${record.approval.valid_from} → ${record.approval.valid_until || "indefinite"}`);
27
+ console.log(` ${DIM("By")} ${record.approval.approver_name} (${record.approval.approver_role})`);
28
+ console.log(` ${DIM("Valid")} ${record.approval.valid_from} ${GRAY("")} ${record.approval.valid_until || "indefinite"}`);
28
29
  }
29
30
  else {
30
- console.log(` Approval: NOT RECORDED`);
31
+ console.log(` ${DIM("By")} ${GRAY("NOT RECORDED")}`);
31
32
  }
32
- console.log(` Evidence: ${record.evidence.length} reference(s)`);
33
+ console.log(` ${DIM("Ev")} ${record.evidence.length} reference(s)`);
33
34
  }
34
35
  export const governanceCommand = new Command("governance")
35
36
  .description("Manage governance approval provenance chains")
@@ -78,13 +79,15 @@ export const governanceCommand = new Command("governance")
78
79
  created_by: "cli-user",
79
80
  });
80
81
  addGovernanceRecord(root, record);
81
- console.log(`\n [✓] Governance record created`);
82
- console.log(` ID: ${record.id}`);
82
+ blank();
83
+ success("Governance record created");
84
+ kv("ID", record.id, 6);
85
+ console.log();
83
86
  printRecordSummary(record);
84
- console.log(`\n Next steps:`);
85
- console.log(` ges governance approve ${record.id} Record approval decision`);
86
- console.log(` ges governance evidence ${record.id} Add evidence reference`);
87
- console.log(` ges governance verify ${record.id} Verify provenance chain\n`);
87
+ console.log(`\n ${DIM("Next steps:")}`);
88
+ console.log(` ${GRAY("–")} ${GREEN("ges governance approve")} ${record.id} ${DIM("Record approval decision")}`);
89
+ console.log(` ${GRAY("–")} ${GREEN("ges governance evidence")} ${record.id} ${DIM("Add evidence reference")}`);
90
+ console.log(` ${GRAY("–")} ${GREEN("ges governance verify")} ${record.id} ${DIM("Verify provenance chain")}\n`);
88
91
  recordActivity(root, {
89
92
  source: "cli",
90
93
  action: "control_override",
@@ -148,10 +151,12 @@ export const governanceCommand = new Command("governance")
148
151
  console.error(` Error: Failed to update record.`);
149
152
  process.exit(1);
150
153
  }
151
- console.log(`\n [✓] Approval recorded for ${updated.system_name}`);
152
- console.log(` Decision: ${decision.toUpperCase()}`);
153
- console.log(` Approver: ${approverName} (${approverRole})`);
154
- console.log(` Valid: ${validFrom} ${validUntil || "indefinite"}\n`);
154
+ blank();
155
+ success("Approval recorded", `for ${updated.system_name}`);
156
+ kv("Decision", decision.toUpperCase(), 6);
157
+ kv("Approver", `${approverName} (${approverRole})`, 6);
158
+ kv("Valid", `${validFrom} → ${validUntil || "indefinite"}`, 6);
159
+ console.log();
155
160
  recordActivity(root, {
156
161
  source: "cli",
157
162
  action: "control_override",
@@ -223,10 +228,13 @@ export const governanceCommand = new Command("governance")
223
228
  console.error(` Error: Failed to add evidence.`);
224
229
  process.exit(1);
225
230
  }
226
- console.log(`\n [✓] Evidence added to ${updated.system_name}`);
227
- console.log(` ${evidence.title}`);
228
- console.log(` Source: ${evidence.source_system} | Ref: ${evidence.reference}`);
229
- console.log(` Total evidence: ${updated.evidence.length} reference(s)\n`);
231
+ blank();
232
+ success("Evidence added", `to ${updated.system_name}`);
233
+ console.log(` ${BOLD(evidence.title)}`);
234
+ kv("Source", evidence.source_system, 6);
235
+ kv("Ref", evidence.reference, 6);
236
+ kv("Total", `${updated.evidence.length} reference(s)`, 6);
237
+ console.log();
230
238
  recordActivity(root, {
231
239
  source: "cli",
232
240
  action: "control_override",
@@ -243,14 +251,16 @@ export const governanceCommand = new Command("governance")
243
251
  const root = ensureGESInitialized();
244
252
  const records = loadGovernanceRecords(root);
245
253
  if (records.length === 0) {
246
- console.log(`\n No governance records found.`);
247
- console.log(` Create one with: ges governance add\n`);
254
+ info("No governance records found.");
255
+ console.log(` ${DIM("Create one with:")} ${GREEN("ges governance add")}\n`);
248
256
  return;
249
257
  }
250
- console.log(`\n Governance Records (${records.length}):\n`);
258
+ blank();
259
+ console.log(` ${BOLD("Governance Records")} ${GRAY(`(${records.length})`)}`);
260
+ console.log();
251
261
  records.forEach(r => {
252
262
  printRecordSummary(r);
253
- console.log("");
263
+ console.log();
254
264
  });
255
265
  }))
256
266
  .addCommand(new Command("show")
@@ -411,34 +421,43 @@ export const governanceCommand = new Command("governance")
411
421
  process.exit(1);
412
422
  }
413
423
  const result = verifyGovernanceRecord(record);
414
- console.log(`\n ═══════════════════════════════════════════════════`);
415
- console.log(` VERIFICATION: ${record.system_name}`);
416
- console.log(` ═══════════════════════════════════════════════════\n`);
417
- console.log(` Overall: ${result.valid ? "✓ VALID" : "✕ ISSUES FOUND"}`);
418
- console.log(` Approval Status: ${result.approval_status.toUpperCase()}`);
424
+ banner("VERIFICATION", record.system_name);
425
+ const overallText = result.valid ? GREEN(BOLD("✓ VALID")) : RED(BOLD("✕ ISSUES FOUND"));
426
+ console.log(` ${DIM("Overall:")} ${overallText}`);
427
+ console.log(` ${DIM("Approval:")} ${statusBadge(result.approval_status)}`);
419
428
  if (result.days_until_expiry !== null) {
420
- const dayLabel = result.days_until_expiry < 0 ? `${Math.abs(result.days_until_expiry)} days AGO` : `${result.days_until_expiry} days remaining`;
421
- console.log(` Expiry: ${dayLabel}`);
422
- }
423
- console.log(` Evidence Count: ${result.completeness.evidence_count}`);
424
- console.log(`\n Completeness Checklist:`);
425
- console.log(` ${result.completeness.has_approval ? "✓" : "✕"} Approval Decision`);
426
- console.log(` ${result.completeness.has_risk_assessment ? "✓" : "✕"} Risk Assessment`);
427
- console.log(` ${result.completeness.has_policy_basis ? "✓" : "✕"} Policy Basis`);
428
- console.log(` ${result.completeness.has_evidence ? "✓" : "✕"} Evidence Chain`);
429
- console.log(` ${result.completeness.has_review_cycle ? "✓" : "△"} Review Cycle`);
430
- console.log(` ${result.completeness.has_data_inventory ? "✓" : "△"} Data Inventory`);
431
- console.log(` ${result.completeness.has_compliance_links ? "✓" : "△"} Compliance Links`);
432
- console.log(` ${result.completeness.is_current ? "✓" : "✕"} Currently Valid`);
429
+ const dayLabel = result.days_until_expiry < 0
430
+ ? RED(`${Math.abs(result.days_until_expiry)} days AGO`)
431
+ : result.days_until_expiry <= 30
432
+ ? YELLOW(`${result.days_until_expiry} days remaining`)
433
+ : GREEN(`${result.days_until_expiry} days remaining`);
434
+ console.log(` ${DIM("Expiry:")} ${dayLabel}`);
435
+ }
436
+ console.log(` ${DIM("Evidence:")} ${result.completeness.evidence_count} reference(s)`);
437
+ console.log(`\n ${BOLD("Completeness Checklist")}`);
438
+ divider(40);
439
+ const check = (ok, label, isWarning = false) => {
440
+ const icon = ok ? GREEN("✓") : isWarning ? YELLOW("△") : RED("✕");
441
+ const text = ok ? label : isWarning ? YELLOW(label) : RED(label);
442
+ console.log(` ${icon} ${text}`);
443
+ };
444
+ check(result.completeness.has_approval, "Approval Decision");
445
+ check(result.completeness.has_risk_assessment, "Risk Assessment");
446
+ check(result.completeness.has_policy_basis, "Policy Basis");
447
+ check(result.completeness.has_evidence, "Evidence Chain");
448
+ check(result.completeness.has_review_cycle, "Review Cycle", true);
449
+ check(result.completeness.has_data_inventory, "Data Inventory", true);
450
+ check(result.completeness.has_compliance_links, "Compliance Links", true);
451
+ check(result.completeness.is_current, "Currently Valid");
433
452
  if (result.issues.length > 0) {
434
- console.log(`\n BLOCKING ISSUES:`);
435
- result.issues.forEach(i => console.log(` ✕ ${i}`));
453
+ console.log(`\n ${RED(BOLD("BLOCKING ISSUES"))}`);
454
+ result.issues.forEach(i => console.log(` ${RED("")} ${i}`));
436
455
  }
437
456
  if (result.warnings.length > 0) {
438
- console.log(`\n WARNINGS:`);
439
- result.warnings.forEach(w => console.log(` △ ${w}`));
457
+ console.log(`\n ${YELLOW(BOLD("WARNINGS"))}`);
458
+ result.warnings.forEach(w => console.log(` ${YELLOW("")} ${w}`));
440
459
  }
441
- console.log("");
460
+ console.log();
442
461
  }))
443
462
  .addCommand(new Command("delete")
444
463
  .description("Delete a governance record")
@@ -454,7 +473,9 @@ export const governanceCommand = new Command("governance")
454
473
  }
455
474
  const deleted = deleteGovernanceRecord(root, record.id);
456
475
  if (deleted) {
457
- console.log(`\n [✓] Deleted governance record: ${record.system_name} (${record.id})\n`);
476
+ blank();
477
+ success("Deleted governance record", `${record.system_name} (${record.id})`);
478
+ console.log();
458
479
  recordActivity(root, {
459
480
  source: "cli",
460
481
  action: "control_override",
@@ -507,8 +528,12 @@ export const governanceCommand = new Command("governance")
507
528
  console.error(` Error: Failed to update.`);
508
529
  process.exit(1);
509
530
  }
510
- console.log(`\n [✓] Risk assessment linked to ${updated.system_name}`);
511
- console.log(` Assessor: ${assessor} | Score: ${score} | Residual: ${residual}\n`);
531
+ blank();
532
+ success("Risk assessment linked", `to ${updated.system_name}`);
533
+ kv("Assessor", assessor, 6);
534
+ kv("Score", score, 6);
535
+ kv("Residual", residual, 6);
536
+ console.log();
512
537
  recordActivity(root, { source: "cli", action: "control_override", title: `Risk assessment added: ${updated.system_name}`, description: `Risk assessment by ${assessor} linked to ${updated.system_name}. Score: ${score}, Residual: ${residual}.`, details: { governance_record_id: updated.id }, actor_name: options.actor, actor_role: options.actorRole });
513
538
  }))
514
539
  .addCommand(new Command("policy-basis")
@@ -544,8 +569,11 @@ export const governanceCommand = new Command("governance")
544
569
  console.error(` Error: Failed to update.`);
545
570
  process.exit(1);
546
571
  }
547
- console.log(`\n [✓] Policy basis documented for ${updated.system_name}`);
548
- console.log(` ${policyName} (${policyId} v${version}) ${standard}\n`);
572
+ blank();
573
+ success("Policy basis documented", `for ${updated.system_name}`);
574
+ kv("Policy", `${policyName} (${policyId} v${version})`, 6);
575
+ kv("Standard", standard, 6);
576
+ console.log();
549
577
  recordActivity(root, { source: "cli", action: "control_override", title: `Policy basis added: ${updated.system_name}`, description: `Policy ${policyName} (${policyId} v${version}) documented for ${updated.system_name}.`, details: { governance_record_id: updated.id }, actor_name: options.actor, actor_role: options.actorRole });
550
578
  }))
551
579
  .addCommand(new Command("review-cycle")
@@ -583,8 +611,11 @@ export const governanceCommand = new Command("governance")
583
611
  console.error(` Error: Failed to update.`);
584
612
  process.exit(1);
585
613
  }
586
- console.log(`\n [✓] Review cycle set for ${updated.system_name}`);
587
- console.log(` Frequency: ${frequency} | Next review: ${nextReview}\n`);
614
+ blank();
615
+ success("Review cycle set", `for ${updated.system_name}`);
616
+ kv("Frequency", frequency, 6);
617
+ kv("Next review", nextReview, 6);
618
+ console.log();
588
619
  recordActivity(root, { source: "cli", action: "control_override", title: `Review cycle set: ${updated.system_name}`, description: `Review cycle (${frequency}) set for ${updated.system_name}. Next review: ${nextReview}.`, details: { governance_record_id: updated.id }, actor_name: options.actor, actor_role: options.actorRole });
589
620
  }))
590
621
  .addCommand(new Command("data-inventory")
@@ -618,7 +649,9 @@ export const governanceCommand = new Command("governance")
618
649
  console.error(` Error: Failed to update.`);
619
650
  process.exit(1);
620
651
  }
621
- console.log(`\n [✓] Data inventory documented for ${updated.system_name}\n`);
652
+ blank();
653
+ success("Data inventory documented", `for ${updated.system_name}`);
654
+ console.log();
622
655
  recordActivity(root, { source: "cli", action: "control_override", title: `Data inventory added: ${updated.system_name}`, description: `Data inventory documented for ${updated.system_name}.`, details: { governance_record_id: updated.id }, actor_name: options.actor, actor_role: options.actorRole });
623
656
  }))
624
657
  .addCommand(new Command("committee")
@@ -653,8 +686,11 @@ export const governanceCommand = new Command("governance")
653
686
  console.error(` Error: Failed to update.`);
654
687
  process.exit(1);
655
688
  }
656
- console.log(`\n [✓] Committee approval recorded for ${updated.system_name}`);
657
- console.log(` ${committeeName} ${meetingDate} (${meetingRef})\n`);
689
+ blank();
690
+ success("Committee approval recorded", `for ${updated.system_name}`);
691
+ kv("Committee", committeeName, 6);
692
+ kv("Meeting", `${meetingDate} (${meetingRef})`, 6);
693
+ console.log();
658
694
  recordActivity(root, { source: "cli", action: "control_override", title: `Committee approval added: ${updated.system_name}`, description: `Committee ${committeeName} (${meetingRef}) recorded for ${updated.system_name}.`, details: { governance_record_id: updated.id }, actor_name: options.actor, actor_role: options.actorRole });
659
695
  }))
660
696
  .addCommand(new Command("compliance-links")
@@ -683,6 +719,8 @@ export const governanceCommand = new Command("governance")
683
719
  console.error(` Error: Failed to update.`);
684
720
  process.exit(1);
685
721
  }
686
- console.log(`\n [✓] Compliance links mapped for ${updated.system_name}\n`);
722
+ blank();
723
+ success("Compliance links mapped", `for ${updated.system_name}`);
724
+ console.log();
687
725
  recordActivity(root, { source: "cli", action: "control_override", title: `Compliance links added: ${updated.system_name}`, description: `Compliance frameworks mapped for ${updated.system_name}.`, details: { governance_record_id: updated.id }, actor_name: options.actor, actor_role: options.actorRole });
688
726
  }));
@@ -1,5 +1,6 @@
1
1
  import { Command } from "commander";
2
2
  import { input, select, checkbox } from "../utils/prompts.js";
3
+ import { banner, divider, blank, success, error, warn, info, step, kv, label, BOLD, CYAN, GREEN, GRAY, } from "../utils/ui.js";
3
4
  import { PROJECT_TYPES, FRAMEWORKS, DEFAULT_FRAMEWORKS, GES_DIR, COMPLIANCE_DIR, SECURITY_DIR, CONTROLS_DIR, POLICIES_DIR, CHECKLISTS_DIR, DOCS_DIR, REPORTS_DIR, } from "@greenarmor/ges-core";
4
5
  import { CLI_VERSION } from "../utils/version.js";
5
6
  import { recordActivity } from "@greenarmor/ges-core";
@@ -18,16 +19,15 @@ export const initCommand = new Command("init")
18
19
  .option("-c, --country <country>", "Country of origin (e.g., BR, CA, US-CA, GB, SG)")
19
20
  .option("--force", "Re-initialize even if GESF is already set up")
20
21
  .action(async (options) => {
21
- console.log("\n Green Engineering Standard Framework (GESF) v" + CLI_VERSION);
22
- console.log(" ─────────────────────────────────────────────\n");
22
+ banner(`Green Engineering Standard Framework`, `v${CLI_VERSION}`);
23
23
  const gesDir = path.join(process.cwd(), GES_DIR);
24
24
  if (fs.existsSync(gesDir)) {
25
25
  if (!options.force) {
26
- console.error(" Error: GESF is already initialized in this project.");
27
- console.error(" Use 'ges init --force' to re-initialize.\n");
26
+ error("GESF is already initialized in this project.");
27
+ info("Use", "ges init --force to re-initialize.\n");
28
28
  process.exit(1);
29
29
  }
30
- console.log("Re-initializing GESF (existing files will be overwritten)...\n");
30
+ warn("Re-initializing GESF", "existing files will be overwritten\n");
31
31
  fs.rmSync(gesDir, { recursive: true, force: true });
32
32
  }
33
33
  const projectName = options.name || await input({ message: "Project name:", default: path.basename(process.cwd()) });
@@ -48,7 +48,7 @@ export const initCommand = new Command("init")
48
48
  })),
49
49
  });
50
50
  if (selectedFrameworks.length === 0) {
51
- console.error(" Error: At least one framework must be selected.");
51
+ error("At least one framework must be selected.");
52
52
  process.exit(1);
53
53
  }
54
54
  // --- Mandatory: Country of Origin ---
@@ -80,8 +80,8 @@ export const initCommand = new Command("init")
80
80
  countryCode = countryCode.toUpperCase();
81
81
  const countryInfo = getCountryByCode(countryCode);
82
82
  if (options.country && !countryInfo && countryCode !== "EU") {
83
- console.warn(`Country code '${options.country}' not recognized. No privacy pack will be auto-installed.`);
84
- console.warn(` Available codes: ${PRIVACY_COUNTRIES.map(c => c.code).join(", ")}, EU`);
83
+ warn(`Country code '${options.country}' not recognized.`, "No privacy pack will be auto-installed.");
84
+ info("Available codes:", `${PRIVACY_COUNTRIES.map(c => c.code).join(", ")}, EU`);
85
85
  }
86
86
  // --- Optional: Additional privacy packs ---
87
87
  const additionalPacks = await checkbox({
@@ -193,31 +193,39 @@ export const initCommand = new Command("init")
193
193
  for (const wf of workflows) {
194
194
  writeFileSync(path.join(process.cwd(), wf.filePath), wf.content);
195
195
  }
196
- console.log(" ✓ Project structure created");
197
- console.log(" Configuration files generated");
198
- console.log(" Compliance documents created");
199
- console.log(" Security documents created");
196
+ blank();
197
+ step(1, 4, "Creating project structure");
198
+ success("Project structure created");
199
+ success("Configuration files generated");
200
+ success("Compliance documents created");
201
+ success("Security documents created");
200
202
  if (countryInfo) {
201
- console.log(` ✓ Country privacy pack auto-installed: ${countryInfo.packId} (${countryInfo.name})`);
203
+ success("Country privacy pack auto-installed", `${countryInfo.packId} (${countryInfo.name})`);
202
204
  }
203
205
  else if (countryCode === "EU") {
204
- console.log("EU GDPR privacy pack auto-installed");
206
+ success("EU GDPR privacy pack auto-installed");
205
207
  }
206
208
  if (additionalPacks.length > 0) {
207
- console.log(` ✓ Additional privacy packs installed: ${additionalPacks.join(", ")}`);
208
- }
209
- console.log("Control packs installed:", packs.map(p => p.id).join(", "));
210
- console.log("GitHub Actions workflows generated");
211
- console.log("Developer logs directory created (.dev-logs/)");
212
- console.log(`\n GESF initialized for "${projectName}" (${projectType})`);
209
+ success("Additional privacy packs installed", additionalPacks.join(", "));
210
+ }
211
+ success("Control packs installed", packs.map(p => p.id).join(", "));
212
+ success("GitHub Actions workflows generated");
213
+ success("Developer logs directory created", ".dev-logs/");
214
+ blank();
215
+ step(2, 4, "Project summary");
216
+ blank();
217
+ console.log(` ${CYAN(BOLD("GESF initialized"))} for "${projectName}" (${projectType})`);
213
218
  if (countryInfo) {
214
- console.log(` Country: ${countryInfo.name} — ${countryInfo.lawName}`);
215
- }
216
- console.log(" Next steps:");
217
- console.log(" 1. Review generated compliance documents");
218
- console.log(" 2. Run 'ges audit' to evaluate your project");
219
- console.log(" 3. Run 'ges score' to see your compliance score");
220
- console.log(" 4. Add more packs with 'ges policy install <pack-id>'\n");
219
+ kv("Country", `${countryInfo.name} — ${countryInfo.lawName}`);
220
+ }
221
+ divider(40);
222
+ blank();
223
+ step(3, 4, "Next steps");
224
+ label("Quick start:");
225
+ console.log(` ${GRAY("1.")} Review generated compliance documents`);
226
+ console.log(` ${GRAY("2.")} Run ${GREEN("ges audit")} to evaluate your project`);
227
+ console.log(` ${GRAY("3.")} Run ${GREEN("ges score")} to see your compliance score`);
228
+ console.log(` ${GRAY("4.")} Add more packs with ${GREEN("ges policy install <pack-id>")}`);
221
229
  recordActivity(process.cwd(), {
222
230
  source: "cli",
223
231
  action: "init",
@@ -3,6 +3,7 @@ import { getAllPacks, listPackIds } from "@greenarmor/ges-policy-engine";
3
3
  import { ensureGESInitialized, writeFileSync } from "../utils/project.js";
4
4
  import { addFrameworkToConfig, removeFrameworkFromConfig, recordActivity } from "@greenarmor/ges-core";
5
5
  import { showNextStepsMenu } from "../utils/next-steps.js";
6
+ import { banner, blank, success, error, BOLD, CYAN, DIM, GRAY } from "../utils/ui.js";
6
7
  import * as fs from "node:fs";
7
8
  import * as path from "node:path";
8
9
  const policyCmd = new Command("policy")
@@ -11,12 +12,11 @@ policyCmd
11
12
  .command("list")
12
13
  .description("List available policy packs")
13
14
  .action(async () => {
14
- console.log("\n Available Policy Packs:\n");
15
+ banner("Policy Packs", "Available compliance control packs");
15
16
  const packs = getAllPacks();
16
17
  for (const pack of packs) {
17
- console.log(` ${pack.id.padEnd(15)} ${pack.name}`);
18
- const indent = " ";
19
- console.log(` ${indent} ${pack.controls.length} controls | ${pack.project_types.join(", ")}`);
18
+ console.log(` ${CYAN(BOLD(pack.id.padEnd(18)))} ${pack.name}`);
19
+ console.log(` ${DIM(`${pack.controls.length} controls`)} ${GRAY("|")} ${DIM(pack.project_types.join(", "))}`);
20
20
  console.log("");
21
21
  }
22
22
  await showNextStepsMenu("policy-list");
@@ -29,7 +29,7 @@ policyCmd
29
29
  const packs = getAllPacks();
30
30
  const pack = packs.find(p => p.id === packId);
31
31
  if (!pack) {
32
- console.error(` Error: Pack '${packId}' not found. Available: ${listPackIds().join(", ")}`);
32
+ error(`Pack '${packId}' not found.`, `Available: ${listPackIds().join(", ")}`);
33
33
  process.exit(1);
34
34
  }
35
35
  const packDir = path.join(root, "controls", pack.id);
@@ -41,11 +41,13 @@ policyCmd
41
41
  frameworksAdded.push(fw);
42
42
  }
43
43
  }
44
- console.log(`\n ✓ Installed policy pack: ${pack.id} (${pack.controls.length} controls)`);
44
+ blank();
45
+ success("Installed policy pack", `${pack.id} (${pack.controls.length} controls)`);
45
46
  if (frameworksAdded.length > 0) {
46
- console.log(` ✓ Added ${frameworksAdded.join(", ")} to project frameworks in .ges/config.json`);
47
+ success("Updated project frameworks", frameworksAdded.join(", "));
47
48
  }
48
- console.log("Dashboard will now reflect this pack's controls\n");
49
+ success("Dashboard will reflect this pack's controls");
50
+ blank();
49
51
  recordActivity(root, {
50
52
  source: "cli",
51
53
  action: "policy_install",
@@ -62,7 +64,7 @@ policyCmd
62
64
  const root = ensureGESInitialized();
63
65
  const packDir = path.join(root, "controls", packId);
64
66
  if (!fs.existsSync(packDir)) {
65
- console.error(` Error: Pack '${packId}' is not installed.`);
67
+ error(`Pack '${packId}' is not installed.`);
66
68
  process.exit(1);
67
69
  }
68
70
  fs.rmSync(packDir, { recursive: true, force: true });
@@ -76,7 +78,9 @@ policyCmd
76
78
  else {
77
79
  removeFrameworkFromConfig(root, packId.toUpperCase());
78
80
  }
79
- console.log(`\n ✓ Removed policy pack: ${packId}\n`);
81
+ blank();
82
+ success("Removed policy pack", packId);
83
+ blank();
80
84
  recordActivity(root, {
81
85
  source: "cli",
82
86
  action: "policy_remove",
@@ -3,6 +3,7 @@ import { ensureGESInitialized, readJsonFile } from "../utils/project.js";
3
3
  import { recordActivity } from "@greenarmor/ges-core";
4
4
  import { formatScoreOutput } from "@greenarmor/ges-scoring-engine";
5
5
  import { showNextStepsMenu } from "../utils/next-steps.js";
6
+ import { warn, info, blank, DIM } from "../utils/ui.js";
6
7
  import * as path from "node:path";
7
8
  export const scoreCommand = new Command("score")
8
9
  .description("Calculate and display compliance score")
@@ -12,7 +13,10 @@ export const scoreCommand = new Command("score")
12
13
  const scorePath = path.join(root, ".ges", "score.json");
13
14
  const score = readJsonFile(scorePath);
14
15
  if (!score || !score.frameworks || Object.keys(score.frameworks).length === 0) {
15
- console.log("\n No compliance score available. Run 'ges audit' first.\n");
16
+ blank();
17
+ warn("No compliance score available.");
18
+ info("Run", "ges audit first.");
19
+ blank();
16
20
  await showNextStepsMenu("score");
17
21
  return;
18
22
  }
@@ -21,7 +25,7 @@ export const scoreCommand = new Command("score")
21
25
  }
22
26
  else {
23
27
  console.log(formatScoreOutput(score));
24
- console.log(` Last evaluated: ${score.evaluated_at}\n`);
28
+ console.log(` ${DIM("Last evaluated:")} ${score.evaluated_at}\n`);
25
29
  }
26
30
  recordActivity(root, {
27
31
  source: "cli",
@@ -1,5 +1,5 @@
1
1
  import { select } from "./prompts.js";
2
- const DIVIDER = " ─────────────────────────────────────────────";
2
+ import { divider, blank, label, info, GREEN } from "./ui.js";
3
3
  function isInteractive() {
4
4
  return process.stdin.isTTY === true && process.stdout.isTTY === true;
5
5
  }
@@ -15,6 +15,7 @@ const ALL_COMMANDS = {
15
15
  generate: { label: "Regenerate docs", value: "ges generate --all", description: "Update all documentation and workflows" },
16
16
  policy: { label: "Manage policies", value: "ges policy list", description: "List, install, or remove policy packs" },
17
17
  update: { label: "Check updates", value: "ges update", description: "Check for GESF updates" },
18
+ governance: { label: "Governance", value: "ges governance list", description: "View approval provenance chains" },
18
19
  };
19
20
  function buildSteps(exclude) {
20
21
  const steps = [];
@@ -55,6 +56,8 @@ export function getNextStepsForCommand(command, context) {
55
56
  return buildSteps(["policy"]);
56
57
  case "update":
57
58
  return buildSteps(["update"]);
59
+ case "governance":
60
+ return buildSteps(["governance"]);
58
61
  case "mcp-setup":
59
62
  return buildSteps(["mcp-setup"]);
60
63
  default:
@@ -65,10 +68,10 @@ export async function showNextStepsMenu(command, context) {
65
68
  if (!isInteractive())
66
69
  return;
67
70
  const steps = getNextStepsForCommand(command, context);
68
- console.log(DIVIDER);
69
- console.log(" What would you like to do next?\n");
71
+ divider();
72
+ label("What would you like to do next?");
70
73
  const answer = await select({
71
- message: "Select next action:",
74
+ message: "Choose your next action:",
72
75
  choices: steps.map(step => ({
73
76
  name: step.description ? `${step.label} — ${step.description}` : step.label,
74
77
  value: step.value,
@@ -78,8 +81,10 @@ export async function showNextStepsMenu(command, context) {
78
81
  console.log("");
79
82
  return;
80
83
  }
81
- console.log(`\n Running: ${answer}\n`);
82
- console.log(DIVIDER + "\n");
84
+ blank();
85
+ info("Running", GREEN(answer));
86
+ divider();
87
+ blank();
83
88
  const { execSync } = await import("node:child_process");
84
89
  try {
85
90
  execSync(answer, { stdio: "inherit" });
@@ -7,7 +7,9 @@ export declare function select<T = string>(options: {
7
7
  choices: {
8
8
  name: string;
9
9
  value: T;
10
+ description?: string;
10
11
  }[];
12
+ pageSize?: number;
11
13
  }): Promise<T>;
12
14
  export declare function checkbox<T = string>(options: {
13
15
  message: string;
@@ -15,5 +17,11 @@ export declare function checkbox<T = string>(options: {
15
17
  name: string;
16
18
  value: T;
17
19
  checked?: boolean;
20
+ description?: string;
18
21
  }[];
22
+ pageSize?: number;
19
23
  }): Promise<T[]>;
24
+ export declare function confirm(options: {
25
+ message: string;
26
+ default?: boolean;
27
+ }): Promise<boolean>;
@@ -1,4 +1,5 @@
1
1
  import * as readline from "node:readline";
2
+ import { chalk, DIM, GREEN, CYAN, GRAY } from "./ui.js";
2
3
  function isInteractive() {
3
4
  return process.stdin.isTTY === true && process.stdout.isTTY === true;
4
5
  }
@@ -18,6 +19,17 @@ async function getInquirer() {
18
19
  }
19
20
  return cachedInquirer;
20
21
  }
22
+ const selectTheme = {
23
+ prefix: { idle: chalk.gray("?"), done: chalk.green("✓") },
24
+ helpMode: "always",
25
+ };
26
+ const checkboxTheme = {
27
+ prefix: { idle: chalk.gray("?"), done: chalk.green("✓") },
28
+ helpMode: "always",
29
+ };
30
+ const inputTheme = {
31
+ prefix: { idle: chalk.gray("?"), done: chalk.green("✓") },
32
+ };
21
33
  export async function input(options) {
22
34
  if (!isInteractive()) {
23
35
  return options.default ?? "";
@@ -27,9 +39,9 @@ export async function input(options) {
27
39
  return inquirer.input({ message: options.message, default: options.default });
28
40
  }
29
41
  const rl = createRL();
30
- const suffix = options.default ? ` (${options.default})` : "";
42
+ const suffix = options.default ? DIM(` (${options.default})`) : "";
31
43
  return new Promise((resolve) => {
32
- rl.question(` ${options.message}${suffix}: `, (answer) => {
44
+ rl.question(` ${GRAY("?")} ${options.message}${suffix}${GRAY(":")} `, (answer) => {
33
45
  rl.close();
34
46
  resolve(answer.trim() || options.default || "");
35
47
  });
@@ -41,15 +53,21 @@ export async function select(options) {
41
53
  }
42
54
  const inquirer = await getInquirer();
43
55
  if (inquirer) {
44
- return inquirer.select({ message: options.message, choices: options.choices });
56
+ return inquirer.select({
57
+ message: options.message,
58
+ choices: options.choices,
59
+ theme: selectTheme,
60
+ pageSize: options.pageSize ?? 10,
61
+ });
45
62
  }
46
- console.log(`\n ${options.message}:\n`);
63
+ console.log(`\n ${CYAN(options.message)}:\n`);
47
64
  options.choices.forEach((c, i) => {
48
- console.log(` ${i + 1}) ${c.name}`);
65
+ const num = GRAY(`${String(i + 1).padStart(2)}.`);
66
+ console.log(` ${num} ${c.name}`);
49
67
  });
50
68
  const rl = createRL();
51
69
  return new Promise((resolve) => {
52
- rl.question(`\n Enter choice [1-${options.choices.length}]: `, (answer) => {
70
+ rl.question(`\n ${GRAY("Enter choice")} [1-${options.choices.length}]: `, (answer) => {
53
71
  rl.close();
54
72
  const num = parseInt(answer.trim(), 10);
55
73
  if (num >= 1 && num <= options.choices.length) {
@@ -67,16 +85,22 @@ export async function checkbox(options) {
67
85
  }
68
86
  const inquirer = await getInquirer();
69
87
  if (inquirer) {
70
- return inquirer.checkbox({ message: options.message, choices: options.choices });
88
+ return inquirer.checkbox({
89
+ message: options.message,
90
+ choices: options.choices,
91
+ theme: checkboxTheme,
92
+ pageSize: options.pageSize ?? 10,
93
+ });
71
94
  }
72
- console.log(`\n ${options.message} (comma-separated numbers):\n`);
95
+ console.log(`\n ${CYAN(options.message)} ${GRAY("(comma-separated numbers)")}\n`);
73
96
  options.choices.forEach((c, i) => {
74
- const marker = c.checked ? "[x]" : "[ ]";
75
- console.log(` ${marker} ${i + 1}) ${c.name}`);
97
+ const marker = c.checked ? GREEN("[x]") : GRAY("[ ]");
98
+ const num = `${String(i + 1).padStart(2)}.`;
99
+ console.log(` ${marker} ${GRAY(num)} ${c.name}`);
76
100
  });
77
101
  const rl = createRL();
78
102
  return new Promise((resolve) => {
79
- rl.question(`\n Enter choices [1-${options.choices.length}]: `, (answer) => {
103
+ rl.question(`\n ${GRAY("Enter choices")} [1-${options.choices.length}]: `, (answer) => {
80
104
  rl.close();
81
105
  const trimmed = answer.trim();
82
106
  if (!trimmed) {
@@ -92,3 +116,30 @@ export async function checkbox(options) {
92
116
  });
93
117
  });
94
118
  }
119
+ export async function confirm(options) {
120
+ if (!isInteractive()) {
121
+ return options.default ?? false;
122
+ }
123
+ const inquirer = await getInquirer();
124
+ if (inquirer) {
125
+ return inquirer.confirm({
126
+ message: options.message,
127
+ default: options.default,
128
+ theme: inputTheme,
129
+ });
130
+ }
131
+ const suffix = options.default !== undefined ? ` (${options.default ? "Y/n" : "y/N"})` : " (y/n)";
132
+ const rl = createRL();
133
+ return new Promise((resolve) => {
134
+ rl.question(` ${GRAY("?")} ${options.message}${suffix}: `, (answer) => {
135
+ rl.close();
136
+ const trimmed = answer.trim().toLowerCase();
137
+ if (!trimmed) {
138
+ resolve(options.default ?? false);
139
+ }
140
+ else {
141
+ resolve(trimmed === "y" || trimmed === "yes");
142
+ }
143
+ });
144
+ });
145
+ }
@@ -0,0 +1,37 @@
1
+ import chalk from "chalk";
2
+ declare const DIM: import("chalk").ChalkInstance;
3
+ declare const BOLD: import("chalk").ChalkInstance;
4
+ declare const GREEN: import("chalk").ChalkInstance;
5
+ declare const RED: import("chalk").ChalkInstance;
6
+ declare const YELLOW: import("chalk").ChalkInstance;
7
+ declare const CYAN: import("chalk").ChalkInstance;
8
+ declare const MAGENTA: import("chalk").ChalkInstance;
9
+ declare const GRAY: import("chalk").ChalkInstance;
10
+ declare const icons: {
11
+ success: string;
12
+ error: string;
13
+ warn: string;
14
+ info: string;
15
+ arrow: string;
16
+ bullet: string;
17
+ check: string;
18
+ cross: string;
19
+ dash: string;
20
+ };
21
+ export declare function banner(title: string, subtitle?: string): void;
22
+ export declare function divider(width?: number): void;
23
+ export declare function blank(): void;
24
+ export declare function success(message: string, detail?: string): void;
25
+ export declare function error(message: string, detail?: string): void;
26
+ export declare function warn(message: string, detail?: string): void;
27
+ export declare function info(message: string, detail?: string): void;
28
+ export declare function step(n: number, total: number, message: string): void;
29
+ export declare function kv(key: string, value: string, indent?: number): void;
30
+ export declare function label(text: string, color?: typeof GREEN): void;
31
+ export declare function item(text: string, value?: string): void;
32
+ export declare function group(title: string, lines: string[]): void;
33
+ export declare function progressBar(current: number, total: number, width?: number): string;
34
+ export declare function statusBadge(status: string): string;
35
+ export declare function severityBadge(severity: string): string;
36
+ export declare function gradeColor(grade: string): string;
37
+ export { chalk, icons, DIM, BOLD, GREEN, RED, YELLOW, CYAN, MAGENTA, GRAY };
@@ -0,0 +1,115 @@
1
+ import chalk from "chalk";
2
+ const DIM = chalk.dim;
3
+ const BOLD = chalk.bold;
4
+ const GREEN = chalk.green;
5
+ const RED = chalk.red;
6
+ const YELLOW = chalk.yellow;
7
+ const CYAN = chalk.cyan;
8
+ const MAGENTA = chalk.magenta;
9
+ const GRAY = chalk.gray;
10
+ const icons = {
11
+ success: GREEN("✓"),
12
+ error: RED("✕"),
13
+ warn: YELLOW("!"),
14
+ info: CYAN("○"),
15
+ arrow: GRAY("→"),
16
+ bullet: GRAY("•"),
17
+ check: GREEN("✓"),
18
+ cross: RED("✕"),
19
+ dash: GRAY("—"),
20
+ };
21
+ export function banner(title, subtitle) {
22
+ const line = "═".repeat(52);
23
+ console.log();
24
+ console.log(CYAN(BOLD(` ${title}`)));
25
+ if (subtitle) {
26
+ console.log(DIM(` ${subtitle}`));
27
+ }
28
+ console.log(GRAY(` ${line}`));
29
+ console.log();
30
+ }
31
+ export function divider(width = 52) {
32
+ console.log(GRAY(` ${"─".repeat(width)}`));
33
+ }
34
+ export function blank() {
35
+ console.log();
36
+ }
37
+ export function success(message, detail) {
38
+ console.log(` ${icons.success} ${GREEN(message)}${detail ? DIM(` ${detail}`) : ""}`);
39
+ }
40
+ export function error(message, detail) {
41
+ console.error(` ${icons.error} ${RED(message)}${detail ? DIM(` ${detail}`) : ""}`);
42
+ }
43
+ export function warn(message, detail) {
44
+ console.log(` ${icons.warn} ${YELLOW(message)}${detail ? DIM(` ${detail}`) : ""}`);
45
+ }
46
+ export function info(message, detail) {
47
+ console.log(` ${icons.info} ${CYAN(message)}${detail ? DIM(` ${detail}`) : ""}`);
48
+ }
49
+ export function step(n, total, message) {
50
+ const counter = DIM(`[${n}/${total}]`);
51
+ console.log(`\n ${counter} ${BOLD(message)}`);
52
+ console.log(GRAY(` ${"─".repeat(40)}`));
53
+ }
54
+ export function kv(key, value, indent = 4) {
55
+ const pad = Math.max(key.length, 16);
56
+ console.log(`${" ".repeat(indent)}${DIM(key.padEnd(pad))} ${value}`);
57
+ }
58
+ export function label(text, color = CYAN) {
59
+ console.log(`\n ${color(BOLD(text))}`);
60
+ }
61
+ export function item(text, value) {
62
+ const v = value ? DIM(GRAY(` ${value}`)) : "";
63
+ console.log(` ${icons.bullet} ${text}${v}`);
64
+ }
65
+ export function group(title, lines) {
66
+ console.log(`\n ${BOLD(title)}`);
67
+ for (const line of lines) {
68
+ console.log(` ${icons.bullet} ${line}`);
69
+ }
70
+ }
71
+ export function progressBar(current, total, width = 30) {
72
+ const pct = Math.round((current / total) * 100);
73
+ const filled = Math.round((current / total) * width);
74
+ const empty = width - filled;
75
+ const bar = GREEN("█".repeat(filled)) + GRAY("░".repeat(empty));
76
+ return `${bar} ${pct}%`;
77
+ }
78
+ export function statusBadge(status) {
79
+ const badges = {
80
+ pass: GREEN("● PASS"),
81
+ fail: RED("● FAIL"),
82
+ warning: YELLOW("● WARN"),
83
+ "not-implemented": GRAY("○ N/I"),
84
+ "not-applicable": CYAN("◐ N/A"),
85
+ approved: GREEN("● APPROVED"),
86
+ rejected: RED("✕ REJECTED"),
87
+ conditional: YELLOW("◔ CONDITIONAL"),
88
+ "pending-review": YELLOW("◐ PENDING"),
89
+ draft: GRAY("○ DRAFT"),
90
+ expired: RED("⚠ EXPIRED"),
91
+ valid: GREEN("✓ VALID"),
92
+ };
93
+ return badges[status] || GRAY(`○ ${status.toUpperCase()}`);
94
+ }
95
+ export function severityBadge(severity) {
96
+ const badges = {
97
+ critical: RED(BOLD("CRITICAL")),
98
+ high: RED("HIGH"),
99
+ medium: YELLOW("MEDIUM"),
100
+ low: CYAN("LOW"),
101
+ };
102
+ return badges[severity] || GRAY(severity.toUpperCase());
103
+ }
104
+ export function gradeColor(grade) {
105
+ if (grade === "A")
106
+ return GREEN(BOLD(grade));
107
+ if (grade === "B")
108
+ return CYAN(BOLD(grade));
109
+ if (grade === "C")
110
+ return YELLOW(BOLD(grade));
111
+ if (grade === "D")
112
+ return MAGENTA(BOLD(grade));
113
+ return RED(BOLD(grade));
114
+ }
115
+ export { chalk, icons, DIM, BOLD, GREEN, RED, YELLOW, CYAN, MAGENTA, GRAY };
package/package.json CHANGED
@@ -3,19 +3,20 @@
3
3
  "ges": "./dist/cli.js"
4
4
  },
5
5
  "dependencies": {
6
- "@greenarmor/ges-audit-engine": "1.4.0",
7
- "@greenarmor/ges-cicd-generator": "1.4.0",
8
- "@greenarmor/ges-compliance-engine": "1.4.0",
9
- "@greenarmor/ges-core": "1.4.0",
10
- "@greenarmor/ges-doc-generator": "1.4.0",
11
- "@greenarmor/ges-git-hooks": "1.4.0",
12
- "@greenarmor/ges-mcp-server": "1.4.0",
13
- "@greenarmor/ges-policy-engine": "1.4.0",
14
- "@greenarmor/ges-report-generator": "1.4.0",
15
- "@greenarmor/ges-rules-engine": "1.4.0",
16
- "@greenarmor/ges-scanner-integration": "1.4.0",
17
- "@greenarmor/ges-scoring-engine": "1.4.0",
18
- "@greenarmor/ges-web-dashboard": "1.4.0",
6
+ "@greenarmor/ges-audit-engine": "1.4.2",
7
+ "@greenarmor/ges-cicd-generator": "1.4.2",
8
+ "@greenarmor/ges-compliance-engine": "1.4.2",
9
+ "@greenarmor/ges-core": "1.4.2",
10
+ "@greenarmor/ges-doc-generator": "1.4.2",
11
+ "@greenarmor/ges-git-hooks": "1.4.2",
12
+ "@greenarmor/ges-mcp-server": "1.4.2",
13
+ "@greenarmor/ges-policy-engine": "1.4.2",
14
+ "@greenarmor/ges-report-generator": "1.4.2",
15
+ "@greenarmor/ges-rules-engine": "1.4.2",
16
+ "@greenarmor/ges-scanner-integration": "1.4.2",
17
+ "@greenarmor/ges-scoring-engine": "1.4.2",
18
+ "@greenarmor/ges-web-dashboard": "1.4.2",
19
+ "chalk": "^5.6.2",
19
20
  "commander": "^13.0.0"
20
21
  },
21
22
  "description": "Green Engineering Standard Framework - Compliance-as-Code CLI",
@@ -24,6 +25,9 @@
24
25
  "typescript": "^6.0.0",
25
26
  "vitest": "^4.1.8"
26
27
  },
28
+ "optionalDependencies": {
29
+ "@inquirer/prompts": "^7.10.1"
30
+ },
27
31
  "engines": {
28
32
  "node": ">=20.0.0"
29
33
  },
@@ -53,7 +57,7 @@
53
57
  },
54
58
  "type": "module",
55
59
  "types": "./dist/index.d.ts",
56
- "version": "1.4.0",
60
+ "version": "1.4.2",
57
61
  "scripts": {
58
62
  "build": "tsc",
59
63
  "clean": "rm -rf dist tsconfig.tsbuildinfo",