@greenarmor/ges 1.3.0 → 1.4.1

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.
@@ -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.3.0",
7
- "@greenarmor/ges-cicd-generator": "1.3.0",
8
- "@greenarmor/ges-compliance-engine": "1.3.0",
9
- "@greenarmor/ges-core": "1.3.0",
10
- "@greenarmor/ges-doc-generator": "1.3.0",
11
- "@greenarmor/ges-git-hooks": "1.3.0",
12
- "@greenarmor/ges-mcp-server": "1.3.0",
13
- "@greenarmor/ges-policy-engine": "1.3.0",
14
- "@greenarmor/ges-report-generator": "1.3.0",
15
- "@greenarmor/ges-rules-engine": "1.3.0",
16
- "@greenarmor/ges-scanner-integration": "1.3.0",
17
- "@greenarmor/ges-scoring-engine": "1.3.0",
18
- "@greenarmor/ges-web-dashboard": "1.3.0",
6
+ "@greenarmor/ges-audit-engine": "1.4.1",
7
+ "@greenarmor/ges-cicd-generator": "1.4.1",
8
+ "@greenarmor/ges-compliance-engine": "1.4.1",
9
+ "@greenarmor/ges-core": "1.4.1",
10
+ "@greenarmor/ges-doc-generator": "1.4.1",
11
+ "@greenarmor/ges-git-hooks": "1.4.1",
12
+ "@greenarmor/ges-mcp-server": "1.4.1",
13
+ "@greenarmor/ges-policy-engine": "1.4.1",
14
+ "@greenarmor/ges-report-generator": "1.4.1",
15
+ "@greenarmor/ges-rules-engine": "1.4.1",
16
+ "@greenarmor/ges-scanner-integration": "1.4.1",
17
+ "@greenarmor/ges-scoring-engine": "1.4.1",
18
+ "@greenarmor/ges-web-dashboard": "1.4.1",
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.3.0",
60
+ "version": "1.4.1",
57
61
  "scripts": {
58
62
  "build": "tsc",
59
63
  "clean": "rm -rf dist tsconfig.tsbuildinfo",