@greenarmor/ges
Advanced tools
| import chalk from "chalk"; | ||
| declare const DIM: import("chalk").ChalkInstance; | ||
| declare const BOLD: import("chalk").ChalkInstance; | ||
| declare const GREEN: import("chalk").ChalkInstance; | ||
| declare const RED: import("chalk").ChalkInstance; | ||
| declare const YELLOW: import("chalk").ChalkInstance; | ||
| declare const CYAN: import("chalk").ChalkInstance; | ||
| declare const MAGENTA: import("chalk").ChalkInstance; | ||
| declare const GRAY: import("chalk").ChalkInstance; | ||
| declare const icons: { | ||
| success: string; | ||
| error: string; | ||
| warn: string; | ||
| info: string; | ||
| arrow: string; | ||
| bullet: string; | ||
| check: string; | ||
| cross: string; | ||
| dash: string; | ||
| }; | ||
| export declare function banner(title: string, subtitle?: string): void; | ||
| export declare function divider(width?: number): void; | ||
| export declare function blank(): void; | ||
| export declare function success(message: string, detail?: string): void; | ||
| export declare function error(message: string, detail?: string): void; | ||
| export declare function warn(message: string, detail?: string): void; | ||
| export declare function info(message: string, detail?: string): void; | ||
| export declare function step(n: number, total: number, message: string): void; | ||
| export declare function kv(key: string, value: string, indent?: number): void; | ||
| export declare function label(text: string, color?: typeof GREEN): void; | ||
| export declare function item(text: string, value?: string): void; | ||
| export declare function group(title: string, lines: string[]): void; | ||
| export declare function progressBar(current: number, total: number, width?: number): string; | ||
| export declare function statusBadge(status: string): string; | ||
| export declare function severityBadge(severity: string): string; | ||
| export declare function gradeColor(grade: string): string; | ||
| export { chalk, icons, DIM, BOLD, GREEN, RED, YELLOW, CYAN, MAGENTA, GRAY }; |
+115
| import chalk from "chalk"; | ||
| const DIM = chalk.dim; | ||
| const BOLD = chalk.bold; | ||
| const GREEN = chalk.green; | ||
| const RED = chalk.red; | ||
| const YELLOW = chalk.yellow; | ||
| const CYAN = chalk.cyan; | ||
| const MAGENTA = chalk.magenta; | ||
| const GRAY = chalk.gray; | ||
| const icons = { | ||
| success: GREEN("✓"), | ||
| error: RED("✕"), | ||
| warn: YELLOW("!"), | ||
| info: CYAN("○"), | ||
| arrow: GRAY("→"), | ||
| bullet: GRAY("•"), | ||
| check: GREEN("✓"), | ||
| cross: RED("✕"), | ||
| dash: GRAY("—"), | ||
| }; | ||
| export function banner(title, subtitle) { | ||
| const line = "═".repeat(52); | ||
| console.log(); | ||
| console.log(CYAN(BOLD(` ${title}`))); | ||
| if (subtitle) { | ||
| console.log(DIM(` ${subtitle}`)); | ||
| } | ||
| console.log(GRAY(` ${line}`)); | ||
| console.log(); | ||
| } | ||
| export function divider(width = 52) { | ||
| console.log(GRAY(` ${"─".repeat(width)}`)); | ||
| } | ||
| export function blank() { | ||
| console.log(); | ||
| } | ||
| export function success(message, detail) { | ||
| console.log(` ${icons.success} ${GREEN(message)}${detail ? DIM(` ${detail}`) : ""}`); | ||
| } | ||
| export function error(message, detail) { | ||
| console.error(` ${icons.error} ${RED(message)}${detail ? DIM(` ${detail}`) : ""}`); | ||
| } | ||
| export function warn(message, detail) { | ||
| console.log(` ${icons.warn} ${YELLOW(message)}${detail ? DIM(` ${detail}`) : ""}`); | ||
| } | ||
| export function info(message, detail) { | ||
| console.log(` ${icons.info} ${CYAN(message)}${detail ? DIM(` ${detail}`) : ""}`); | ||
| } | ||
| export function step(n, total, message) { | ||
| const counter = DIM(`[${n}/${total}]`); | ||
| console.log(`\n ${counter} ${BOLD(message)}`); | ||
| console.log(GRAY(` ${"─".repeat(40)}`)); | ||
| } | ||
| export function kv(key, value, indent = 4) { | ||
| const pad = Math.max(key.length, 16); | ||
| console.log(`${" ".repeat(indent)}${DIM(key.padEnd(pad))} ${value}`); | ||
| } | ||
| export function label(text, color = CYAN) { | ||
| console.log(`\n ${color(BOLD(text))}`); | ||
| } | ||
| export function item(text, value) { | ||
| const v = value ? DIM(GRAY(` ${value}`)) : ""; | ||
| console.log(` ${icons.bullet} ${text}${v}`); | ||
| } | ||
| export function group(title, lines) { | ||
| console.log(`\n ${BOLD(title)}`); | ||
| for (const line of lines) { | ||
| console.log(` ${icons.bullet} ${line}`); | ||
| } | ||
| } | ||
| export function progressBar(current, total, width = 30) { | ||
| const pct = Math.round((current / total) * 100); | ||
| const filled = Math.round((current / total) * width); | ||
| const empty = width - filled; | ||
| const bar = GREEN("█".repeat(filled)) + GRAY("░".repeat(empty)); | ||
| return `${bar} ${pct}%`; | ||
| } | ||
| export function statusBadge(status) { | ||
| const badges = { | ||
| pass: GREEN("● PASS"), | ||
| fail: RED("● FAIL"), | ||
| warning: YELLOW("● WARN"), | ||
| "not-implemented": GRAY("○ N/I"), | ||
| "not-applicable": CYAN("◐ N/A"), | ||
| approved: GREEN("● APPROVED"), | ||
| rejected: RED("✕ REJECTED"), | ||
| conditional: YELLOW("◔ CONDITIONAL"), | ||
| "pending-review": YELLOW("◐ PENDING"), | ||
| draft: GRAY("○ DRAFT"), | ||
| expired: RED("⚠ EXPIRED"), | ||
| valid: GREEN("✓ VALID"), | ||
| }; | ||
| return badges[status] || GRAY(`○ ${status.toUpperCase()}`); | ||
| } | ||
| export function severityBadge(severity) { | ||
| const badges = { | ||
| critical: RED(BOLD("CRITICAL")), | ||
| high: RED("HIGH"), | ||
| medium: YELLOW("MEDIUM"), | ||
| low: CYAN("LOW"), | ||
| }; | ||
| return badges[severity] || GRAY(severity.toUpperCase()); | ||
| } | ||
| export function gradeColor(grade) { | ||
| if (grade === "A") | ||
| return GREEN(BOLD(grade)); | ||
| if (grade === "B") | ||
| return CYAN(BOLD(grade)); | ||
| if (grade === "C") | ||
| return YELLOW(BOLD(grade)); | ||
| if (grade === "D") | ||
| return MAGENTA(BOLD(grade)); | ||
| return RED(BOLD(grade)); | ||
| } | ||
| export { chalk, icons, DIM, BOLD, GREEN, RED, YELLOW, CYAN, MAGENTA, GRAY }; |
+28
-19
@@ -8,2 +8,3 @@ import { Command } from "commander"; | ||
| import { showNextStepsMenu } from "../utils/next-steps.js"; | ||
| import { banner, divider, blank, success, warn, info, severityBadge, BOLD, DIM, CYAN, RED, YELLOW, GRAY } from "../utils/ui.js"; | ||
| import * as fs from "node:fs"; | ||
@@ -19,5 +20,6 @@ import * as path from "node:path"; | ||
| const config = readJsonFile(path.join(root, ".ges", "config.json")); | ||
| console.log("\n GESF Compliance Audit"); | ||
| console.log(" ────────────────────\n"); | ||
| console.log(" Scanning project files..."); | ||
| if (!options.json) { | ||
| banner("GESF Compliance Audit", options.incremental ? "Incremental scan" : "Full project scan"); | ||
| info("Scanning project files..."); | ||
| } | ||
| let rawFindings; | ||
@@ -32,3 +34,4 @@ let scannedFiles; | ||
| scannedFiles = result.scannedFiles; | ||
| console.log(` Scanned ${scannedFiles} files (${result.changedFiles} changed)`); | ||
| if (!options.json) | ||
| success("Scan complete", `${scannedFiles} files (${result.changedFiles} changed)`); | ||
| } | ||
@@ -39,6 +42,8 @@ else { | ||
| scannedFiles = result.scannedFiles; | ||
| console.log(` Scanned ${scannedFiles} files`); | ||
| if (!options.json) | ||
| success("Scan complete", `${scannedFiles} files`); | ||
| } | ||
| const findings = deduplicateFindings(rawFindings); | ||
| console.log(""); | ||
| if (!options.json) | ||
| blank(); | ||
| const configFrameworks = (config?.frameworks || ["GDPR", "OWASP"]); | ||
@@ -83,3 +88,4 @@ const projectPacks = getPacksForProjectType(config?.project_type || "generic-web-application"); | ||
| const passCount = overrides.filter(o => o.status === "pass").length; | ||
| console.log(` Control overrides: ${naCount} not-applicable, ${passCount} pre-verified\n`); | ||
| if (!options.json) | ||
| info("Control overrides", `${naCount} not-applicable, ${passCount} pre-verified`); | ||
| } | ||
@@ -92,19 +98,19 @@ if (options.json) { | ||
| } | ||
| console.log(" ── Findings ─────────────────────\n"); | ||
| console.log(` Total findings: ${findings.length}`); | ||
| console.log(` Critical: ${critical.length} High: ${high.length} Medium: ${medium.length} Low: ${low.length}\n`); | ||
| console.log(` ${BOLD("Findings")}`); | ||
| divider(40); | ||
| console.log(` ${DIM("Total")} ${findings.length}`); | ||
| console.log(` ${RED(`Critical ${critical.length}`)} ${RED(`High ${high.length}`)} ${YELLOW(`Medium ${medium.length}`)} ${CYAN(`Low ${low.length}`)}\n`); | ||
| if (findings.length > 0) { | ||
| const grouped = groupByCategory(findings); | ||
| for (const [category, categoryFindings] of Object.entries(grouped)) { | ||
| console.log(` [${category.toUpperCase()}]`); | ||
| console.log(` ${BOLD(category.toUpperCase())}`); | ||
| for (const f of categoryFindings.slice(0, 10)) { | ||
| const sev = f.severity === "critical" ? "CRIT" : f.severity === "high" ? "HIGH" : f.severity === "medium" ? "MED " : "LOW "; | ||
| const loc = f.file !== "project" ? ` (${f.file}${f.line ? ":" + f.line : ""})` : ""; | ||
| console.log(` [${sev}] ${f.title}${loc}`); | ||
| const loc = f.file !== "project" ? ` ${DIM(`(${f.file}${f.line ? ":" + f.line : ""})`)}` : ""; | ||
| console.log(` ${severityBadge(f.severity).padEnd(10)} ${f.title}${loc}`); | ||
| if (f.evidence && f.file !== "project") { | ||
| console.log(` ${f.evidence.slice(0, 100)}`); | ||
| console.log(` ${GRAY(f.evidence.slice(0, 100))}`); | ||
| } | ||
| } | ||
| if (categoryFindings.length > 10) { | ||
| console.log(` ... and ${categoryFindings.length - 10} more`); | ||
| console.log(` ${DIM(`... and ${categoryFindings.length - 10} more`)}`); | ||
| } | ||
@@ -115,8 +121,11 @@ console.log(""); | ||
| else { | ||
| console.log(" ✓ No security or compliance issues found in source code.\n"); | ||
| success("No security or compliance issues found in source code."); | ||
| blank(); | ||
| } | ||
| console.log(" ── Compliance Score ──────────────"); | ||
| console.log(` ${BOLD("Compliance Score")}`); | ||
| divider(40); | ||
| console.log(formatScoreOutput(scoreData)); | ||
| if (critical.length > 0) { | ||
| console.log(" !! Critical issues must be resolved before deployment. !!\n"); | ||
| warn("Critical issues must be resolved before deployment."); | ||
| blank(); | ||
| } | ||
@@ -123,0 +132,0 @@ recordActivity(root, { |
@@ -6,2 +6,3 @@ import { Command } from "commander"; | ||
| import { showNextStepsMenu } from "../utils/next-steps.js"; | ||
| import { banner, success, warn, error, blank, progressBar, BOLD, DIM, GREEN, RED, YELLOW } from "../utils/ui.js"; | ||
| import * as fs from "node:fs"; | ||
@@ -12,4 +13,3 @@ import * as path from "node:path"; | ||
| .action(async () => { | ||
| console.log("\n GESF Doctor - Diagnostic Check"); | ||
| console.log(" ─────────────────────────────\n"); | ||
| banner("GESF Doctor", "Diagnostic health check"); | ||
| const checks = []; | ||
@@ -87,9 +87,21 @@ const root = findProjectRoot(); | ||
| checks.push({ name: "GESF Version", status: "OK", detail: CLI_VERSION }); | ||
| const okCount = checks.filter(c => c.status === "OK").length; | ||
| const warnCount = checks.filter(c => c.status === "WARN" || c.status === "MISSING").length; | ||
| const failCount = checks.filter(c => c.status === "FAIL").length; | ||
| console.log(` ${BOLD("Health Score")} ${progressBar(okCount, checks.length, 24)}`); | ||
| console.log(` ${DIM("Checks")} ${GREEN(`${okCount} ok`)} ${YELLOW(`${warnCount} warn`)} ${RED(`${failCount} fail`)}`); | ||
| blank(); | ||
| for (const check of checks) { | ||
| const icon = check.status === "OK" ? "✓" : check.status === "WARN" ? "!" : "✗"; | ||
| const line = ` [${icon}] ${check.name}`; | ||
| console.log(line + (check.detail ? ` - ${check.detail}` : "")); | ||
| if (check.status === "OK") { | ||
| success(check.name, check.detail); | ||
| } | ||
| else if (check.status === "WARN" || check.status === "MISSING") { | ||
| warn(check.name, check.detail); | ||
| } | ||
| else { | ||
| error(check.name, check.detail); | ||
| } | ||
| } | ||
| console.log(""); | ||
| blank(); | ||
| await showNextStepsMenu("doctor"); | ||
| }); |
| import { Command } from "commander"; | ||
| import { ensureGESInitialized } from "../utils/project.js"; | ||
| import { input, select } from "../utils/prompts.js"; | ||
| import { banner, divider, blank, success, info, kv, statusBadge, severityBadge, BOLD, DIM, GREEN, RED, YELLOW, GRAY, } from "../utils/ui.js"; | ||
| import { loadGovernanceRecords, createGovernanceRecord, addGovernanceRecord, findGovernanceRecord, setGovernanceApproval, addGovernanceEvidence, createEvidenceRef, verifyGovernanceRecord, deleteGovernanceRecord, setGovernanceRiskAssessment, setGovernancePolicyBasis, setGovernanceReviewCycle, setGovernanceDataInventory, setGovernanceComplianceLinks, setGovernanceCommittee, } from "@greenarmor/ges-core"; | ||
@@ -22,13 +23,13 @@ import { recordActivity } from "@greenarmor/ges-core"; | ||
| function printRecordSummary(record) { | ||
| const badge = STATUS_BADGE[record.status] || "?"; | ||
| console.log(` ${badge} ${record.id} ${record.system_name}`); | ||
| console.log(` Type: ${record.system_type} | Risk: ${RISK_COLOR[record.risk_level] || record.risk_level} | Status: ${record.status}`); | ||
| console.log(` ${statusBadge(record.status)} ${BOLD(record.system_name)}`); | ||
| console.log(` ${DIM("ID")} ${record.id}`); | ||
| console.log(` ${DIM("Type")} ${record.system_type} ${GRAY("|")} ${DIM("Risk")} ${severityBadge(record.risk_level)}`); | ||
| if (record.approval) { | ||
| console.log(` Approved by: ${record.approval.approver_name} (${record.approval.approver_role})`); | ||
| console.log(` Valid: ${record.approval.valid_from} → ${record.approval.valid_until || "indefinite"}`); | ||
| console.log(` ${DIM("By")} ${record.approval.approver_name} (${record.approval.approver_role})`); | ||
| console.log(` ${DIM("Valid")} ${record.approval.valid_from} ${GRAY("→")} ${record.approval.valid_until || "indefinite"}`); | ||
| } | ||
| else { | ||
| console.log(` Approval: NOT RECORDED`); | ||
| console.log(` ${DIM("By")} ${GRAY("NOT RECORDED")}`); | ||
| } | ||
| console.log(` Evidence: ${record.evidence.length} reference(s)`); | ||
| console.log(` ${DIM("Ev")} ${record.evidence.length} reference(s)`); | ||
| } | ||
@@ -82,9 +83,11 @@ export const governanceCommand = new Command("governance") | ||
| addGovernanceRecord(root, record); | ||
| console.log(`\n [✓] Governance record created`); | ||
| console.log(` ID: ${record.id}`); | ||
| blank(); | ||
| success("Governance record created"); | ||
| kv("ID", record.id, 6); | ||
| console.log(); | ||
| printRecordSummary(record); | ||
| console.log(`\n Next steps:`); | ||
| console.log(` ges governance approve ${record.id} — Record approval decision`); | ||
| console.log(` ges governance evidence ${record.id} — Add evidence reference`); | ||
| console.log(` ges governance verify ${record.id} — Verify provenance chain\n`); | ||
| console.log(`\n ${DIM("Next steps:")}`); | ||
| console.log(` ${GRAY("–")} ${GREEN("ges governance approve")} ${record.id} ${DIM("Record approval decision")}`); | ||
| console.log(` ${GRAY("–")} ${GREEN("ges governance evidence")} ${record.id} ${DIM("Add evidence reference")}`); | ||
| console.log(` ${GRAY("–")} ${GREEN("ges governance verify")} ${record.id} ${DIM("Verify provenance chain")}\n`); | ||
| recordActivity(root, { | ||
@@ -153,6 +156,8 @@ source: "cli", | ||
| } | ||
| console.log(`\n [✓] Approval recorded for ${updated.system_name}`); | ||
| console.log(` Decision: ${decision.toUpperCase()}`); | ||
| console.log(` Approver: ${approverName} (${approverRole})`); | ||
| console.log(` Valid: ${validFrom} → ${validUntil || "indefinite"}\n`); | ||
| blank(); | ||
| success("Approval recorded", `for ${updated.system_name}`); | ||
| kv("Decision", decision.toUpperCase(), 6); | ||
| kv("Approver", `${approverName} (${approverRole})`, 6); | ||
| kv("Valid", `${validFrom} → ${validUntil || "indefinite"}`, 6); | ||
| console.log(); | ||
| recordActivity(root, { | ||
@@ -229,6 +234,9 @@ source: "cli", | ||
| } | ||
| console.log(`\n [✓] Evidence added to ${updated.system_name}`); | ||
| console.log(` ${evidence.title}`); | ||
| console.log(` Source: ${evidence.source_system} | Ref: ${evidence.reference}`); | ||
| console.log(` Total evidence: ${updated.evidence.length} reference(s)\n`); | ||
| blank(); | ||
| success("Evidence added", `to ${updated.system_name}`); | ||
| console.log(` ${BOLD(evidence.title)}`); | ||
| kv("Source", evidence.source_system, 6); | ||
| kv("Ref", evidence.reference, 6); | ||
| kv("Total", `${updated.evidence.length} reference(s)`, 6); | ||
| console.log(); | ||
| recordActivity(root, { | ||
@@ -250,10 +258,12 @@ source: "cli", | ||
| if (records.length === 0) { | ||
| console.log(`\n No governance records found.`); | ||
| console.log(` Create one with: ges governance add\n`); | ||
| info("No governance records found."); | ||
| console.log(` ${DIM("Create one with:")} ${GREEN("ges governance add")}\n`); | ||
| return; | ||
| } | ||
| console.log(`\n Governance Records (${records.length}):\n`); | ||
| blank(); | ||
| console.log(` ${BOLD("Governance Records")} ${GRAY(`(${records.length})`)}`); | ||
| console.log(); | ||
| records.forEach(r => { | ||
| printRecordSummary(r); | ||
| console.log(""); | ||
| console.log(); | ||
| }); | ||
@@ -419,30 +429,39 @@ })) | ||
| const result = verifyGovernanceRecord(record); | ||
| console.log(`\n ═══════════════════════════════════════════════════`); | ||
| console.log(` VERIFICATION: ${record.system_name}`); | ||
| console.log(` ═══════════════════════════════════════════════════\n`); | ||
| console.log(` Overall: ${result.valid ? "✓ VALID" : "✕ ISSUES FOUND"}`); | ||
| console.log(` Approval Status: ${result.approval_status.toUpperCase()}`); | ||
| banner("VERIFICATION", record.system_name); | ||
| const overallText = result.valid ? GREEN(BOLD("✓ VALID")) : RED(BOLD("✕ ISSUES FOUND")); | ||
| console.log(` ${DIM("Overall:")} ${overallText}`); | ||
| console.log(` ${DIM("Approval:")} ${statusBadge(result.approval_status)}`); | ||
| if (result.days_until_expiry !== null) { | ||
| const dayLabel = result.days_until_expiry < 0 ? `${Math.abs(result.days_until_expiry)} days AGO` : `${result.days_until_expiry} days remaining`; | ||
| console.log(` Expiry: ${dayLabel}`); | ||
| const dayLabel = result.days_until_expiry < 0 | ||
| ? RED(`${Math.abs(result.days_until_expiry)} days AGO`) | ||
| : result.days_until_expiry <= 30 | ||
| ? YELLOW(`${result.days_until_expiry} days remaining`) | ||
| : GREEN(`${result.days_until_expiry} days remaining`); | ||
| console.log(` ${DIM("Expiry:")} ${dayLabel}`); | ||
| } | ||
| console.log(` Evidence Count: ${result.completeness.evidence_count}`); | ||
| console.log(`\n Completeness Checklist:`); | ||
| console.log(` ${result.completeness.has_approval ? "✓" : "✕"} Approval Decision`); | ||
| console.log(` ${result.completeness.has_risk_assessment ? "✓" : "✕"} Risk Assessment`); | ||
| console.log(` ${result.completeness.has_policy_basis ? "✓" : "✕"} Policy Basis`); | ||
| console.log(` ${result.completeness.has_evidence ? "✓" : "✕"} Evidence Chain`); | ||
| console.log(` ${result.completeness.has_review_cycle ? "✓" : "△"} Review Cycle`); | ||
| console.log(` ${result.completeness.has_data_inventory ? "✓" : "△"} Data Inventory`); | ||
| console.log(` ${result.completeness.has_compliance_links ? "✓" : "△"} Compliance Links`); | ||
| console.log(` ${result.completeness.is_current ? "✓" : "✕"} Currently Valid`); | ||
| console.log(` ${DIM("Evidence:")} ${result.completeness.evidence_count} reference(s)`); | ||
| console.log(`\n ${BOLD("Completeness Checklist")}`); | ||
| divider(40); | ||
| const check = (ok, label, isWarning = false) => { | ||
| const icon = ok ? GREEN("✓") : isWarning ? YELLOW("△") : RED("✕"); | ||
| const text = ok ? label : isWarning ? YELLOW(label) : RED(label); | ||
| console.log(` ${icon} ${text}`); | ||
| }; | ||
| check(result.completeness.has_approval, "Approval Decision"); | ||
| check(result.completeness.has_risk_assessment, "Risk Assessment"); | ||
| check(result.completeness.has_policy_basis, "Policy Basis"); | ||
| check(result.completeness.has_evidence, "Evidence Chain"); | ||
| check(result.completeness.has_review_cycle, "Review Cycle", true); | ||
| check(result.completeness.has_data_inventory, "Data Inventory", true); | ||
| check(result.completeness.has_compliance_links, "Compliance Links", true); | ||
| check(result.completeness.is_current, "Currently Valid"); | ||
| if (result.issues.length > 0) { | ||
| console.log(`\n BLOCKING ISSUES:`); | ||
| result.issues.forEach(i => console.log(` ✕ ${i}`)); | ||
| console.log(`\n ${RED(BOLD("BLOCKING ISSUES"))}`); | ||
| result.issues.forEach(i => console.log(` ${RED("✕")} ${i}`)); | ||
| } | ||
| if (result.warnings.length > 0) { | ||
| console.log(`\n WARNINGS:`); | ||
| result.warnings.forEach(w => console.log(` △ ${w}`)); | ||
| console.log(`\n ${YELLOW(BOLD("WARNINGS"))}`); | ||
| result.warnings.forEach(w => console.log(` ${YELLOW("△")} ${w}`)); | ||
| } | ||
| console.log(""); | ||
| console.log(); | ||
| })) | ||
@@ -463,3 +482,5 @@ .addCommand(new Command("delete") | ||
| if (deleted) { | ||
| console.log(`\n [✓] Deleted governance record: ${record.system_name} (${record.id})\n`); | ||
| blank(); | ||
| success("Deleted governance record", `${record.system_name} (${record.id})`); | ||
| console.log(); | ||
| recordActivity(root, { | ||
@@ -517,4 +538,8 @@ source: "cli", | ||
| } | ||
| console.log(`\n [✓] Risk assessment linked to ${updated.system_name}`); | ||
| console.log(` Assessor: ${assessor} | Score: ${score} | Residual: ${residual}\n`); | ||
| blank(); | ||
| success("Risk assessment linked", `to ${updated.system_name}`); | ||
| kv("Assessor", assessor, 6); | ||
| kv("Score", score, 6); | ||
| kv("Residual", residual, 6); | ||
| console.log(); | ||
| 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 }); | ||
@@ -555,4 +580,7 @@ })) | ||
| } | ||
| console.log(`\n [✓] Policy basis documented for ${updated.system_name}`); | ||
| console.log(` ${policyName} (${policyId} v${version}) — ${standard}\n`); | ||
| blank(); | ||
| success("Policy basis documented", `for ${updated.system_name}`); | ||
| kv("Policy", `${policyName} (${policyId} v${version})`, 6); | ||
| kv("Standard", standard, 6); | ||
| console.log(); | ||
| 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 }); | ||
@@ -595,4 +623,7 @@ })) | ||
| } | ||
| console.log(`\n [✓] Review cycle set for ${updated.system_name}`); | ||
| console.log(` Frequency: ${frequency} | Next review: ${nextReview}\n`); | ||
| blank(); | ||
| success("Review cycle set", `for ${updated.system_name}`); | ||
| kv("Frequency", frequency, 6); | ||
| kv("Next review", nextReview, 6); | ||
| console.log(); | ||
| 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 }); | ||
@@ -631,3 +662,5 @@ })) | ||
| } | ||
| console.log(`\n [✓] Data inventory documented for ${updated.system_name}\n`); | ||
| blank(); | ||
| success("Data inventory documented", `for ${updated.system_name}`); | ||
| console.log(); | ||
| 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 }); | ||
@@ -667,4 +700,7 @@ })) | ||
| } | ||
| console.log(`\n [✓] Committee approval recorded for ${updated.system_name}`); | ||
| console.log(` ${committeeName} — ${meetingDate} (${meetingRef})\n`); | ||
| blank(); | ||
| success("Committee approval recorded", `for ${updated.system_name}`); | ||
| kv("Committee", committeeName, 6); | ||
| kv("Meeting", `${meetingDate} (${meetingRef})`, 6); | ||
| console.log(); | ||
| 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 }); | ||
@@ -698,4 +734,6 @@ })) | ||
| } | ||
| console.log(`\n [✓] Compliance links mapped for ${updated.system_name}\n`); | ||
| blank(); | ||
| success("Compliance links mapped", `for ${updated.system_name}`); | ||
| console.log(); | ||
| 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 }); | ||
| })); |
+33
-25
| import { Command } from "commander"; | ||
| import { input, select, checkbox } from "../utils/prompts.js"; | ||
| import { banner, divider, blank, success, error, warn, info, step, kv, label, BOLD, CYAN, GREEN, GRAY, } from "../utils/ui.js"; | ||
| 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"; | ||
@@ -21,12 +22,11 @@ import { CLI_VERSION } from "../utils/version.js"; | ||
| .action(async (options) => { | ||
| console.log("\n Green Engineering Standard Framework (GESF) v" + CLI_VERSION); | ||
| console.log(" ─────────────────────────────────────────────\n"); | ||
| banner(`Green Engineering Standard Framework`, `v${CLI_VERSION}`); | ||
| const gesDir = path.join(process.cwd(), GES_DIR); | ||
| if (fs.existsSync(gesDir)) { | ||
| if (!options.force) { | ||
| console.error(" Error: GESF is already initialized in this project."); | ||
| console.error(" Use 'ges init --force' to re-initialize.\n"); | ||
| error("GESF is already initialized in this project."); | ||
| info("Use", "ges init --force to re-initialize.\n"); | ||
| process.exit(1); | ||
| } | ||
| console.log(" ⚠ Re-initializing GESF (existing files will be overwritten)...\n"); | ||
| warn("Re-initializing GESF", "existing files will be overwritten\n"); | ||
| fs.rmSync(gesDir, { recursive: true, force: true }); | ||
@@ -52,3 +52,3 @@ } | ||
| if (selectedFrameworks.length === 0) { | ||
| console.error(" Error: At least one framework must be selected."); | ||
| error("At least one framework must be selected."); | ||
| process.exit(1); | ||
@@ -85,4 +85,4 @@ } | ||
| if (options.country && !countryInfo && countryCode !== "EU") { | ||
| console.warn(` ⚠ Country code '${options.country}' not recognized. No privacy pack will be auto-installed.`); | ||
| console.warn(` Available codes: ${PRIVACY_COUNTRIES.map(c => c.code).join(", ")}, EU`); | ||
| warn(`Country code '${options.country}' not recognized.`, "No privacy pack will be auto-installed."); | ||
| info("Available codes:", `${PRIVACY_COUNTRIES.map(c => c.code).join(", ")}, EU`); | ||
| } | ||
@@ -199,27 +199,35 @@ // --- Optional: Additional privacy packs --- | ||
| } | ||
| console.log(" ✓ Project structure created"); | ||
| console.log(" ✓ Configuration files generated"); | ||
| console.log(" ✓ Compliance documents created"); | ||
| console.log(" ✓ Security documents created"); | ||
| blank(); | ||
| step(1, 4, "Creating project structure"); | ||
| success("Project structure created"); | ||
| success("Configuration files generated"); | ||
| success("Compliance documents created"); | ||
| success("Security documents created"); | ||
| if (countryInfo) { | ||
| console.log(` ✓ Country privacy pack auto-installed: ${countryInfo.packId} (${countryInfo.name})`); | ||
| success("Country privacy pack auto-installed", `${countryInfo.packId} (${countryInfo.name})`); | ||
| } | ||
| else if (countryCode === "EU") { | ||
| console.log(" ✓ EU GDPR privacy pack auto-installed"); | ||
| success("EU GDPR privacy pack auto-installed"); | ||
| } | ||
| if (additionalPacks.length > 0) { | ||
| console.log(` ✓ Additional privacy packs installed: ${additionalPacks.join(", ")}`); | ||
| success("Additional privacy packs installed", additionalPacks.join(", ")); | ||
| } | ||
| console.log(" ✓ Control packs installed:", packs.map(p => p.id).join(", ")); | ||
| console.log(" ✓ GitHub Actions workflows generated"); | ||
| console.log(" ✓ Developer logs directory created (.dev-logs/)"); | ||
| console.log(`\n GESF initialized for "${projectName}" (${projectType})`); | ||
| success("Control packs installed", packs.map(p => p.id).join(", ")); | ||
| success("GitHub Actions workflows generated"); | ||
| success("Developer logs directory created", ".dev-logs/"); | ||
| blank(); | ||
| step(2, 4, "Project summary"); | ||
| blank(); | ||
| console.log(` ${CYAN(BOLD("GESF initialized"))} for "${projectName}" (${projectType})`); | ||
| if (countryInfo) { | ||
| console.log(` Country: ${countryInfo.name} — ${countryInfo.lawName}`); | ||
| kv("Country", `${countryInfo.name} — ${countryInfo.lawName}`); | ||
| } | ||
| console.log(" Next steps:"); | ||
| console.log(" 1. Review generated compliance documents"); | ||
| console.log(" 2. Run 'ges audit' to evaluate your project"); | ||
| console.log(" 3. Run 'ges score' to see your compliance score"); | ||
| console.log(" 4. Add more packs with 'ges policy install <pack-id>'\n"); | ||
| divider(40); | ||
| blank(); | ||
| step(3, 4, "Next steps"); | ||
| label("Quick start:"); | ||
| console.log(` ${GRAY("1.")} Review generated compliance documents`); | ||
| console.log(` ${GRAY("2.")} Run ${GREEN("ges audit")} to evaluate your project`); | ||
| console.log(` ${GRAY("3.")} Run ${GREEN("ges score")} to see your compliance score`); | ||
| console.log(` ${GRAY("4.")} Add more packs with ${GREEN("ges policy install <pack-id>")}`); | ||
| recordActivity(process.cwd(), { | ||
@@ -226,0 +234,0 @@ source: "cli", |
+14
-10
@@ -6,2 +6,3 @@ import { Command } from "commander"; | ||
| import { showNextStepsMenu } from "../utils/next-steps.js"; | ||
| import { banner, blank, success, error, BOLD, CYAN, DIM, GRAY } from "../utils/ui.js"; | ||
| import * as fs from "node:fs"; | ||
@@ -15,8 +16,7 @@ import * as path from "node:path"; | ||
| .action(async () => { | ||
| console.log("\n Available Policy Packs:\n"); | ||
| banner("Policy Packs", "Available compliance control packs"); | ||
| const packs = getAllPacks(); | ||
| for (const pack of packs) { | ||
| console.log(` ${pack.id.padEnd(15)} ${pack.name}`); | ||
| const indent = " "; | ||
| console.log(` ${indent} ${pack.controls.length} controls | ${pack.project_types.join(", ")}`); | ||
| console.log(` ${CYAN(BOLD(pack.id.padEnd(18)))} ${pack.name}`); | ||
| console.log(` ${DIM(`${pack.controls.length} controls`)} ${GRAY("|")} ${DIM(pack.project_types.join(", "))}`); | ||
| console.log(""); | ||
@@ -34,3 +34,3 @@ } | ||
| if (!pack) { | ||
| console.error(` Error: Pack '${packId}' not found. Available: ${listPackIds().join(", ")}`); | ||
| error(`Pack '${packId}' not found.`, `Available: ${listPackIds().join(", ")}`); | ||
| process.exit(1); | ||
@@ -47,7 +47,9 @@ } | ||
| } | ||
| console.log(`\n ✓ Installed policy pack: ${pack.id} (${pack.controls.length} controls)`); | ||
| blank(); | ||
| success("Installed policy pack", `${pack.id} (${pack.controls.length} controls)`); | ||
| if (frameworksAdded.length > 0) { | ||
| console.log(` ✓ Added ${frameworksAdded.join(", ")} to project frameworks in .ges/config.json`); | ||
| success("Updated project frameworks", frameworksAdded.join(", ")); | ||
| } | ||
| console.log(" ✓ Dashboard will now reflect this pack's controls\n"); | ||
| success("Dashboard will reflect this pack's controls"); | ||
| blank(); | ||
| recordActivity(root, { | ||
@@ -69,3 +71,3 @@ source: "cli", | ||
| if (!fs.existsSync(packDir)) { | ||
| console.error(` Error: Pack '${packId}' is not installed.`); | ||
| error(`Pack '${packId}' is not installed.`); | ||
| process.exit(1); | ||
@@ -84,3 +86,5 @@ } | ||
| } | ||
| console.log(`\n ✓ Removed policy pack: ${packId}\n`); | ||
| blank(); | ||
| success("Removed policy pack", packId); | ||
| blank(); | ||
| recordActivity(root, { | ||
@@ -87,0 +91,0 @@ source: "cli", |
@@ -6,2 +6,3 @@ import { Command } from "commander"; | ||
| import { showNextStepsMenu } from "../utils/next-steps.js"; | ||
| import { warn, info, blank, DIM } from "../utils/ui.js"; | ||
| import * as path from "node:path"; | ||
@@ -16,3 +17,6 @@ export const scoreCommand = new Command("score") | ||
| if (!score || !score.frameworks || Object.keys(score.frameworks).length === 0) { | ||
| console.log("\n No compliance score available. Run 'ges audit' first.\n"); | ||
| blank(); | ||
| warn("No compliance score available."); | ||
| info("Run", "ges audit first."); | ||
| blank(); | ||
| await showNextStepsMenu("score"); | ||
@@ -26,3 +30,3 @@ return; | ||
| console.log(formatScoreOutput(score)); | ||
| console.log(` Last evaluated: ${score.evaluated_at}\n`); | ||
| console.log(` ${DIM("Last evaluated:")} ${score.evaluated_at}\n`); | ||
| } | ||
@@ -29,0 +33,0 @@ recordActivity(root, { |
| import { select } from "./prompts.js"; | ||
| const DIVIDER = " ─────────────────────────────────────────────"; | ||
| import { divider, blank, label, info, GREEN } from "./ui.js"; | ||
| function isInteractive() { | ||
@@ -18,2 +18,3 @@ return process.stdin.isTTY === true && process.stdout.isTTY === true; | ||
| update: { label: "Check updates", value: "ges update", description: "Check for GESF updates" }, | ||
| governance: { label: "Governance", value: "ges governance list", description: "View approval provenance chains" }, | ||
| }; | ||
@@ -59,2 +60,4 @@ function buildSteps(exclude) { | ||
| return buildSteps(["update"]); | ||
| case "governance": | ||
| return buildSteps(["governance"]); | ||
| case "mcp-setup": | ||
@@ -70,6 +73,6 @@ return buildSteps(["mcp-setup"]); | ||
| const steps = getNextStepsForCommand(command, context); | ||
| console.log(DIVIDER); | ||
| console.log(" What would you like to do next?\n"); | ||
| divider(); | ||
| label("What would you like to do next?"); | ||
| const answer = await select({ | ||
| message: "Select next action:", | ||
| message: "Choose your next action:", | ||
| choices: steps.map(step => ({ | ||
@@ -84,4 +87,6 @@ name: step.description ? `${step.label} — ${step.description}` : step.label, | ||
| } | ||
| console.log(`\n Running: ${answer}\n`); | ||
| console.log(DIVIDER + "\n"); | ||
| blank(); | ||
| info("Running", GREEN(answer)); | ||
| divider(); | ||
| blank(); | ||
| const { execSync } = await import("node:child_process"); | ||
@@ -88,0 +93,0 @@ try { |
@@ -10,3 +10,5 @@ export declare function input(options: { | ||
| value: T; | ||
| description?: string; | ||
| }[]; | ||
| pageSize?: number; | ||
| }): Promise<T>; | ||
@@ -19,3 +21,9 @@ export declare function checkbox<T = string>(options: { | ||
| checked?: boolean; | ||
| description?: string; | ||
| }[]; | ||
| pageSize?: number; | ||
| }): Promise<T[]>; | ||
| export declare function confirm(options: { | ||
| message: string; | ||
| default?: boolean; | ||
| }): Promise<boolean>; |
+62
-11
| import * as readline from "node:readline"; | ||
| import { chalk, DIM, GREEN, CYAN, GRAY } from "./ui.js"; | ||
| function isInteractive() { | ||
@@ -21,2 +22,13 @@ return process.stdin.isTTY === true && process.stdout.isTTY === true; | ||
| } | ||
| const selectTheme = { | ||
| prefix: { idle: chalk.gray("?"), done: chalk.green("✓") }, | ||
| helpMode: "always", | ||
| }; | ||
| const checkboxTheme = { | ||
| prefix: { idle: chalk.gray("?"), done: chalk.green("✓") }, | ||
| helpMode: "always", | ||
| }; | ||
| const inputTheme = { | ||
| prefix: { idle: chalk.gray("?"), done: chalk.green("✓") }, | ||
| }; | ||
| export async function input(options) { | ||
@@ -31,5 +43,5 @@ if (!isInteractive()) { | ||
| const rl = createRL(); | ||
| const suffix = options.default ? ` (${options.default})` : ""; | ||
| const suffix = options.default ? DIM(` (${options.default})`) : ""; | ||
| return new Promise((resolve) => { | ||
| rl.question(` ${options.message}${suffix}: `, (answer) => { | ||
| rl.question(` ${GRAY("?")} ${options.message}${suffix}${GRAY(":")} `, (answer) => { | ||
| rl.close(); | ||
@@ -46,11 +58,17 @@ resolve(answer.trim() || options.default || ""); | ||
| if (inquirer) { | ||
| return inquirer.select({ message: options.message, choices: options.choices }); | ||
| return inquirer.select({ | ||
| message: options.message, | ||
| choices: options.choices, | ||
| theme: selectTheme, | ||
| pageSize: options.pageSize ?? 10, | ||
| }); | ||
| } | ||
| console.log(`\n ${options.message}:\n`); | ||
| console.log(`\n ${CYAN(options.message)}:\n`); | ||
| options.choices.forEach((c, i) => { | ||
| console.log(` ${i + 1}) ${c.name}`); | ||
| const num = GRAY(`${String(i + 1).padStart(2)}.`); | ||
| console.log(` ${num} ${c.name}`); | ||
| }); | ||
| const rl = createRL(); | ||
| return new Promise((resolve) => { | ||
| rl.question(`\n Enter choice [1-${options.choices.length}]: `, (answer) => { | ||
| rl.question(`\n ${GRAY("Enter choice")} [1-${options.choices.length}]: `, (answer) => { | ||
| rl.close(); | ||
@@ -73,12 +91,18 @@ const num = parseInt(answer.trim(), 10); | ||
| if (inquirer) { | ||
| return inquirer.checkbox({ message: options.message, choices: options.choices }); | ||
| return inquirer.checkbox({ | ||
| message: options.message, | ||
| choices: options.choices, | ||
| theme: checkboxTheme, | ||
| pageSize: options.pageSize ?? 10, | ||
| }); | ||
| } | ||
| console.log(`\n ${options.message} (comma-separated numbers):\n`); | ||
| console.log(`\n ${CYAN(options.message)} ${GRAY("(comma-separated numbers)")}\n`); | ||
| options.choices.forEach((c, i) => { | ||
| const marker = c.checked ? "[x]" : "[ ]"; | ||
| console.log(` ${marker} ${i + 1}) ${c.name}`); | ||
| const marker = c.checked ? GREEN("[x]") : GRAY("[ ]"); | ||
| const num = `${String(i + 1).padStart(2)}.`; | ||
| console.log(` ${marker} ${GRAY(num)} ${c.name}`); | ||
| }); | ||
| const rl = createRL(); | ||
| return new Promise((resolve) => { | ||
| rl.question(`\n Enter choices [1-${options.choices.length}]: `, (answer) => { | ||
| rl.question(`\n ${GRAY("Enter choices")} [1-${options.choices.length}]: `, (answer) => { | ||
| rl.close(); | ||
@@ -99,1 +123,28 @@ const trimmed = answer.trim(); | ||
| } | ||
| export async function confirm(options) { | ||
| if (!isInteractive()) { | ||
| return options.default ?? false; | ||
| } | ||
| const inquirer = await getInquirer(); | ||
| if (inquirer) { | ||
| return inquirer.confirm({ | ||
| message: options.message, | ||
| default: options.default, | ||
| theme: inputTheme, | ||
| }); | ||
| } | ||
| const suffix = options.default !== undefined ? ` (${options.default ? "Y/n" : "y/N"})` : " (y/n)"; | ||
| const rl = createRL(); | ||
| return new Promise((resolve) => { | ||
| rl.question(` ${GRAY("?")} ${options.message}${suffix}: `, (answer) => { | ||
| rl.close(); | ||
| const trimmed = answer.trim().toLowerCase(); | ||
| if (!trimmed) { | ||
| resolve(options.default ?? false); | ||
| } | ||
| else { | ||
| resolve(trimmed === "y" || trimmed === "yes"); | ||
| } | ||
| }); | ||
| }); | ||
| } |
+18
-14
@@ -6,15 +6,16 @@ { | ||
| "dependencies": { | ||
| "@greenarmor/ges-audit-engine": "1.4.0", | ||
| "@greenarmor/ges-cicd-generator": "1.4.0", | ||
| "@greenarmor/ges-compliance-engine": "1.4.0", | ||
| "@greenarmor/ges-core": "1.4.0", | ||
| "@greenarmor/ges-doc-generator": "1.4.0", | ||
| "@greenarmor/ges-git-hooks": "1.4.0", | ||
| "@greenarmor/ges-mcp-server": "1.4.0", | ||
| "@greenarmor/ges-policy-engine": "1.4.0", | ||
| "@greenarmor/ges-report-generator": "1.4.0", | ||
| "@greenarmor/ges-rules-engine": "1.4.0", | ||
| "@greenarmor/ges-scanner-integration": "1.4.0", | ||
| "@greenarmor/ges-scoring-engine": "1.4.0", | ||
| "@greenarmor/ges-web-dashboard": "1.4.0", | ||
| "@greenarmor/ges-audit-engine": "1.4.1", | ||
| "@greenarmor/ges-cicd-generator": "1.4.1", | ||
| "@greenarmor/ges-compliance-engine": "1.4.1", | ||
| "@greenarmor/ges-core": "1.4.1", | ||
| "@greenarmor/ges-doc-generator": "1.4.1", | ||
| "@greenarmor/ges-git-hooks": "1.4.1", | ||
| "@greenarmor/ges-mcp-server": "1.4.1", | ||
| "@greenarmor/ges-policy-engine": "1.4.1", | ||
| "@greenarmor/ges-report-generator": "1.4.1", | ||
| "@greenarmor/ges-rules-engine": "1.4.1", | ||
| "@greenarmor/ges-scanner-integration": "1.4.1", | ||
| "@greenarmor/ges-scoring-engine": "1.4.1", | ||
| "@greenarmor/ges-web-dashboard": "1.4.1", | ||
| "chalk": "^5.6.2", | ||
| "commander": "^13.0.0" | ||
@@ -28,2 +29,5 @@ }, | ||
| }, | ||
| "optionalDependencies": { | ||
| "@inquirer/prompts": "^7.10.1" | ||
| }, | ||
| "engines": { | ||
@@ -58,3 +62,3 @@ "node": ">=20.0.0" | ||
| "types": "./dist/index.d.ts", | ||
| "version": "1.4.0", | ||
| "version": "1.4.1", | ||
| "scripts": { | ||
@@ -61,0 +65,0 @@ "build": "tsc", |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
135178
7.12%55
3.77%2973
10.85%16
14.29%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
Updated