@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.
- package/dist/cli.js +2 -0
- package/dist/commands/audit.js +28 -19
- package/dist/commands/doctor.js +48 -7
- package/dist/commands/governance.d.ts +2 -0
- package/dist/commands/governance.js +726 -0
- package/dist/commands/init.js +35 -27
- package/dist/commands/policy.js +14 -10
- package/dist/commands/score.js +6 -2
- package/dist/utils/next-steps.js +11 -6
- package/dist/utils/prompts.d.ts +8 -0
- package/dist/utils/prompts.js +62 -11
- package/dist/utils/ui.d.ts +37 -0
- package/dist/utils/ui.js +115 -0
- package/package.json +18 -14
package/dist/commands/init.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
27
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
84
|
-
|
|
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
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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
|
-
|
|
203
|
+
success("Country privacy pack auto-installed", `${countryInfo.packId} (${countryInfo.name})`);
|
|
202
204
|
}
|
|
203
205
|
else if (countryCode === "EU") {
|
|
204
|
-
|
|
206
|
+
success("EU GDPR privacy pack auto-installed");
|
|
205
207
|
}
|
|
206
208
|
if (additionalPacks.length > 0) {
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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
|
-
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
console.log("
|
|
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",
|
package/dist/commands/policy.js
CHANGED
|
@@ -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
|
-
|
|
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(
|
|
18
|
-
|
|
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
|
-
|
|
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
|
-
|
|
44
|
+
blank();
|
|
45
|
+
success("Installed policy pack", `${pack.id} (${pack.controls.length} controls)`);
|
|
45
46
|
if (frameworksAdded.length > 0) {
|
|
46
|
-
|
|
47
|
+
success("Updated project frameworks", frameworksAdded.join(", "));
|
|
47
48
|
}
|
|
48
|
-
|
|
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
|
-
|
|
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
|
-
|
|
81
|
+
blank();
|
|
82
|
+
success("Removed policy pack", packId);
|
|
83
|
+
blank();
|
|
80
84
|
recordActivity(root, {
|
|
81
85
|
source: "cli",
|
|
82
86
|
action: "policy_remove",
|
package/dist/commands/score.js
CHANGED
|
@@ -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
|
-
|
|
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",
|
package/dist/utils/next-steps.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { select } from "./prompts.js";
|
|
2
|
-
|
|
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
|
-
|
|
69
|
-
|
|
71
|
+
divider();
|
|
72
|
+
label("What would you like to do next?");
|
|
70
73
|
const answer = await select({
|
|
71
|
-
message: "
|
|
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
|
-
|
|
82
|
-
|
|
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" });
|
package/dist/utils/prompts.d.ts
CHANGED
|
@@ -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>;
|
package/dist/utils/prompts.js
CHANGED
|
@@ -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({
|
|
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
|
-
|
|
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({
|
|
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)
|
|
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
|
-
|
|
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 };
|
package/dist/utils/ui.js
ADDED
|
@@ -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.
|
|
7
|
-
"@greenarmor/ges-cicd-generator": "1.
|
|
8
|
-
"@greenarmor/ges-compliance-engine": "1.
|
|
9
|
-
"@greenarmor/ges-core": "1.
|
|
10
|
-
"@greenarmor/ges-doc-generator": "1.
|
|
11
|
-
"@greenarmor/ges-git-hooks": "1.
|
|
12
|
-
"@greenarmor/ges-mcp-server": "1.
|
|
13
|
-
"@greenarmor/ges-policy-engine": "1.
|
|
14
|
-
"@greenarmor/ges-report-generator": "1.
|
|
15
|
-
"@greenarmor/ges-rules-engine": "1.
|
|
16
|
-
"@greenarmor/ges-scanner-integration": "1.
|
|
17
|
-
"@greenarmor/ges-scoring-engine": "1.
|
|
18
|
-
"@greenarmor/ges-web-dashboard": "1.
|
|
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.
|
|
60
|
+
"version": "1.4.1",
|
|
57
61
|
"scripts": {
|
|
58
62
|
"build": "tsc",
|
|
59
63
|
"clean": "rm -rf dist tsconfig.tsbuildinfo",
|