@aigrc/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/aigrc.js ADDED
@@ -0,0 +1,2372 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/bin/aigrc.ts
4
+ import { program } from "commander";
5
+
6
+ // src/commands/scan.ts
7
+ import { Command } from "commander";
8
+ import chalk2 from "chalk";
9
+ import ora from "ora";
10
+ import path from "path";
11
+ import {
12
+ scan,
13
+ suggestAssetCard,
14
+ initializePatterns
15
+ } from "@aigrc/core";
16
+
17
+ // src/utils/output.ts
18
+ import chalk from "chalk";
19
+ function printHeader() {
20
+ console.log();
21
+ console.log(chalk.cyan.bold("AIGRC") + chalk.dim(" - AI Governance, Risk, Compliance"));
22
+ console.log(chalk.dim("\u2500".repeat(50)));
23
+ console.log();
24
+ }
25
+ function printSubheader(text) {
26
+ console.log();
27
+ console.log(chalk.bold.cyan(`\u25B8 ${text}`));
28
+ console.log(chalk.cyan("\u2500".repeat(40)));
29
+ }
30
+ function printSuccess(text) {
31
+ console.log(chalk.green(`\u2713 ${text}`));
32
+ }
33
+ function printWarning(text) {
34
+ console.log(chalk.yellow(`\u26A0 ${text}`));
35
+ }
36
+ function printError(text) {
37
+ console.log(chalk.red(`\u2717 ${text}`));
38
+ }
39
+ function printInfo(text) {
40
+ console.log(chalk.gray(`\u2139 ${text}`));
41
+ }
42
+ function printScanSummary(result) {
43
+ const { summary, scannedFiles, skippedFiles, duration } = result;
44
+ printSubheader("Scan Summary");
45
+ console.log(` Files scanned: ${chalk.bold(scannedFiles)}`);
46
+ if (skippedFiles > 0) {
47
+ console.log(` Files skipped: ${chalk.yellow(skippedFiles)}`);
48
+ }
49
+ console.log(` Duration: ${chalk.gray(`${duration}ms`)}`);
50
+ console.log();
51
+ if (summary.totalDetections === 0) {
52
+ printInfo("No AI frameworks or models detected.");
53
+ return;
54
+ }
55
+ console.log(` ${chalk.bold("Total detections:")} ${summary.totalDetections}`);
56
+ console.log();
57
+ console.log(chalk.dim(" By confidence:"));
58
+ if (summary.byConfidence.high > 0) {
59
+ console.log(` ${confidenceLabel("high")}: ${summary.byConfidence.high}`);
60
+ }
61
+ if (summary.byConfidence.medium > 0) {
62
+ console.log(` ${confidenceLabel("medium")}: ${summary.byConfidence.medium}`);
63
+ }
64
+ if (summary.byConfidence.low > 0) {
65
+ console.log(` ${confidenceLabel("low")}: ${summary.byConfidence.low}`);
66
+ }
67
+ console.log();
68
+ if (Object.keys(summary.byFramework).length > 0) {
69
+ console.log(chalk.dim(" By framework:"));
70
+ for (const [framework, count] of Object.entries(summary.byFramework)) {
71
+ console.log(` ${chalk.bold(framework)}: ${count}`);
72
+ }
73
+ console.log();
74
+ }
75
+ if (summary.primaryFramework) {
76
+ console.log(
77
+ ` ${chalk.bold("Primary framework:")} ${chalk.cyan(summary.primaryFramework)}`
78
+ );
79
+ }
80
+ if (summary.primaryCategory) {
81
+ console.log(
82
+ ` ${chalk.bold("Primary category:")} ${formatCategory(summary.primaryCategory)}`
83
+ );
84
+ }
85
+ }
86
+ function printDetections(detections, verbose = false, limit = 20) {
87
+ if (detections.length === 0) {
88
+ return;
89
+ }
90
+ printSubheader(`Detections (${detections.length} total)`);
91
+ const toShow = detections.slice(0, limit);
92
+ for (const detection of toShow) {
93
+ const confidence = confidenceLabel(detection.confidence);
94
+ const location = chalk.gray(`${detection.filePath}:${detection.line}`);
95
+ const framework = chalk.bold(detection.framework);
96
+ console.log(` ${confidence} ${framework}`);
97
+ console.log(` ${location}`);
98
+ console.log(` ${chalk.dim(truncate(detection.match, 60))}`);
99
+ if (verbose) {
100
+ console.log(` ${chalk.dim(`Strategy: ${detection.strategy}`)}`);
101
+ if (detection.implies.length > 0) {
102
+ console.log(
103
+ ` ${chalk.dim(`Implies: ${detection.implies.map((i) => i.factor).join(", ")}`)}`
104
+ );
105
+ }
106
+ }
107
+ console.log();
108
+ }
109
+ if (detections.length > limit) {
110
+ printInfo(`... and ${detections.length - limit} more detections`);
111
+ }
112
+ }
113
+ function printRiskFactors(riskFactors) {
114
+ printSubheader("Inferred Risk Factors");
115
+ const factors = [
116
+ { key: "autonomousDecisions", label: "Autonomous Decisions", value: riskFactors.autonomousDecisions },
117
+ { key: "customerFacing", label: "Customer Facing", value: riskFactors.customerFacing },
118
+ { key: "toolExecution", label: "Tool Execution", value: riskFactors.toolExecution },
119
+ { key: "externalDataAccess", label: "External Data Access", value: riskFactors.externalDataAccess },
120
+ { key: "piiProcessing", label: "PII Processing", value: riskFactors.piiProcessing },
121
+ { key: "highStakesDecisions", label: "High-Stakes Decisions", value: riskFactors.highStakesDecisions }
122
+ ];
123
+ for (const factor of factors) {
124
+ const icon = factor.value === true || factor.value === "yes" ? chalk.red("\u25CF") : factor.value === false || factor.value === "no" ? chalk.green("\u25CB") : chalk.yellow("\u25D0");
125
+ const valueStr = factor.value === true ? chalk.red("Yes") : factor.value === false ? chalk.green("No") : factor.value === "yes" ? chalk.red("Yes") : factor.value === "no" ? chalk.green("No") : chalk.yellow("Unknown");
126
+ console.log(` ${icon} ${factor.label}: ${valueStr}`);
127
+ }
128
+ }
129
+ function confidenceLabel(confidence) {
130
+ switch (confidence) {
131
+ case "high":
132
+ return chalk.green("HIGH ");
133
+ case "medium":
134
+ return chalk.yellow("MEDIUM");
135
+ case "low":
136
+ return chalk.gray("LOW ");
137
+ }
138
+ }
139
+ function formatCategory(category) {
140
+ const labels = {
141
+ llm_provider: "LLM Provider",
142
+ framework: "Framework",
143
+ agent_framework: "Agent Framework",
144
+ ml_framework: "ML Framework",
145
+ ml_ops: "ML Ops",
146
+ model_file: "Model File"
147
+ };
148
+ return labels[category] ?? category;
149
+ }
150
+ function truncate(text, maxLength) {
151
+ const cleaned = text.replace(/\s+/g, " ").trim();
152
+ if (cleaned.length <= maxLength) {
153
+ return cleaned;
154
+ }
155
+ return cleaned.slice(0, maxLength - 3) + "...";
156
+ }
157
+ function printAssetSuggestion(suggestion) {
158
+ printSubheader("Asset Card Suggestion");
159
+ console.log(` ${chalk.bold("Name:")} ${chalk.cyan(suggestion.name)}`);
160
+ console.log(` ${chalk.bold("Description:")} ${suggestion.description}`);
161
+ console.log(` ${chalk.bold("Type:")} ${suggestion.technical.type}`);
162
+ console.log(` ${chalk.bold("Framework:")} ${suggestion.technical.framework}`);
163
+ console.log(` ${chalk.bold("Confidence:")} ${confidenceLabel(suggestion.confidence)}`);
164
+ console.log();
165
+ if (suggestion.technical.components.length > 0) {
166
+ console.log(chalk.dim(" Components:"));
167
+ for (const comp of suggestion.technical.components) {
168
+ const details = [comp.type];
169
+ if (comp.provider) details.push(`provider: ${comp.provider}`);
170
+ if (comp.model) details.push(`model: ${comp.model}`);
171
+ console.log(` - ${details.join(", ")}`);
172
+ }
173
+ console.log();
174
+ }
175
+ if (Object.keys(suggestion.riskFactors).length > 0) {
176
+ console.log(chalk.dim(" Risk Factors:"));
177
+ printRiskFactorsInline(suggestion.riskFactors);
178
+ }
179
+ }
180
+ function printRiskFactorsInline(riskFactors) {
181
+ const factors = [
182
+ { key: "autonomousDecisions", label: "Autonomous", value: riskFactors.autonomousDecisions },
183
+ { key: "customerFacing", label: "Customer-Facing", value: riskFactors.customerFacing },
184
+ { key: "toolExecution", label: "Tool Execution", value: riskFactors.toolExecution },
185
+ { key: "externalDataAccess", label: "External Data", value: riskFactors.externalDataAccess },
186
+ { key: "piiProcessing", label: "PII", value: riskFactors.piiProcessing },
187
+ { key: "highStakesDecisions", label: "High-Stakes", value: riskFactors.highStakesDecisions }
188
+ ];
189
+ for (const factor of factors) {
190
+ if (factor.value === void 0) continue;
191
+ const icon = factor.value === true || factor.value === "yes" ? chalk.red("\u25CF") : factor.value === false || factor.value === "no" ? chalk.green("\u25CB") : chalk.yellow("\u25D0");
192
+ const valueStr = factor.value === true || factor.value === "yes" ? chalk.red("Yes") : factor.value === false || factor.value === "no" ? chalk.green("No") : chalk.yellow("Unknown");
193
+ console.log(` ${icon} ${factor.label}: ${valueStr}`);
194
+ }
195
+ }
196
+
197
+ // src/commands/scan.ts
198
+ var scanCommand = new Command("scan").description("Scan a directory for AI/ML frameworks and generate risk assessments").argument("[directory]", "Directory to scan", ".").option("-o, --output <format>", "Output format (text, json, yaml)", "text").option("-v, --verbose", "Show detailed detection information").option("-i, --include <patterns...>", "Include glob patterns").option("-e, --exclude <patterns...>", "Exclude glob patterns").option("-d, --max-depth <depth>", "Maximum directory depth", parseInt).option("-s, --suggest", "Generate asset card suggestion from detections").option("--no-progress", "Disable progress spinner").action(async (directory, options) => {
199
+ await runScan(directory, options);
200
+ });
201
+ async function runScan(directory, options) {
202
+ const targetDir = path.resolve(process.cwd(), directory);
203
+ if (options.output === "text") {
204
+ printHeader();
205
+ console.log(chalk2.dim(`Scanning: ${targetDir}
206
+ `));
207
+ }
208
+ initializePatterns();
209
+ const scanOptions = {
210
+ directory: targetDir,
211
+ ignorePatterns: options.exclude
212
+ };
213
+ let spinner;
214
+ let lastFile = "";
215
+ if (options.output === "text" && options.noProgress !== false) {
216
+ spinner = ora("Scanning...").start();
217
+ }
218
+ try {
219
+ const result = await scan(scanOptions, (progress) => {
220
+ if (spinner && progress.currentFile !== lastFile) {
221
+ lastFile = progress.currentFile;
222
+ const pct = Math.round(progress.scannedFiles / Math.max(progress.totalFiles, 1) * 100);
223
+ spinner.text = `Scanning (${pct}%): ${path.basename(progress.currentFile)}`;
224
+ }
225
+ });
226
+ spinner?.succeed(`Scanned ${result.scannedFiles} files`);
227
+ if (options.output === "json") {
228
+ outputJson(result);
229
+ } else if (options.output === "yaml") {
230
+ await outputYaml(result);
231
+ } else {
232
+ outputText(result, options);
233
+ }
234
+ if (options.suggest && result.detections.length > 0) {
235
+ const suggestion = suggestAssetCard(result);
236
+ if (options.output === "json") {
237
+ console.log(JSON.stringify({ suggestion }, null, 2));
238
+ } else if (options.output === "yaml") {
239
+ const { stringify: stringify2 } = await import("yaml");
240
+ console.log(stringify2({ suggestion }));
241
+ } else {
242
+ console.log();
243
+ printAssetSuggestion(suggestion);
244
+ }
245
+ }
246
+ if (result.summary.byConfidence.high > 0) {
247
+ process.exitCode = 1;
248
+ }
249
+ } catch (error) {
250
+ spinner?.fail("Scan failed");
251
+ if (error instanceof Error) {
252
+ console.error(chalk2.red(`
253
+ Error: ${error.message}`));
254
+ if (options.verbose) {
255
+ console.error(chalk2.dim(error.stack));
256
+ }
257
+ }
258
+ process.exit(1);
259
+ }
260
+ }
261
+ function outputText(result, options) {
262
+ console.log();
263
+ printScanSummary(result);
264
+ if (result.detections.length > 0) {
265
+ console.log();
266
+ printDetections(result.detections, options.verbose);
267
+ } else {
268
+ console.log(chalk2.dim("\nNo AI/ML frameworks detected."));
269
+ }
270
+ if (result.inferredRiskFactors && Object.keys(result.inferredRiskFactors).length > 0) {
271
+ console.log();
272
+ printRiskFactors(result.inferredRiskFactors);
273
+ }
274
+ }
275
+ function outputJson(result) {
276
+ console.log(JSON.stringify(result, null, 2));
277
+ }
278
+ async function outputYaml(result) {
279
+ const { stringify: stringify2 } = await import("yaml");
280
+ console.log(stringify2(result));
281
+ }
282
+
283
+ // src/commands/init.ts
284
+ import { Command as Command2 } from "commander";
285
+ import chalk3 from "chalk";
286
+ import ora2 from "ora";
287
+ import path2 from "path";
288
+ import fs from "fs/promises";
289
+ import { stringify } from "yaml";
290
+ import {
291
+ scan as scan2,
292
+ suggestAssetCard as suggestAssetCard2,
293
+ initializePatterns as initializePatterns2,
294
+ createAssetCard,
295
+ saveAssetCard
296
+ } from "@aigrc/core";
297
+
298
+ // src/utils/prompts.ts
299
+ import inquirer from "inquirer";
300
+ async function promptForOwner() {
301
+ const answers = await inquirer.prompt([
302
+ {
303
+ type: "input",
304
+ name: "name",
305
+ message: "Owner name:",
306
+ validate: (input) => input.length > 0 || "Name is required"
307
+ },
308
+ {
309
+ type: "input",
310
+ name: "email",
311
+ message: "Owner email:",
312
+ validate: (input) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(input) || "Valid email is required"
313
+ },
314
+ {
315
+ type: "input",
316
+ name: "team",
317
+ message: "Team name (optional):"
318
+ }
319
+ ]);
320
+ return {
321
+ name: answers.name,
322
+ email: answers.email,
323
+ team: answers.team || void 0
324
+ };
325
+ }
326
+ async function promptForRegistration(suggestion) {
327
+ console.log();
328
+ const basicAnswers = await inquirer.prompt([
329
+ {
330
+ type: "input",
331
+ name: "name",
332
+ message: "Asset name:",
333
+ default: suggestion.name,
334
+ validate: (input) => input.length > 0 || "Name is required"
335
+ },
336
+ {
337
+ type: "input",
338
+ name: "description",
339
+ message: "Description:",
340
+ default: suggestion.description
341
+ }
342
+ ]);
343
+ console.log("\n-- Owner Information --\n");
344
+ const owner = await promptForOwner();
345
+ console.log("\n-- Risk Factors --\n");
346
+ console.log("Based on the scan, the following risk factors were inferred:");
347
+ console.log();
348
+ const riskFactorLabels = [
349
+ { key: "autonomousDecisions", label: "Autonomous Decisions", value: suggestion.riskFactors.autonomousDecisions },
350
+ { key: "customerFacing", label: "Customer Facing", value: suggestion.riskFactors.customerFacing },
351
+ { key: "toolExecution", label: "Tool Execution", value: suggestion.riskFactors.toolExecution },
352
+ { key: "externalDataAccess", label: "External Data Access", value: suggestion.riskFactors.externalDataAccess },
353
+ { key: "piiProcessing", label: "PII Processing", value: suggestion.riskFactors.piiProcessing },
354
+ { key: "highStakesDecisions", label: "High-Stakes Decisions", value: suggestion.riskFactors.highStakesDecisions }
355
+ ];
356
+ for (const factor of riskFactorLabels) {
357
+ const value = factor.value === true || factor.value === "yes" ? "Yes" : factor.value === false || factor.value === "no" ? "No" : "Unknown";
358
+ console.log(` ${factor.label}: ${value}`);
359
+ }
360
+ console.log();
361
+ const { confirmRiskFactors } = await inquirer.prompt([
362
+ {
363
+ type: "confirm",
364
+ name: "confirmRiskFactors",
365
+ message: "Are these risk factors correct?",
366
+ default: true
367
+ }
368
+ ]);
369
+ let riskFactorOverrides;
370
+ if (!confirmRiskFactors) {
371
+ riskFactorOverrides = await promptForRiskFactorOverrides(suggestion.riskFactors);
372
+ }
373
+ return {
374
+ name: basicAnswers.name,
375
+ description: basicAnswers.description,
376
+ owner,
377
+ confirmRiskFactors,
378
+ riskFactorOverrides
379
+ };
380
+ }
381
+ async function promptForRiskFactorOverrides(current) {
382
+ const answers = await inquirer.prompt([
383
+ {
384
+ type: "confirm",
385
+ name: "autonomousDecisions",
386
+ message: "Does this AI make autonomous decisions?",
387
+ default: current.autonomousDecisions ?? false
388
+ },
389
+ {
390
+ type: "confirm",
391
+ name: "customerFacing",
392
+ message: "Is this AI customer-facing?",
393
+ default: current.customerFacing ?? false
394
+ },
395
+ {
396
+ type: "confirm",
397
+ name: "toolExecution",
398
+ message: "Does this AI execute tools or functions?",
399
+ default: current.toolExecution ?? false
400
+ },
401
+ {
402
+ type: "confirm",
403
+ name: "externalDataAccess",
404
+ message: "Does this AI access external data?",
405
+ default: current.externalDataAccess ?? false
406
+ },
407
+ {
408
+ type: "list",
409
+ name: "piiProcessing",
410
+ message: "Does this AI process PII (personal data)?",
411
+ choices: [
412
+ { name: "Yes", value: "yes" },
413
+ { name: "No", value: "no" },
414
+ { name: "Unknown", value: "unknown" }
415
+ ],
416
+ default: current.piiProcessing ?? "unknown"
417
+ },
418
+ {
419
+ type: "confirm",
420
+ name: "highStakesDecisions",
421
+ message: "Does this AI make high-stakes decisions (medical, legal, financial, hiring)?",
422
+ default: current.highStakesDecisions ?? false
423
+ }
424
+ ]);
425
+ return answers;
426
+ }
427
+
428
+ // src/commands/init.ts
429
+ var DEFAULT_CONFIG_FILE = ".aigrc.yaml";
430
+ var DEFAULT_CARDS_DIR = ".aigrc/cards";
431
+ var initCommand = new Command2("init").description("Initialize AIGRC in a project - scan, detect, and create asset card").argument("[directory]", "Directory to initialize", ".").option("-f, --force", "Overwrite existing configuration").option("-y, --yes", "Accept all defaults without prompting").option("-o, --output <path>", "Output path for asset card").action(async (directory, options) => {
432
+ await runInit(directory, options);
433
+ });
434
+ async function runInit(directory, options) {
435
+ const targetDir = path2.resolve(process.cwd(), directory);
436
+ printHeader();
437
+ console.log(chalk3.cyan("Initializing AIGRC in:"), targetDir);
438
+ console.log();
439
+ const configPath = path2.join(targetDir, DEFAULT_CONFIG_FILE);
440
+ const configExists = await fileExists(configPath);
441
+ if (configExists && !options.force) {
442
+ console.log(chalk3.yellow("\u26A0 AIGRC already initialized in this directory."));
443
+ console.log(chalk3.dim(` Config file: ${configPath}`));
444
+ console.log(chalk3.dim(" Use --force to reinitialize."));
445
+ return;
446
+ }
447
+ initializePatterns2();
448
+ const spinner = ora2("Scanning for AI/ML frameworks...").start();
449
+ try {
450
+ const result = await scan2({
451
+ directory: targetDir,
452
+ ignorePatterns: ["node_modules", ".git", "dist", "build", "__pycache__", ".venv", "venv"]
453
+ });
454
+ spinner.succeed(`Found ${result.detections.length} AI/ML detections`);
455
+ if (result.detections.length === 0) {
456
+ console.log();
457
+ console.log(chalk3.yellow("No AI/ML frameworks detected."));
458
+ console.log(chalk3.dim("You can still create an asset card manually using:"));
459
+ console.log(chalk3.dim(" aigrc register --manual"));
460
+ return;
461
+ }
462
+ const suggestion = suggestAssetCard2(result);
463
+ console.log();
464
+ printAssetSuggestion(suggestion);
465
+ console.log();
466
+ let registrationData;
467
+ if (options.yes) {
468
+ registrationData = {
469
+ name: suggestion.name,
470
+ description: suggestion.description,
471
+ owner: {
472
+ name: process.env.USER || process.env.USERNAME || "Unknown",
473
+ email: `${process.env.USER || process.env.USERNAME || "user"}@example.com`
474
+ },
475
+ confirmRiskFactors: true
476
+ };
477
+ } else {
478
+ registrationData = await promptForRegistration(suggestion);
479
+ }
480
+ const riskFactors = registrationData.confirmRiskFactors ? suggestion.riskFactors : { ...suggestion.riskFactors, ...registrationData.riskFactorOverrides };
481
+ const assetCard = createAssetCard({
482
+ name: registrationData.name,
483
+ description: registrationData.description,
484
+ owner: registrationData.owner,
485
+ technical: {
486
+ type: suggestion.technical.type,
487
+ framework: suggestion.technical.framework
488
+ },
489
+ riskFactors: {
490
+ autonomousDecisions: riskFactors.autonomousDecisions ?? false,
491
+ customerFacing: riskFactors.customerFacing ?? false,
492
+ toolExecution: riskFactors.toolExecution ?? false,
493
+ externalDataAccess: riskFactors.externalDataAccess ?? false,
494
+ piiProcessing: riskFactors.piiProcessing ?? "unknown",
495
+ highStakesDecisions: riskFactors.highStakesDecisions ?? false
496
+ }
497
+ });
498
+ const cardsDir = path2.join(targetDir, DEFAULT_CARDS_DIR);
499
+ await fs.mkdir(cardsDir, { recursive: true });
500
+ const cardFileName = `${sanitizeFileName(assetCard.name)}.yaml`;
501
+ const cardPath = options.output || path2.join(cardsDir, cardFileName);
502
+ saveAssetCard(assetCard, cardPath);
503
+ console.log();
504
+ console.log(chalk3.green("\u2713 Asset card created:"), chalk3.cyan(cardPath));
505
+ const config = {
506
+ version: "1.0",
507
+ cardsDir: DEFAULT_CARDS_DIR,
508
+ scan: {
509
+ exclude: ["node_modules", ".git", "dist", "build", "__pycache__", ".venv", "venv"]
510
+ }
511
+ };
512
+ await fs.writeFile(configPath, stringify(config), "utf-8");
513
+ console.log(chalk3.green("\u2713 Config file created:"), chalk3.cyan(configPath));
514
+ console.log();
515
+ console.log(chalk3.green("AIGRC initialized successfully!"));
516
+ console.log();
517
+ console.log(chalk3.dim("Next steps:"));
518
+ console.log(chalk3.dim(" 1. Review and edit the asset card: ") + chalk3.cyan(cardPath));
519
+ console.log(chalk3.dim(" 2. Run ") + chalk3.cyan("aigrc validate") + chalk3.dim(" to check compliance"));
520
+ console.log(chalk3.dim(" 3. Run ") + chalk3.cyan("aigrc status") + chalk3.dim(" to see current status"));
521
+ } catch (error) {
522
+ spinner.fail("Initialization failed");
523
+ if (error instanceof Error) {
524
+ console.error(chalk3.red(`
525
+ Error: ${error.message}`));
526
+ }
527
+ process.exit(1);
528
+ }
529
+ }
530
+ async function fileExists(filePath) {
531
+ try {
532
+ await fs.access(filePath);
533
+ return true;
534
+ } catch {
535
+ return false;
536
+ }
537
+ }
538
+ function sanitizeFileName(name) {
539
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
540
+ }
541
+
542
+ // src/commands/register.ts
543
+ import { Command as Command3 } from "commander";
544
+ import chalk4 from "chalk";
545
+ import path3 from "path";
546
+ import fs2 from "fs/promises";
547
+ import inquirer2 from "inquirer";
548
+ import {
549
+ createAssetCard as createAssetCard2,
550
+ saveAssetCard as saveAssetCard2
551
+ } from "@aigrc/core";
552
+ var DEFAULT_CARDS_DIR2 = ".aigrc/cards";
553
+ var registerCommand = new Command3("register").description("Manually register an AI asset without scanning").option("-o, --output <path>", "Output path for asset card").option("-m, --manual", "Skip detection and enter all details manually").action(async (options) => {
554
+ await runRegister(options);
555
+ });
556
+ async function runRegister(options) {
557
+ printHeader();
558
+ console.log(chalk4.cyan("Register a new AI asset\n"));
559
+ const basicInfo = await inquirer2.prompt([
560
+ {
561
+ type: "input",
562
+ name: "name",
563
+ message: "Asset name:",
564
+ validate: (input) => input.length > 0 || "Name is required"
565
+ },
566
+ {
567
+ type: "input",
568
+ name: "description",
569
+ message: "Description:",
570
+ validate: (input) => input.length > 0 || "Description is required"
571
+ },
572
+ {
573
+ type: "input",
574
+ name: "purpose",
575
+ message: "Business purpose:"
576
+ }
577
+ ]);
578
+ console.log("\n-- Owner Information --\n");
579
+ const owner = await promptForOwner();
580
+ console.log("\n-- Risk Factors --\n");
581
+ const riskFactors = await promptForRiskFactors();
582
+ console.log("\n-- Framework Information --\n");
583
+ const frameworks = await promptForFrameworks();
584
+ const frameworkTypes = {
585
+ openai: "api_client",
586
+ anthropic: "api_client",
587
+ langchain: "framework",
588
+ llamaindex: "framework",
589
+ "vercel-ai-sdk": "framework",
590
+ pytorch: "model",
591
+ tensorflow: "model",
592
+ huggingface: "model",
593
+ crewai: "agent",
594
+ autogen: "agent"
595
+ };
596
+ const primaryFramework = frameworks[0] || "unknown";
597
+ const technicalType = frameworkTypes[primaryFramework] || "framework";
598
+ const assetCard = createAssetCard2({
599
+ name: basicInfo.name,
600
+ description: basicInfo.description,
601
+ owner,
602
+ technical: {
603
+ type: technicalType,
604
+ framework: primaryFramework
605
+ },
606
+ riskFactors
607
+ });
608
+ const cardsDir = path3.join(process.cwd(), DEFAULT_CARDS_DIR2);
609
+ await fs2.mkdir(cardsDir, { recursive: true });
610
+ const cardFileName = `${sanitizeFileName2(assetCard.name)}.yaml`;
611
+ const cardPath = options.output || path3.join(cardsDir, cardFileName);
612
+ saveAssetCard2(assetCard, cardPath);
613
+ console.log();
614
+ console.log(chalk4.green("\u2713 Asset card created:"), chalk4.cyan(cardPath));
615
+ console.log();
616
+ console.log(chalk4.dim("Next steps:"));
617
+ console.log(chalk4.dim(" 1. Review and edit the asset card"));
618
+ console.log(chalk4.dim(" 2. Run ") + chalk4.cyan("aigrc validate") + chalk4.dim(" to check compliance"));
619
+ }
620
+ async function promptForRiskFactors() {
621
+ const answers = await inquirer2.prompt([
622
+ {
623
+ type: "confirm",
624
+ name: "autonomousDecisions",
625
+ message: "Does this AI make autonomous decisions?",
626
+ default: false
627
+ },
628
+ {
629
+ type: "confirm",
630
+ name: "customerFacing",
631
+ message: "Is this AI customer-facing?",
632
+ default: false
633
+ },
634
+ {
635
+ type: "confirm",
636
+ name: "toolExecution",
637
+ message: "Does this AI execute tools or functions?",
638
+ default: false
639
+ },
640
+ {
641
+ type: "confirm",
642
+ name: "externalDataAccess",
643
+ message: "Does this AI access external data?",
644
+ default: false
645
+ },
646
+ {
647
+ type: "list",
648
+ name: "piiProcessing",
649
+ message: "Does this AI process PII (personal data)?",
650
+ choices: [
651
+ { name: "Yes", value: "yes" },
652
+ { name: "No", value: "no" },
653
+ { name: "Unknown", value: "unknown" }
654
+ ],
655
+ default: "unknown"
656
+ },
657
+ {
658
+ type: "confirm",
659
+ name: "highStakesDecisions",
660
+ message: "Does this AI make high-stakes decisions (medical, legal, financial, hiring)?",
661
+ default: false
662
+ }
663
+ ]);
664
+ return answers;
665
+ }
666
+ async function promptForFrameworks() {
667
+ const { hasFrameworks } = await inquirer2.prompt([
668
+ {
669
+ type: "confirm",
670
+ name: "hasFrameworks",
671
+ message: "Do you want to specify AI/ML frameworks used?",
672
+ default: true
673
+ }
674
+ ]);
675
+ if (!hasFrameworks) {
676
+ return [];
677
+ }
678
+ const { frameworkList } = await inquirer2.prompt([
679
+ {
680
+ type: "checkbox",
681
+ name: "frameworkList",
682
+ message: "Select frameworks used:",
683
+ choices: [
684
+ { name: "OpenAI API", value: "openai" },
685
+ { name: "Anthropic API", value: "anthropic" },
686
+ { name: "LangChain", value: "langchain" },
687
+ { name: "LlamaIndex", value: "llamaindex" },
688
+ { name: "Vercel AI SDK", value: "vercel-ai-sdk" },
689
+ { name: "PyTorch", value: "pytorch" },
690
+ { name: "TensorFlow", value: "tensorflow" },
691
+ { name: "Hugging Face", value: "huggingface" },
692
+ { name: "CrewAI", value: "crewai" },
693
+ { name: "AutoGen", value: "autogen" },
694
+ { name: "Other", value: "other" }
695
+ ]
696
+ }
697
+ ]);
698
+ if (frameworkList.includes("other")) {
699
+ const { otherFrameworks } = await inquirer2.prompt([
700
+ {
701
+ type: "input",
702
+ name: "otherFrameworks",
703
+ message: "Enter other frameworks (comma-separated):"
704
+ }
705
+ ]);
706
+ const others = otherFrameworks.split(",").map((f) => f.trim()).filter((f) => f.length > 0);
707
+ return [...frameworkList.filter((f) => f !== "other"), ...others];
708
+ }
709
+ return frameworkList;
710
+ }
711
+ function sanitizeFileName2(name) {
712
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
713
+ }
714
+
715
+ // src/commands/validate.ts
716
+ import { Command as Command4 } from "commander";
717
+ import chalk5 from "chalk";
718
+ import ora3 from "ora";
719
+ import path4 from "path";
720
+ import fs3 from "fs/promises";
721
+ import {
722
+ loadAssetCard,
723
+ classifyRisk,
724
+ validateAssetCard
725
+ } from "@aigrc/core";
726
+ var DEFAULT_CARDS_DIR3 = ".aigrc/cards";
727
+ var validateCommand = new Command4("validate").description("Validate asset cards against compliance requirements").argument("[path]", "Path to asset card or cards directory").option("-s, --strict", "Fail on warnings as well as errors").option("-o, --output <format>", "Output format (text, json)", "text").option("-a, --all", "Validate all cards in the cards directory").action(async (cardPath, options) => {
728
+ await runValidate(cardPath, options);
729
+ });
730
+ async function runValidate(cardPath, options) {
731
+ if (options.output === "text") {
732
+ printHeader();
733
+ }
734
+ const cardsToValidate = [];
735
+ if (options.all || !cardPath) {
736
+ const cardsDir = path4.join(process.cwd(), DEFAULT_CARDS_DIR3);
737
+ try {
738
+ const files = await fs3.readdir(cardsDir);
739
+ const yamlFiles = files.filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
740
+ for (const file of yamlFiles) {
741
+ cardsToValidate.push(path4.join(cardsDir, file));
742
+ }
743
+ } catch {
744
+ if (options.output === "text") {
745
+ console.log(chalk5.yellow("No cards directory found."));
746
+ console.log(chalk5.dim(`Expected: ${cardsDir}`));
747
+ console.log(chalk5.dim("Run `aigrc init` to initialize AIGRC."));
748
+ } else {
749
+ console.log(JSON.stringify({ error: "No cards directory found" }));
750
+ }
751
+ process.exit(1);
752
+ }
753
+ } else {
754
+ cardsToValidate.push(path4.resolve(process.cwd(), cardPath));
755
+ }
756
+ if (cardsToValidate.length === 0) {
757
+ if (options.output === "text") {
758
+ console.log(chalk5.yellow("No asset cards found to validate."));
759
+ } else {
760
+ console.log(JSON.stringify({ error: "No asset cards found" }));
761
+ }
762
+ process.exit(1);
763
+ }
764
+ const results = [];
765
+ let hasErrors = false;
766
+ for (const cardFile of cardsToValidate) {
767
+ const spinner = options.output === "text" ? ora3(`Validating ${path4.basename(cardFile)}...`).start() : void 0;
768
+ try {
769
+ const card = loadAssetCard(cardFile);
770
+ const validation = validateAssetCard(card);
771
+ const classification = classifyRisk(card.classification.riskFactors);
772
+ results.push({
773
+ path: cardFile,
774
+ card,
775
+ validation: {
776
+ valid: validation.valid,
777
+ errors: validation.errors ?? []
778
+ },
779
+ classification
780
+ });
781
+ if (!validation.valid) {
782
+ hasErrors = true;
783
+ spinner?.fail(`${path4.basename(cardFile)}: Invalid`);
784
+ } else {
785
+ spinner?.succeed(`${path4.basename(cardFile)}: Valid`);
786
+ }
787
+ } catch (error) {
788
+ hasErrors = true;
789
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
790
+ results.push({
791
+ path: cardFile,
792
+ validation: {
793
+ valid: false,
794
+ errors: [errorMessage]
795
+ }
796
+ });
797
+ spinner?.fail(`${path4.basename(cardFile)}: Parse error`);
798
+ }
799
+ }
800
+ if (options.output === "json") {
801
+ console.log(JSON.stringify({ results }, null, 2));
802
+ } else {
803
+ console.log();
804
+ printValidationSummary(results);
805
+ }
806
+ if (hasErrors) {
807
+ process.exit(1);
808
+ }
809
+ }
810
+ function printValidationSummary(results) {
811
+ console.log(chalk5.bold("Validation Summary"));
812
+ console.log(chalk5.dim("\u2500".repeat(50)));
813
+ console.log();
814
+ for (const result of results) {
815
+ const fileName = path4.basename(result.path);
816
+ if (!result.validation.valid) {
817
+ console.log(chalk5.red(`\u2717 ${fileName}`));
818
+ for (const error of result.validation.errors) {
819
+ console.log(chalk5.red(` Error: ${error}`));
820
+ }
821
+ } else {
822
+ console.log(chalk5.green(`\u2713 ${fileName}`));
823
+ if (result.card && result.classification) {
824
+ const tierColor = getRiskLevelColor(result.classification.riskLevel);
825
+ console.log(
826
+ chalk5.dim(" Risk Level: ") + tierColor(result.classification.riskLevel)
827
+ );
828
+ if (result.classification.euAiActCategory) {
829
+ console.log(
830
+ chalk5.dim(" EU AI Act: ") + chalk5.yellow(result.classification.euAiActCategory)
831
+ );
832
+ }
833
+ }
834
+ }
835
+ console.log();
836
+ }
837
+ const valid = results.filter((r) => r.validation.valid).length;
838
+ const invalid = results.filter((r) => !r.validation.valid).length;
839
+ console.log(chalk5.dim("\u2500".repeat(50)));
840
+ console.log(
841
+ `Total: ${results.length} | ` + chalk5.green(`Valid: ${valid}`) + ` | ` + chalk5.red(`Invalid: ${invalid}`)
842
+ );
843
+ }
844
+ function getRiskLevelColor(level) {
845
+ switch (level) {
846
+ case "minimal":
847
+ return chalk5.green;
848
+ case "limited":
849
+ return chalk5.yellow;
850
+ case "high":
851
+ return chalk5.red;
852
+ case "unacceptable":
853
+ return chalk5.magenta;
854
+ default:
855
+ return chalk5.white;
856
+ }
857
+ }
858
+
859
+ // src/commands/status.ts
860
+ import { Command as Command5 } from "commander";
861
+ import chalk6 from "chalk";
862
+ import path5 from "path";
863
+ import fs4 from "fs/promises";
864
+ import {
865
+ loadAssetCard as loadAssetCard2,
866
+ classifyRisk as classifyRisk2
867
+ } from "@aigrc/core";
868
+ var DEFAULT_CARDS_DIR4 = ".aigrc/cards";
869
+ var DEFAULT_CONFIG_FILE2 = ".aigrc.yaml";
870
+ var statusCommand = new Command5("status").description("Show the current AIGRC status and registered assets").option("-o, --output <format>", "Output format (text, json)", "text").action(async (options) => {
871
+ await runStatus(options);
872
+ });
873
+ async function runStatus(options) {
874
+ const cwd = process.cwd();
875
+ if (options.output === "text") {
876
+ printHeader();
877
+ }
878
+ const configPath = path5.join(cwd, DEFAULT_CONFIG_FILE2);
879
+ const cardsDir = path5.join(cwd, DEFAULT_CARDS_DIR4);
880
+ const configExists = await fileExists2(configPath);
881
+ const cardsDirExists = await directoryExists(cardsDir);
882
+ if (!configExists && !cardsDirExists) {
883
+ if (options.output === "text") {
884
+ console.log(chalk6.yellow("AIGRC is not initialized in this directory."));
885
+ console.log(chalk6.dim("\nRun `aigrc init` to get started."));
886
+ } else {
887
+ console.log(JSON.stringify({ initialized: false }));
888
+ }
889
+ return;
890
+ }
891
+ const cards = [];
892
+ if (cardsDirExists) {
893
+ try {
894
+ const files = await fs4.readdir(cardsDir);
895
+ const yamlFiles = files.filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
896
+ for (const file of yamlFiles) {
897
+ try {
898
+ const filePath = path5.join(cardsDir, file);
899
+ const card = loadAssetCard2(filePath);
900
+ const classification = classifyRisk2(card.classification.riskFactors);
901
+ cards.push({ path: filePath, card, classification });
902
+ } catch {
903
+ }
904
+ }
905
+ } catch {
906
+ }
907
+ }
908
+ if (options.output === "json") {
909
+ console.log(
910
+ JSON.stringify(
911
+ {
912
+ initialized: true,
913
+ configPath,
914
+ cardsDir,
915
+ cards: cards.map((c) => ({
916
+ path: c.path,
917
+ name: c.card.name,
918
+ id: c.card.id,
919
+ riskLevel: c.classification.riskLevel,
920
+ euAiActCategory: c.classification.euAiActCategory
921
+ }))
922
+ },
923
+ null,
924
+ 2
925
+ )
926
+ );
927
+ return;
928
+ }
929
+ console.log(chalk6.bold("AIGRC Status"));
930
+ console.log(chalk6.dim("\u2500".repeat(50)));
931
+ console.log();
932
+ console.log(chalk6.dim("Config:"), configExists ? chalk6.green("\u2713") : chalk6.red("\u2717"), configPath);
933
+ console.log(chalk6.dim("Cards:"), cardsDirExists ? chalk6.green("\u2713") : chalk6.red("\u2717"), cardsDir);
934
+ console.log();
935
+ if (cards.length === 0) {
936
+ console.log(chalk6.yellow("No asset cards registered."));
937
+ console.log(chalk6.dim("\nRun `aigrc init` or `aigrc register` to create an asset card."));
938
+ return;
939
+ }
940
+ console.log(chalk6.bold(`Registered Assets (${cards.length})`));
941
+ console.log(chalk6.dim("\u2500".repeat(50)));
942
+ console.log();
943
+ const byLevel = groupByRiskLevel(cards);
944
+ for (const level of ["unacceptable", "high", "limited", "minimal"]) {
945
+ const levelCards = byLevel.get(level);
946
+ if (!levelCards || levelCards.length === 0) continue;
947
+ const levelColor = getRiskLevelColor2(level);
948
+ console.log(levelColor(`${level.toUpperCase()} (${levelCards.length})`));
949
+ console.log();
950
+ for (const { card, classification } of levelCards) {
951
+ console.log(` ${chalk6.bold(card.name)}`);
952
+ console.log(chalk6.dim(` ID: ${card.id}`));
953
+ console.log(chalk6.dim(` Risk Level: ${classification.riskLevel}`));
954
+ if (classification.euAiActCategory) {
955
+ console.log(chalk6.dim(` EU AI Act: `) + chalk6.yellow(classification.euAiActCategory));
956
+ }
957
+ if (card.ownership?.owner) {
958
+ console.log(chalk6.dim(` Owner: ${card.ownership.owner.name} <${card.ownership.owner.email}>`));
959
+ }
960
+ const activeRisks = getActiveRiskFactors(card);
961
+ if (activeRisks.length > 0) {
962
+ console.log(chalk6.dim(` Risks: `) + activeRisks.join(", "));
963
+ }
964
+ console.log();
965
+ }
966
+ }
967
+ printStatusSummary(cards);
968
+ }
969
+ function groupByRiskLevel(cards) {
970
+ const byLevel = /* @__PURE__ */ new Map();
971
+ for (const item of cards) {
972
+ const level = item.classification.riskLevel;
973
+ if (!byLevel.has(level)) {
974
+ byLevel.set(level, []);
975
+ }
976
+ byLevel.get(level).push(item);
977
+ }
978
+ return byLevel;
979
+ }
980
+ function printStatusSummary(cards) {
981
+ console.log(chalk6.dim("\u2500".repeat(50)));
982
+ const minimal = cards.filter((c) => c.classification.riskLevel === "minimal").length;
983
+ const limited = cards.filter((c) => c.classification.riskLevel === "limited").length;
984
+ const high = cards.filter((c) => c.classification.riskLevel === "high").length;
985
+ const unacceptable = cards.filter((c) => c.classification.riskLevel === "unacceptable").length;
986
+ console.log(
987
+ `Total: ${cards.length} | ` + chalk6.green(`Minimal: ${minimal}`) + ` | ` + chalk6.yellow(`Limited: ${limited}`) + ` | ` + chalk6.red(`High: ${high}`) + ` | ` + chalk6.magenta(`Unacceptable: ${unacceptable}`)
988
+ );
989
+ if (high > 0 || unacceptable > 0) {
990
+ console.log();
991
+ console.log(chalk6.yellow("\u26A0 High-risk assets detected. Review compliance requirements."));
992
+ }
993
+ }
994
+ function getActiveRiskFactors(card) {
995
+ const risks = [];
996
+ const rf = card.classification?.riskFactors;
997
+ if (!rf) return risks;
998
+ if (rf.autonomousDecisions) risks.push("Autonomous");
999
+ if (rf.customerFacing) risks.push("Customer-Facing");
1000
+ if (rf.toolExecution) risks.push("Tool Execution");
1001
+ if (rf.externalDataAccess) risks.push("External Data");
1002
+ if (rf.piiProcessing === "yes") risks.push("PII");
1003
+ if (rf.highStakesDecisions) risks.push("High-Stakes");
1004
+ return risks;
1005
+ }
1006
+ function getRiskLevelColor2(level) {
1007
+ switch (level) {
1008
+ case "minimal":
1009
+ return chalk6.green;
1010
+ case "limited":
1011
+ return chalk6.yellow;
1012
+ case "high":
1013
+ return chalk6.red;
1014
+ case "unacceptable":
1015
+ return chalk6.magenta;
1016
+ default:
1017
+ return chalk6.white;
1018
+ }
1019
+ }
1020
+ async function fileExists2(filePath) {
1021
+ try {
1022
+ const stat = await fs4.stat(filePath);
1023
+ return stat.isFile();
1024
+ } catch {
1025
+ return false;
1026
+ }
1027
+ }
1028
+ async function directoryExists(dirPath) {
1029
+ try {
1030
+ const stat = await fs4.stat(dirPath);
1031
+ return stat.isDirectory();
1032
+ } catch {
1033
+ return false;
1034
+ }
1035
+ }
1036
+
1037
+ // src/commands/compliance.ts
1038
+ import { Command as Command6 } from "commander";
1039
+ import chalk7 from "chalk";
1040
+ import ora4 from "ora";
1041
+ import { existsSync, readFileSync, writeFileSync } from "fs";
1042
+ import { join } from "path";
1043
+ import YAML from "yaml";
1044
+ var BUILTIN_PROFILES = [
1045
+ {
1046
+ id: "eu-ai-act",
1047
+ name: "EU AI Act",
1048
+ version: "2024.1",
1049
+ jurisdiction: "EU",
1050
+ description: "European Union Artificial Intelligence Act"
1051
+ },
1052
+ {
1053
+ id: "us-omb-m24",
1054
+ name: "US OMB M-24-10/M-24-18",
1055
+ version: "2024.1",
1056
+ jurisdiction: "US",
1057
+ description: "US Federal AI governance memoranda"
1058
+ },
1059
+ {
1060
+ id: "nist-ai-rmf",
1061
+ name: "NIST AI RMF",
1062
+ version: "1.0",
1063
+ jurisdiction: "US",
1064
+ description: "NIST AI Risk Management Framework"
1065
+ },
1066
+ {
1067
+ id: "iso-42001",
1068
+ name: "ISO/IEC 42001",
1069
+ version: "2023",
1070
+ jurisdiction: "International",
1071
+ description: "AI Management System Standard"
1072
+ }
1073
+ ];
1074
+ var complianceCommand = new Command6("compliance").description("Manage compliance profiles").addCommand(
1075
+ new Command6("list").description("List available compliance profiles").option("-a, --active", "Show only active profiles").option("-o, --output <format>", "Output format (text, json, yaml)", "text").action(async (options) => {
1076
+ await listProfiles(options);
1077
+ })
1078
+ ).addCommand(
1079
+ new Command6("show").description("Show details of a profile").argument("<profileId>", "Profile ID (e.g., eu-ai-act, us-omb-m24)").option("-o, --output <format>", "Output format (text, json, yaml)", "text").action(async (profileId, options) => {
1080
+ await showProfile(profileId, options);
1081
+ })
1082
+ ).addCommand(
1083
+ new Command6("set").description("Set active profiles in .aigrc.yaml").argument("<profiles...>", "Profile IDs to activate").option("--stack", "Enable profile stacking (strictest wins)").action(async (profiles, options) => {
1084
+ await setProfiles(profiles, options);
1085
+ })
1086
+ );
1087
+ async function listProfiles(options) {
1088
+ if (options.output === "text") {
1089
+ printHeader();
1090
+ printSubheader("Available Compliance Profiles");
1091
+ }
1092
+ const activeProfiles = loadActiveProfiles();
1093
+ const profiles = options.active ? BUILTIN_PROFILES.filter((p) => activeProfiles.includes(p.id)) : BUILTIN_PROFILES;
1094
+ if (options.output === "json") {
1095
+ console.log(JSON.stringify(profiles, null, 2));
1096
+ return;
1097
+ }
1098
+ if (options.output === "yaml") {
1099
+ console.log(YAML.stringify(profiles));
1100
+ return;
1101
+ }
1102
+ console.log();
1103
+ console.log(
1104
+ chalk7.dim(" ID".padEnd(16)) + chalk7.dim("Name".padEnd(28)) + chalk7.dim("Jurisdiction".padEnd(16)) + chalk7.dim("Active")
1105
+ );
1106
+ console.log(chalk7.dim(" " + "\u2500".repeat(70)));
1107
+ for (const profile of profiles) {
1108
+ const isActive = activeProfiles.includes(profile.id);
1109
+ const activeIndicator = isActive ? chalk7.green("\u2713") : chalk7.gray("\xB7");
1110
+ console.log(
1111
+ ` ${chalk7.cyan(profile.id.padEnd(14))} ${profile.name.padEnd(26)} ${chalk7.dim(profile.jurisdiction.padEnd(14))} ${activeIndicator}`
1112
+ );
1113
+ }
1114
+ console.log();
1115
+ printInfo(`${profiles.length} profile(s) available, ${activeProfiles.length} active`);
1116
+ }
1117
+ async function showProfile(profileId, options) {
1118
+ const profile = BUILTIN_PROFILES.find((p) => p.id === profileId);
1119
+ if (!profile) {
1120
+ printError(`Profile not found: ${profileId}`);
1121
+ console.log();
1122
+ printInfo(`Available profiles: ${BUILTIN_PROFILES.map((p) => p.id).join(", ")}`);
1123
+ process.exit(1);
1124
+ }
1125
+ if (options.output === "json") {
1126
+ console.log(JSON.stringify(profile, null, 2));
1127
+ return;
1128
+ }
1129
+ if (options.output === "yaml") {
1130
+ console.log(YAML.stringify(profile));
1131
+ return;
1132
+ }
1133
+ printHeader();
1134
+ console.log(chalk7.bold.cyan(`Profile: ${profile.name}`));
1135
+ console.log();
1136
+ console.log(` ${chalk7.dim("ID:")} ${profile.id}`);
1137
+ console.log(` ${chalk7.dim("Version:")} ${profile.version}`);
1138
+ console.log(` ${chalk7.dim("Jurisdiction:")} ${profile.jurisdiction}`);
1139
+ console.log(` ${chalk7.dim("Description:")} ${profile.description}`);
1140
+ const activeProfiles = loadActiveProfiles();
1141
+ const isActive = activeProfiles.includes(profile.id);
1142
+ console.log();
1143
+ console.log(
1144
+ ` ${chalk7.dim("Status:")} ${isActive ? chalk7.green("Active") : chalk7.gray("Inactive")}`
1145
+ );
1146
+ }
1147
+ async function setProfiles(profiles, options) {
1148
+ printHeader();
1149
+ const invalidProfiles = profiles.filter(
1150
+ (p) => !BUILTIN_PROFILES.find((bp) => bp.id === p)
1151
+ );
1152
+ if (invalidProfiles.length > 0) {
1153
+ printError(`Unknown profile(s): ${invalidProfiles.join(", ")}`);
1154
+ console.log();
1155
+ printInfo(`Available profiles: ${BUILTIN_PROFILES.map((p) => p.id).join(", ")}`);
1156
+ process.exit(1);
1157
+ }
1158
+ const spinner = ora4("Updating configuration...").start();
1159
+ try {
1160
+ const configPath = join(process.cwd(), ".aigrc.yaml");
1161
+ let config = {};
1162
+ if (existsSync(configPath)) {
1163
+ const content = readFileSync(configPath, "utf-8");
1164
+ config = YAML.parse(content) || {};
1165
+ }
1166
+ config.profiles = profiles;
1167
+ if (options.stack) {
1168
+ config.stackProfiles = true;
1169
+ }
1170
+ writeFileSync(configPath, YAML.stringify(config, { indent: 2 }), "utf-8");
1171
+ spinner.succeed("Configuration updated");
1172
+ console.log();
1173
+ printSuccess(`Active profiles: ${profiles.join(", ")}`);
1174
+ if (options.stack) {
1175
+ printInfo("Profile stacking enabled (strictest requirements apply)");
1176
+ }
1177
+ } catch (error) {
1178
+ spinner.fail("Failed to update configuration");
1179
+ printError(error instanceof Error ? error.message : String(error));
1180
+ process.exit(1);
1181
+ }
1182
+ }
1183
+ function loadActiveProfiles() {
1184
+ const configPath = join(process.cwd(), ".aigrc.yaml");
1185
+ if (!existsSync(configPath)) {
1186
+ return ["eu-ai-act"];
1187
+ }
1188
+ try {
1189
+ const content = readFileSync(configPath, "utf-8");
1190
+ const config = YAML.parse(content);
1191
+ return config?.profiles || ["eu-ai-act"];
1192
+ } catch {
1193
+ return ["eu-ai-act"];
1194
+ }
1195
+ }
1196
+
1197
+ // src/commands/classify.ts
1198
+ import { Command as Command7 } from "commander";
1199
+ import chalk8 from "chalk";
1200
+ import ora5 from "ora";
1201
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
1202
+ import { join as join2 } from "path";
1203
+ import YAML2 from "yaml";
1204
+ import { loadAssetCard as loadAssetCard3, saveAssetCard as saveAssetCard3 } from "@aigrc/core";
1205
+ var RISK_MAPPINGS = {
1206
+ "eu-ai-act": {
1207
+ minimal: "minimal",
1208
+ limited: "limited",
1209
+ high: "high",
1210
+ unacceptable: "unacceptable"
1211
+ },
1212
+ "us-omb-m24": {
1213
+ minimal: "neither",
1214
+ limited: "neither",
1215
+ high: "rights-impacting",
1216
+ unacceptable: "safety-impacting"
1217
+ },
1218
+ "nist-ai-rmf": {
1219
+ minimal: "minimal",
1220
+ limited: "low",
1221
+ high: "high",
1222
+ unacceptable: "critical"
1223
+ },
1224
+ "iso-42001": {
1225
+ minimal: "low",
1226
+ limited: "medium",
1227
+ high: "high",
1228
+ unacceptable: "critical"
1229
+ }
1230
+ };
1231
+ var PROFILE_NAMES = {
1232
+ "eu-ai-act": "EU AI Act",
1233
+ "us-omb-m24": "US OMB M-24",
1234
+ "nist-ai-rmf": "NIST AI RMF",
1235
+ "iso-42001": "ISO/IEC 42001"
1236
+ };
1237
+ var classifyCommand = new Command7("classify").description("Classify an asset against compliance profiles").argument("<assetPath>", "Path to asset card YAML file").option("-p, --profiles <profiles...>", "Profile IDs to classify against").option("-a, --all", "Classify against all available profiles").option("-o, --output <format>", "Output format (text, json, yaml)", "text").option("-u, --update", "Update asset card with classification results").action(async (assetPath, options) => {
1238
+ await runClassify(assetPath, options);
1239
+ });
1240
+ async function runClassify(assetPath, options) {
1241
+ if (options.output === "text") {
1242
+ printHeader();
1243
+ }
1244
+ if (!existsSync2(assetPath)) {
1245
+ printError(`Asset card not found: ${assetPath}`);
1246
+ process.exit(1);
1247
+ }
1248
+ const spinner = ora5("Loading asset card...").start();
1249
+ let card;
1250
+ try {
1251
+ card = loadAssetCard3(assetPath);
1252
+ spinner.succeed(`Loaded: ${card.name}`);
1253
+ } catch (error) {
1254
+ spinner.fail("Failed to load asset card");
1255
+ printError(error instanceof Error ? error.message : String(error));
1256
+ process.exit(1);
1257
+ }
1258
+ let profileIds;
1259
+ if (options.all) {
1260
+ profileIds = Object.keys(RISK_MAPPINGS);
1261
+ } else if (options.profiles && options.profiles.length > 0) {
1262
+ profileIds = options.profiles;
1263
+ } else {
1264
+ profileIds = loadActiveProfiles2();
1265
+ }
1266
+ const invalidProfiles = profileIds.filter((p) => !RISK_MAPPINGS[p]);
1267
+ if (invalidProfiles.length > 0) {
1268
+ printError(`Unknown profile(s): ${invalidProfiles.join(", ")}`);
1269
+ process.exit(1);
1270
+ }
1271
+ const classifications = [];
1272
+ const aigrcLevel = card.classification.riskLevel;
1273
+ for (const profileId of profileIds) {
1274
+ const mapping = RISK_MAPPINGS[profileId];
1275
+ const mappedLevel = mapping[aigrcLevel] || aigrcLevel;
1276
+ classifications.push({
1277
+ profileId,
1278
+ profileName: PROFILE_NAMES[profileId] || profileId,
1279
+ aigrcLevel,
1280
+ mappedLevel
1281
+ });
1282
+ }
1283
+ if (options.output === "json") {
1284
+ console.log(
1285
+ JSON.stringify(
1286
+ {
1287
+ asset: card.name,
1288
+ aigrcRiskLevel: aigrcLevel,
1289
+ classifications
1290
+ },
1291
+ null,
1292
+ 2
1293
+ )
1294
+ );
1295
+ return;
1296
+ }
1297
+ if (options.output === "yaml") {
1298
+ console.log(
1299
+ YAML2.stringify({
1300
+ asset: card.name,
1301
+ aigrcRiskLevel: aigrcLevel,
1302
+ classifications
1303
+ })
1304
+ );
1305
+ return;
1306
+ }
1307
+ console.log();
1308
+ printSubheader(`Classification: ${card.name}`);
1309
+ console.log();
1310
+ console.log(` ${chalk8.dim("AIGRC Risk Level:")} ${formatRiskLevel(aigrcLevel)}`);
1311
+ console.log();
1312
+ console.log(
1313
+ chalk8.dim(" Profile".padEnd(24)) + chalk8.dim("Mapped Risk Level")
1314
+ );
1315
+ console.log(chalk8.dim(" " + "\u2500".repeat(50)));
1316
+ for (const c of classifications) {
1317
+ console.log(
1318
+ ` ${c.profileName.padEnd(22)} ${formatRiskLevel(c.mappedLevel)}`
1319
+ );
1320
+ }
1321
+ const riskOrder = ["minimal", "neither", "low", "limited", "medium", "moderate", "high", "rights-impacting", "critical", "safety-impacting", "unacceptable"];
1322
+ let strictestLevel = classifications[0]?.mappedLevel || aigrcLevel;
1323
+ let strictestIndex = riskOrder.indexOf(strictestLevel);
1324
+ for (const c of classifications) {
1325
+ const idx = riskOrder.indexOf(c.mappedLevel);
1326
+ if (idx > strictestIndex) {
1327
+ strictestIndex = idx;
1328
+ strictestLevel = c.mappedLevel;
1329
+ }
1330
+ }
1331
+ console.log();
1332
+ console.log(` ${chalk8.dim("Strictest:")} ${formatRiskLevel(strictestLevel)}`);
1333
+ if (options.update) {
1334
+ const updateSpinner = ora5("Updating asset card...").start();
1335
+ try {
1336
+ const jurisdictions = classifications.map((c) => ({
1337
+ jurisdictionId: c.profileId,
1338
+ riskLevel: c.mappedLevel,
1339
+ lastChecked: (/* @__PURE__ */ new Date()).toISOString()
1340
+ }));
1341
+ card.classification.jurisdictions = jurisdictions;
1342
+ saveAssetCard3(card, assetPath);
1343
+ updateSpinner.succeed("Asset card updated with classifications");
1344
+ } catch (error) {
1345
+ updateSpinner.fail("Failed to update asset card");
1346
+ printError(error instanceof Error ? error.message : String(error));
1347
+ }
1348
+ }
1349
+ }
1350
+ function formatRiskLevel(level) {
1351
+ const colors = {
1352
+ minimal: chalk8.green,
1353
+ neither: chalk8.green,
1354
+ low: chalk8.green,
1355
+ limited: chalk8.yellow,
1356
+ medium: chalk8.yellow,
1357
+ moderate: chalk8.yellow,
1358
+ high: chalk8.red,
1359
+ "rights-impacting": chalk8.red,
1360
+ critical: chalk8.magenta,
1361
+ "safety-impacting": chalk8.magenta,
1362
+ unacceptable: chalk8.magenta.bold
1363
+ };
1364
+ const colorFn = colors[level] || chalk8.white;
1365
+ return colorFn(level.toUpperCase());
1366
+ }
1367
+ function loadActiveProfiles2() {
1368
+ const configPath = join2(process.cwd(), ".aigrc.yaml");
1369
+ if (!existsSync2(configPath)) {
1370
+ return ["eu-ai-act"];
1371
+ }
1372
+ try {
1373
+ const content = readFileSync2(configPath, "utf-8");
1374
+ const config = YAML2.parse(content);
1375
+ return config?.profiles || ["eu-ai-act"];
1376
+ } catch {
1377
+ return ["eu-ai-act"];
1378
+ }
1379
+ }
1380
+
1381
+ // src/commands/check.ts
1382
+ import { Command as Command8 } from "commander";
1383
+ import chalk9 from "chalk";
1384
+ import ora6 from "ora";
1385
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
1386
+ import { join as join3 } from "path";
1387
+ import YAML3 from "yaml";
1388
+ import { loadAssetCard as loadAssetCard4 } from "@aigrc/core";
1389
+ var PROFILE_CONTROLS = {
1390
+ "eu-ai-act": [
1391
+ { id: "eu-art-9", name: "Risk Management System", category: "Risk Management", riskLevels: ["high"] },
1392
+ { id: "eu-art-10", name: "Data Governance", category: "Data Governance", riskLevels: ["high"] },
1393
+ { id: "eu-art-11", name: "Technical Documentation", category: "Documentation", riskLevels: ["high"] },
1394
+ { id: "eu-art-12", name: "Record Keeping", category: "Logging", riskLevels: ["high"] },
1395
+ { id: "eu-art-13", name: "Transparency", category: "Transparency", riskLevels: ["high", "limited"] },
1396
+ { id: "eu-art-14", name: "Human Oversight", category: "Oversight", riskLevels: ["high"] },
1397
+ { id: "eu-art-15", name: "Accuracy and Robustness", category: "Technical", riskLevels: ["high"] },
1398
+ { id: "eu-art-50", name: "Transparency Obligations", category: "Transparency", riskLevels: ["limited"] }
1399
+ ],
1400
+ "us-omb-m24": [
1401
+ { id: "us-aia", name: "AI Impact Assessment", category: "Assessment", riskLevels: ["rights-impacting", "safety-impacting"] },
1402
+ { id: "us-human-oversight", name: "Human Oversight", category: "Oversight", riskLevels: ["rights-impacting", "safety-impacting"] },
1403
+ { id: "us-equity", name: "Equity Assessment", category: "Equity", riskLevels: ["rights-impacting"] },
1404
+ { id: "us-notice", name: "Affected Individual Notice", category: "Transparency", riskLevels: ["rights-impacting"] },
1405
+ { id: "us-appeal", name: "Appeal Process", category: "Remediation", riskLevels: ["rights-impacting"] },
1406
+ { id: "us-safety-testing", name: "Safety Testing", category: "Testing", riskLevels: ["safety-impacting"] }
1407
+ ],
1408
+ "nist-ai-rmf": [
1409
+ { id: "nist-govern", name: "GOVERN Function", category: "Governance", riskLevels: ["minimal", "low", "moderate", "high", "critical"] },
1410
+ { id: "nist-map", name: "MAP Function", category: "Risk Mapping", riskLevels: ["low", "moderate", "high", "critical"] },
1411
+ { id: "nist-measure", name: "MEASURE Function", category: "Measurement", riskLevels: ["low", "moderate", "high", "critical"] },
1412
+ { id: "nist-manage", name: "MANAGE Function", category: "Management", riskLevels: ["low", "moderate", "high", "critical"] }
1413
+ ],
1414
+ "iso-42001": [
1415
+ { id: "iso-a5", name: "Leadership and Commitment", category: "Leadership", riskLevels: ["low", "medium", "high", "critical"] },
1416
+ { id: "iso-a6", name: "Planning", category: "Planning", riskLevels: ["low", "medium", "high", "critical"] },
1417
+ { id: "iso-a7", name: "Support", category: "Support", riskLevels: ["medium", "high", "critical"] },
1418
+ { id: "iso-a8", name: "Operation", category: "Operations", riskLevels: ["medium", "high", "critical"] },
1419
+ { id: "iso-a9", name: "Performance Evaluation", category: "Performance", riskLevels: ["medium", "high", "critical"] },
1420
+ { id: "iso-a10", name: "Improvement", category: "Improvement", riskLevels: ["high", "critical"] }
1421
+ ]
1422
+ };
1423
+ var RISK_MAPPINGS2 = {
1424
+ "eu-ai-act": { minimal: "minimal", limited: "limited", high: "high", unacceptable: "unacceptable" },
1425
+ "us-omb-m24": { minimal: "neither", limited: "neither", high: "rights-impacting", unacceptable: "safety-impacting" },
1426
+ "nist-ai-rmf": { minimal: "minimal", limited: "low", high: "high", unacceptable: "critical" },
1427
+ "iso-42001": { minimal: "low", limited: "medium", high: "high", unacceptable: "critical" }
1428
+ };
1429
+ var PROFILE_NAMES2 = {
1430
+ "eu-ai-act": "EU AI Act",
1431
+ "us-omb-m24": "US OMB M-24",
1432
+ "nist-ai-rmf": "NIST AI RMF",
1433
+ "iso-42001": "ISO/IEC 42001"
1434
+ };
1435
+ var checkCommand = new Command8("check").description("Check compliance status for an asset").argument("<assetPath>", "Path to asset card YAML file").option("-p, --profiles <profiles...>", "Profile IDs to check").option("-s, --stack", "Check against stacked profiles (strictest wins)").option("-o, --output <format>", "Output format (text, json, yaml)", "text").option("-v, --verbose", "Show detailed control status").action(async (assetPath, options) => {
1436
+ await runCheck(assetPath, options);
1437
+ });
1438
+ async function runCheck(assetPath, options) {
1439
+ if (options.output === "text") {
1440
+ printHeader();
1441
+ }
1442
+ if (!existsSync3(assetPath)) {
1443
+ printError(`Asset card not found: ${assetPath}`);
1444
+ process.exit(1);
1445
+ }
1446
+ const spinner = ora6("Loading asset card...").start();
1447
+ let card;
1448
+ try {
1449
+ card = loadAssetCard4(assetPath);
1450
+ spinner.succeed(`Loaded: ${card.name}`);
1451
+ } catch (error) {
1452
+ spinner.fail("Failed to load asset card");
1453
+ printError(error instanceof Error ? error.message : String(error));
1454
+ process.exit(1);
1455
+ }
1456
+ let profileIds;
1457
+ if (options.profiles && options.profiles.length > 0) {
1458
+ profileIds = options.profiles;
1459
+ } else {
1460
+ profileIds = loadActiveProfiles3();
1461
+ }
1462
+ const results = [];
1463
+ for (const profileId of profileIds) {
1464
+ const result = checkProfile(card, profileId);
1465
+ if (result) {
1466
+ results.push(result);
1467
+ }
1468
+ }
1469
+ if (options.output === "json") {
1470
+ console.log(JSON.stringify({ asset: card.name, results }, null, 2));
1471
+ return;
1472
+ }
1473
+ if (options.output === "yaml") {
1474
+ console.log(YAML3.stringify({ asset: card.name, results }));
1475
+ return;
1476
+ }
1477
+ console.log();
1478
+ printSubheader(`Compliance Check: ${card.name}`);
1479
+ console.log();
1480
+ console.log(
1481
+ chalk9.dim(" Profile".padEnd(20)) + chalk9.dim("Risk Level".padEnd(20)) + chalk9.dim("Compliant".padEnd(12)) + chalk9.dim("Score")
1482
+ );
1483
+ console.log(chalk9.dim(" " + "\u2500".repeat(60)));
1484
+ for (const r of results) {
1485
+ const compliantIcon = r.compliant ? chalk9.green("\u2713 Yes") : chalk9.red("\u2717 No");
1486
+ const scoreColor = r.percentage >= 80 ? chalk9.green : r.percentage >= 50 ? chalk9.yellow : chalk9.red;
1487
+ console.log(
1488
+ ` ${r.profileName.padEnd(18)} ${formatRiskLevel2(r.riskLevel).padEnd(28)} ${compliantIcon.padEnd(20)} ${scoreColor(r.percentage + "%")}`
1489
+ );
1490
+ }
1491
+ if (options.stack && results.length > 1) {
1492
+ console.log();
1493
+ const allCompliant = results.every((r) => r.compliant);
1494
+ const avgPercentage = Math.round(results.reduce((acc, r) => acc + r.percentage, 0) / results.length);
1495
+ console.log(
1496
+ ` ${chalk9.bold("STACKED")}`.padEnd(18) + `${chalk9.dim("(strictest)")}`.padEnd(28) + `${allCompliant ? chalk9.green("\u2713 Yes") : chalk9.red("\u2717 No")}`.padEnd(20) + `${avgPercentage}%`
1497
+ );
1498
+ }
1499
+ if (options.verbose) {
1500
+ for (const r of results) {
1501
+ console.log();
1502
+ printSubheader(`${r.profileName} Details`);
1503
+ console.log();
1504
+ console.log(
1505
+ chalk9.dim(" Control".padEnd(32)) + chalk9.dim("Status")
1506
+ );
1507
+ console.log(chalk9.dim(" " + "\u2500".repeat(50)));
1508
+ for (const c of r.controls) {
1509
+ const statusIcon = getStatusIcon(c.status);
1510
+ console.log(` ${c.controlName.padEnd(30)} ${statusIcon}`);
1511
+ }
1512
+ if (r.gaps.length > 0) {
1513
+ console.log();
1514
+ printWarning(`${r.gaps.length} gap(s) identified:`);
1515
+ for (const gap of r.gaps) {
1516
+ console.log(chalk9.yellow(` - ${gap}`));
1517
+ }
1518
+ }
1519
+ }
1520
+ } else {
1521
+ const allGaps = [];
1522
+ for (const r of results) {
1523
+ allGaps.push(...r.gaps);
1524
+ }
1525
+ if (allGaps.length > 0) {
1526
+ console.log();
1527
+ printWarning(`${allGaps.length} total gap(s) identified. Use --verbose for details.`);
1528
+ }
1529
+ }
1530
+ }
1531
+ function checkProfile(card, profileId) {
1532
+ const controls = PROFILE_CONTROLS[profileId];
1533
+ const mapping = RISK_MAPPINGS2[profileId];
1534
+ if (!controls || !mapping) {
1535
+ return null;
1536
+ }
1537
+ const aigrcLevel = card.classification.riskLevel;
1538
+ const mappedLevel = mapping[aigrcLevel] || aigrcLevel;
1539
+ const applicableControls = controls.filter((c) => c.riskLevels.includes(mappedLevel));
1540
+ const controlStatuses = applicableControls.map((control) => {
1541
+ const status = evaluateControl(card, control);
1542
+ return {
1543
+ controlId: control.id,
1544
+ controlName: control.name,
1545
+ status,
1546
+ notes: status === "not_implemented" ? "No evidence found" : void 0
1547
+ };
1548
+ });
1549
+ const implemented = controlStatuses.filter((c) => c.status === "implemented" || c.status === "not_applicable").length;
1550
+ const total = controlStatuses.filter((c) => c.status !== "not_applicable").length;
1551
+ const percentage = total > 0 ? Math.round(implemented / total * 100) : 100;
1552
+ const gaps = [];
1553
+ for (const c of controlStatuses) {
1554
+ if (c.status === "not_implemented") {
1555
+ gaps.push(`Missing: ${c.controlName}`);
1556
+ } else if (c.status === "partial") {
1557
+ gaps.push(`Partial: ${c.controlName}`);
1558
+ }
1559
+ }
1560
+ return {
1561
+ profileId,
1562
+ profileName: PROFILE_NAMES2[profileId] || profileId,
1563
+ riskLevel: mappedLevel,
1564
+ compliant: percentage === 100,
1565
+ percentage,
1566
+ controls: controlStatuses,
1567
+ gaps
1568
+ };
1569
+ }
1570
+ function evaluateControl(card, control) {
1571
+ const hasApprovals = card.governance.approvals.length > 0;
1572
+ const hasIntent = card.intent.linked;
1573
+ const hasConstraints = !!card.constraints;
1574
+ const categoryEvidence = {
1575
+ "Risk Management": hasApprovals || hasIntent,
1576
+ "Data Governance": hasConstraints,
1577
+ Documentation: hasIntent && !!card.intent.businessJustification,
1578
+ Logging: hasConstraints && card.constraints?.monitoring?.logAllDecisions,
1579
+ Transparency: card.intent.linked,
1580
+ Oversight: hasApprovals || hasConstraints && card.constraints?.humanApprovalRequired?.length,
1581
+ Technical: hasConstraints,
1582
+ Assessment: hasIntent && !!card.intent.businessJustification,
1583
+ Governance: hasApprovals,
1584
+ Planning: hasIntent,
1585
+ Leadership: hasApprovals,
1586
+ Support: card.ownership.team !== void 0,
1587
+ Operations: hasConstraints,
1588
+ Performance: hasConstraints && card.constraints?.monitoring?.logAllDecisions,
1589
+ Improvement: hasApprovals && card.governance.status !== "draft",
1590
+ Equity: hasIntent && !!card.intent.businessJustification,
1591
+ Remediation: hasApprovals,
1592
+ Testing: hasConstraints,
1593
+ Measurement: hasConstraints && card.constraints?.monitoring,
1594
+ Management: hasApprovals || hasIntent,
1595
+ "Risk Mapping": hasIntent
1596
+ };
1597
+ const hasEvidence = categoryEvidence[control.category] ?? false;
1598
+ if (hasEvidence) {
1599
+ return "implemented";
1600
+ }
1601
+ return "not_implemented";
1602
+ }
1603
+ function getStatusIcon(status) {
1604
+ switch (status) {
1605
+ case "implemented":
1606
+ return chalk9.green("\u2713 Implemented");
1607
+ case "partial":
1608
+ return chalk9.yellow("\u25D0 Partial");
1609
+ case "not_implemented":
1610
+ return chalk9.red("\u2717 Missing");
1611
+ case "not_applicable":
1612
+ return chalk9.gray("- N/A");
1613
+ }
1614
+ }
1615
+ function formatRiskLevel2(level) {
1616
+ const colors = {
1617
+ minimal: chalk9.green,
1618
+ neither: chalk9.green,
1619
+ low: chalk9.green,
1620
+ limited: chalk9.yellow,
1621
+ medium: chalk9.yellow,
1622
+ moderate: chalk9.yellow,
1623
+ high: chalk9.red,
1624
+ "rights-impacting": chalk9.red,
1625
+ critical: chalk9.magenta,
1626
+ "safety-impacting": chalk9.magenta,
1627
+ unacceptable: chalk9.magenta.bold
1628
+ };
1629
+ const colorFn = colors[level] || chalk9.white;
1630
+ return colorFn(level.toUpperCase());
1631
+ }
1632
+ function loadActiveProfiles3() {
1633
+ const configPath = join3(process.cwd(), ".aigrc.yaml");
1634
+ if (!existsSync3(configPath)) {
1635
+ return ["eu-ai-act"];
1636
+ }
1637
+ try {
1638
+ const content = readFileSync3(configPath, "utf-8");
1639
+ const config = YAML3.parse(content);
1640
+ return config?.profiles || ["eu-ai-act"];
1641
+ } catch {
1642
+ return ["eu-ai-act"];
1643
+ }
1644
+ }
1645
+
1646
+ // src/commands/generate.ts
1647
+ import { Command as Command9 } from "commander";
1648
+ import ora7 from "ora";
1649
+ import { existsSync as existsSync4, mkdirSync, writeFileSync as writeFileSync3, readFileSync as readFileSync4 } from "fs";
1650
+ import { join as join4 } from "path";
1651
+ import YAML4 from "yaml";
1652
+ import { loadAssetCard as loadAssetCard5 } from "@aigrc/core";
1653
+ var ARTIFACT_TEMPLATES = {
1654
+ "eu-ai-act": [
1655
+ { id: "risk-management-plan", name: "Risk Management Plan", requiredFor: ["high"], format: "md" },
1656
+ { id: "technical-documentation", name: "Technical Documentation", requiredFor: ["high"], format: "md" },
1657
+ { id: "conformity-declaration", name: "EU Declaration of Conformity", requiredFor: ["high"], format: "md" }
1658
+ ],
1659
+ "us-omb-m24": [
1660
+ { id: "ai-impact-assessment", name: "AI Impact Assessment", requiredFor: ["rights-impacting", "safety-impacting"], format: "md" },
1661
+ { id: "equity-assessment", name: "Equity Assessment", requiredFor: ["rights-impacting"], format: "md" }
1662
+ ],
1663
+ "nist-ai-rmf": [
1664
+ { id: "nist-profile", name: "NIST AI RMF Profile", requiredFor: ["moderate", "high", "critical"], format: "yaml" },
1665
+ { id: "nist-playbook", name: "NIST AI RMF Playbook", requiredFor: ["high", "critical"], format: "md" }
1666
+ ],
1667
+ "iso-42001": [
1668
+ { id: "aims-policy", name: "AIMS Policy", requiredFor: ["low", "medium", "high", "critical"], format: "md" },
1669
+ { id: "aims-manual", name: "AIMS Manual", requiredFor: ["high", "critical"], format: "md" },
1670
+ { id: "risk-treatment-plan", name: "Risk Treatment Plan", requiredFor: ["medium", "high", "critical"], format: "md" }
1671
+ ]
1672
+ };
1673
+ var RISK_MAPPINGS3 = {
1674
+ "eu-ai-act": { minimal: "minimal", limited: "limited", high: "high", unacceptable: "unacceptable" },
1675
+ "us-omb-m24": { minimal: "neither", limited: "neither", high: "rights-impacting", unacceptable: "safety-impacting" },
1676
+ "nist-ai-rmf": { minimal: "minimal", limited: "low", high: "high", unacceptable: "critical" },
1677
+ "iso-42001": { minimal: "low", limited: "medium", high: "high", unacceptable: "critical" }
1678
+ };
1679
+ var PROFILE_NAMES3 = {
1680
+ "eu-ai-act": "EU AI Act",
1681
+ "us-omb-m24": "US OMB M-24",
1682
+ "nist-ai-rmf": "NIST AI RMF",
1683
+ "iso-42001": "ISO/IEC 42001"
1684
+ };
1685
+ var generateCommand = new Command9("generate").description("Generate compliance artifacts from templates").argument("<assetPath>", "Path to asset card YAML file").option("-p, --profile <profile>", "Profile ID for templates").option("-t, --template <template>", "Specific template ID to generate").option("-a, --all", "Generate all required artifacts").option("-o, --output-dir <dir>", "Output directory", ".aigrc/artifacts").option("-f, --force", "Overwrite existing files").action(async (assetPath, options) => {
1686
+ await runGenerate(assetPath, options);
1687
+ });
1688
+ async function runGenerate(assetPath, options) {
1689
+ printHeader();
1690
+ if (!existsSync4(assetPath)) {
1691
+ printError(`Asset card not found: ${assetPath}`);
1692
+ process.exit(1);
1693
+ }
1694
+ const spinner = ora7("Loading asset card...").start();
1695
+ let card;
1696
+ try {
1697
+ card = loadAssetCard5(assetPath);
1698
+ spinner.succeed(`Loaded: ${card.name}`);
1699
+ } catch (error) {
1700
+ spinner.fail("Failed to load asset card");
1701
+ printError(error instanceof Error ? error.message : String(error));
1702
+ process.exit(1);
1703
+ }
1704
+ const profileId = options.profile || loadActiveProfiles4()[0];
1705
+ const templates = ARTIFACT_TEMPLATES[profileId];
1706
+ if (!templates) {
1707
+ printError(`Unknown profile: ${profileId}`);
1708
+ printInfo(`Available profiles: ${Object.keys(ARTIFACT_TEMPLATES).join(", ")}`);
1709
+ process.exit(1);
1710
+ }
1711
+ const mapping = RISK_MAPPINGS3[profileId];
1712
+ const mappedLevel = mapping[card.classification.riskLevel] || card.classification.riskLevel;
1713
+ let templatesToGenerate = templates.filter((t) => t.requiredFor.includes(mappedLevel));
1714
+ if (options.template) {
1715
+ const specific = templates.find((t) => t.id === options.template);
1716
+ if (!specific) {
1717
+ printError(`Template not found: ${options.template}`);
1718
+ printInfo(`Available templates: ${templates.map((t) => t.id).join(", ")}`);
1719
+ process.exit(1);
1720
+ }
1721
+ templatesToGenerate = [specific];
1722
+ }
1723
+ if (!options.all && !options.template && templatesToGenerate.length === 0) {
1724
+ printInfo(`No artifacts required for risk level: ${mappedLevel}`);
1725
+ printInfo("Use --all to generate all available templates");
1726
+ return;
1727
+ }
1728
+ if (options.all) {
1729
+ templatesToGenerate = templates;
1730
+ }
1731
+ console.log();
1732
+ printSubheader(`Generating Artifacts (${PROFILE_NAMES3[profileId]})`);
1733
+ console.log();
1734
+ printInfo(`Risk Level: ${mappedLevel}`);
1735
+ printInfo(`Output Directory: ${options.outputDir}`);
1736
+ console.log();
1737
+ const outputDir = join4(process.cwd(), options.outputDir || ".aigrc/artifacts", profileId);
1738
+ if (!existsSync4(outputDir)) {
1739
+ mkdirSync(outputDir, { recursive: true });
1740
+ }
1741
+ let generated = 0;
1742
+ let skipped = 0;
1743
+ for (const template of templatesToGenerate) {
1744
+ const filename = `${template.id}.${template.format}`;
1745
+ const outputPath = join4(outputDir, filename);
1746
+ if (existsSync4(outputPath) && !options.force) {
1747
+ printWarning(`Skipped (exists): ${filename}`);
1748
+ skipped++;
1749
+ continue;
1750
+ }
1751
+ const content = generateTemplateContent(card, profileId, template);
1752
+ writeFileSync3(outputPath, content, "utf-8");
1753
+ printSuccess(`Generated: ${filename}`);
1754
+ generated++;
1755
+ }
1756
+ console.log();
1757
+ printInfo(`Generated: ${generated}, Skipped: ${skipped}`);
1758
+ if (skipped > 0) {
1759
+ printInfo("Use --force to overwrite existing files");
1760
+ }
1761
+ }
1762
+ function generateTemplateContent(card, profileId, template) {
1763
+ const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1764
+ if (template.format === "yaml") {
1765
+ return generateYamlTemplate(card, profileId, template);
1766
+ }
1767
+ return `# ${template.name}
1768
+
1769
+ ## Document Information
1770
+
1771
+ | Field | Value |
1772
+ |-------|-------|
1773
+ | Asset Name | ${card.name} |
1774
+ | Asset ID | ${card.id} |
1775
+ | Profile | ${PROFILE_NAMES3[profileId]} |
1776
+ | Generated | ${now} |
1777
+ | Version | 1.0 |
1778
+
1779
+ ---
1780
+
1781
+ ## 1. Overview
1782
+
1783
+ ${card.description || "No description provided."}
1784
+
1785
+ ### 1.1 Asset Classification
1786
+
1787
+ - **Risk Level:** ${card.classification.riskLevel.toUpperCase()}
1788
+ - **Asset Type:** ${card.technical.type}
1789
+ - **Framework:** ${card.technical.framework || "Not specified"}
1790
+
1791
+ ### 1.2 Ownership
1792
+
1793
+ - **Owner:** ${card.ownership.owner.name}
1794
+ - **Email:** ${card.ownership.owner.email}
1795
+ - **Team:** ${card.ownership.team || "Not specified"}
1796
+
1797
+ ---
1798
+
1799
+ ## 2. ${getSection2Title(template.id)}
1800
+
1801
+ ${getSection2Content(template.id, card)}
1802
+
1803
+ ---
1804
+
1805
+ ## 3. Risk Factors
1806
+
1807
+ | Factor | Status |
1808
+ |--------|--------|
1809
+ | Autonomous Decisions | ${card.classification.riskFactors.autonomousDecisions ? "Yes" : "No"} |
1810
+ | Customer Facing | ${card.classification.riskFactors.customerFacing ? "Yes" : "No"} |
1811
+ | Tool Execution | ${card.classification.riskFactors.toolExecution ? "Yes" : "No"} |
1812
+ | External Data Access | ${card.classification.riskFactors.externalDataAccess ? "Yes" : "No"} |
1813
+ | PII Processing | ${card.classification.riskFactors.piiProcessing} |
1814
+ | High Stakes Decisions | ${card.classification.riskFactors.highStakesDecisions ? "Yes" : "No"} |
1815
+
1816
+ ---
1817
+
1818
+ ## 4. Governance Status
1819
+
1820
+ - **Status:** ${card.governance.status}
1821
+ - **Approvals:** ${card.governance.approvals.length}
1822
+
1823
+ ---
1824
+
1825
+ ## 5. Next Steps
1826
+
1827
+ - [ ] Review and complete all sections
1828
+ - [ ] Obtain required approvals
1829
+ - [ ] Update asset card with artifact path
1830
+ - [ ] Schedule periodic review
1831
+
1832
+ ---
1833
+
1834
+ *This document was auto-generated by AIGRC CLI. Review and customize as needed.*
1835
+ `;
1836
+ }
1837
+ function generateYamlTemplate(card, profileId, template) {
1838
+ const content = {
1839
+ $schema: `https://aigrc.dev/schemas/${template.id}/v1`,
1840
+ metadata: {
1841
+ assetId: card.id,
1842
+ assetName: card.name,
1843
+ profile: profileId,
1844
+ generated: (/* @__PURE__ */ new Date()).toISOString(),
1845
+ version: "1.0"
1846
+ },
1847
+ classification: {
1848
+ riskLevel: card.classification.riskLevel,
1849
+ riskFactors: card.classification.riskFactors
1850
+ },
1851
+ governance: {
1852
+ status: card.governance.status,
1853
+ approvalCount: card.governance.approvals.length
1854
+ },
1855
+ // Profile-specific sections would go here
1856
+ implementation: {
1857
+ status: "draft",
1858
+ completedSections: [],
1859
+ pendingSections: ["All sections require review"]
1860
+ }
1861
+ };
1862
+ return YAML4.stringify(content, { indent: 2 });
1863
+ }
1864
+ function getSection2Title(templateId) {
1865
+ const titles = {
1866
+ "risk-management-plan": "Risk Management",
1867
+ "technical-documentation": "Technical Specification",
1868
+ "conformity-declaration": "Declaration of Conformity",
1869
+ "ai-impact-assessment": "Impact Assessment",
1870
+ "equity-assessment": "Equity Analysis",
1871
+ "nist-playbook": "Implementation Playbook",
1872
+ "aims-policy": "AI Management Policy",
1873
+ "aims-manual": "Management System",
1874
+ "risk-treatment-plan": "Risk Treatment"
1875
+ };
1876
+ return titles[templateId] || "Details";
1877
+ }
1878
+ function getSection2Content(templateId, card) {
1879
+ const content = {
1880
+ "risk-management-plan": `
1881
+ ### 2.1 Risk Identification
1882
+
1883
+ *Document identified risks associated with the AI system.*
1884
+
1885
+ ### 2.2 Risk Analysis
1886
+
1887
+ *Analyze the likelihood and impact of identified risks.*
1888
+
1889
+ ### 2.3 Risk Mitigation
1890
+
1891
+ *Define mitigation strategies for each risk.*
1892
+
1893
+ ### 2.4 Monitoring
1894
+
1895
+ *Define ongoing monitoring procedures.*
1896
+ `,
1897
+ "technical-documentation": `
1898
+ ### 2.1 System Architecture
1899
+
1900
+ *Describe the technical architecture of the AI system.*
1901
+
1902
+ ### 2.2 Training Data
1903
+
1904
+ *Document training data sources, quality measures, and governance.*
1905
+
1906
+ ### 2.3 Model Performance
1907
+
1908
+ *Document accuracy, robustness, and performance metrics.*
1909
+
1910
+ ### 2.4 Integration Points
1911
+
1912
+ *Document integration with other systems.*
1913
+ `,
1914
+ "ai-impact-assessment": `
1915
+ ### 2.1 Purpose and Scope
1916
+
1917
+ *Define the purpose and intended use of the AI system.*
1918
+
1919
+ ### 2.2 Affected Populations
1920
+
1921
+ *Identify populations affected by AI decisions.*
1922
+
1923
+ ### 2.3 Impact Analysis
1924
+
1925
+ *Analyze potential impacts on affected populations.*
1926
+
1927
+ ### 2.4 Mitigation Measures
1928
+
1929
+ *Document measures to mitigate negative impacts.*
1930
+ `
1931
+ };
1932
+ return content[templateId] || `
1933
+ ### 2.1 Purpose
1934
+
1935
+ *Define the purpose of this document.*
1936
+
1937
+ ### 2.2 Scope
1938
+
1939
+ *Define the scope and applicability.*
1940
+
1941
+ ### 2.3 Implementation
1942
+
1943
+ *Document implementation details.*
1944
+ `;
1945
+ }
1946
+ function loadActiveProfiles4() {
1947
+ const configPath = join4(process.cwd(), ".aigrc.yaml");
1948
+ if (!existsSync4(configPath)) {
1949
+ return ["eu-ai-act"];
1950
+ }
1951
+ try {
1952
+ const content = readFileSync4(configPath, "utf-8");
1953
+ const config = YAML4.parse(content);
1954
+ return config?.profiles || ["eu-ai-act"];
1955
+ } catch {
1956
+ return ["eu-ai-act"];
1957
+ }
1958
+ }
1959
+
1960
+ // src/commands/report.ts
1961
+ import { Command as Command10 } from "commander";
1962
+ import ora8 from "ora";
1963
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, writeFileSync as writeFileSync4, readFileSync as readFileSync5, readdirSync } from "fs";
1964
+ import { join as join5 } from "path";
1965
+ import YAML5 from "yaml";
1966
+ import { loadAssetCard as loadAssetCard6 } from "@aigrc/core";
1967
+ var PROFILE_NAMES4 = {
1968
+ "eu-ai-act": "EU AI Act",
1969
+ "us-omb-m24": "US OMB M-24",
1970
+ "nist-ai-rmf": "NIST AI RMF",
1971
+ "iso-42001": "ISO/IEC 42001"
1972
+ };
1973
+ var RISK_MAPPINGS4 = {
1974
+ "eu-ai-act": { minimal: "minimal", limited: "limited", high: "high", unacceptable: "unacceptable" },
1975
+ "us-omb-m24": { minimal: "neither", limited: "neither", high: "rights-impacting", unacceptable: "safety-impacting" },
1976
+ "nist-ai-rmf": { minimal: "minimal", limited: "low", high: "high", unacceptable: "critical" },
1977
+ "iso-42001": { minimal: "low", limited: "medium", high: "high", unacceptable: "critical" }
1978
+ };
1979
+ var CONTROL_CROSSWALK = {
1980
+ risk_management: {
1981
+ "eu-ai-act": ["eu-art-9"],
1982
+ "us-omb-m24": ["us-aia"],
1983
+ "nist-ai-rmf": ["nist-govern", "nist-manage"],
1984
+ "iso-42001": ["iso-a6"]
1985
+ },
1986
+ human_oversight: {
1987
+ "eu-ai-act": ["eu-art-14"],
1988
+ "us-omb-m24": ["us-human-oversight"],
1989
+ "nist-ai-rmf": ["nist-govern"],
1990
+ "iso-42001": ["iso-a8"]
1991
+ },
1992
+ transparency: {
1993
+ "eu-ai-act": ["eu-art-13", "eu-art-50"],
1994
+ "us-omb-m24": ["us-notice"],
1995
+ "nist-ai-rmf": ["nist-govern"],
1996
+ "iso-42001": ["iso-a7"]
1997
+ },
1998
+ testing: {
1999
+ "eu-ai-act": ["eu-art-15"],
2000
+ "us-omb-m24": ["us-safety-testing"],
2001
+ "nist-ai-rmf": ["nist-measure"],
2002
+ "iso-42001": ["iso-a9"]
2003
+ },
2004
+ data_governance: {
2005
+ "eu-ai-act": ["eu-art-10"],
2006
+ "nist-ai-rmf": ["nist-map"],
2007
+ "iso-42001": ["iso-a8"]
2008
+ }
2009
+ };
2010
+ var reportCommand = new Command10("report").description("Generate compliance reports").addCommand(
2011
+ new Command10("gap").description("Generate gap analysis report").argument("[assetPath]", "Path to asset card (or all in .aigrc/cards)").option("-p, --profiles <profiles...>", "Profile IDs to analyze").option("-o, --output <file>", "Output file path").option("-f, --format <format>", "Format (md, json, yaml)", "md").action(async (assetPath, options) => {
2012
+ await generateGapReport(assetPath, options);
2013
+ })
2014
+ ).addCommand(
2015
+ new Command10("crosswalk").description("Generate cross-framework mapping report").argument("<assetPath>", "Path to asset card").option("-p, --profiles <profiles...>", "Profile IDs to include").option("-o, --output <file>", "Output file path").option("-f, --format <format>", "Format (md, json, yaml)", "md").action(async (assetPath, options) => {
2016
+ await generateCrosswalkReport(assetPath, options);
2017
+ })
2018
+ ).addCommand(
2019
+ new Command10("audit").description("Generate audit package").option("-p, --profile <profile>", "Profile ID", "eu-ai-act").option("-o, --output <dir>", "Output directory").option("--include-assets <assets...>", "Specific asset card paths").action(async (options) => {
2020
+ await generateAuditPackage(options);
2021
+ })
2022
+ );
2023
+ async function generateGapReport(assetPath, options) {
2024
+ printHeader();
2025
+ const spinner = ora8("Loading assets...").start();
2026
+ let cards = [];
2027
+ if (assetPath) {
2028
+ try {
2029
+ const card = loadAssetCard6(assetPath);
2030
+ cards.push({ path: assetPath, card });
2031
+ } catch (error) {
2032
+ spinner.fail("Failed to load asset card");
2033
+ printError(error instanceof Error ? error.message : String(error));
2034
+ process.exit(1);
2035
+ }
2036
+ } else {
2037
+ const cardsDir = join5(process.cwd(), ".aigrc/cards");
2038
+ if (existsSync5(cardsDir)) {
2039
+ const files = readdirSync(cardsDir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
2040
+ for (const file of files) {
2041
+ try {
2042
+ const card = loadAssetCard6(join5(cardsDir, file));
2043
+ cards.push({ path: join5(cardsDir, file), card });
2044
+ } catch {
2045
+ }
2046
+ }
2047
+ }
2048
+ }
2049
+ if (cards.length === 0) {
2050
+ spinner.fail("No asset cards found");
2051
+ process.exit(1);
2052
+ }
2053
+ spinner.succeed(`Loaded ${cards.length} asset(s)`);
2054
+ const profileIds = options.profiles || loadActiveProfiles5();
2055
+ const gaps = [];
2056
+ for (const { card } of cards) {
2057
+ const assetGaps = {
2058
+ assetId: card.id,
2059
+ assetName: card.name,
2060
+ profiles: []
2061
+ };
2062
+ for (const profileId of profileIds) {
2063
+ const mapping = RISK_MAPPINGS4[profileId];
2064
+ if (!mapping) continue;
2065
+ const mappedLevel = mapping[card.classification.riskLevel] || card.classification.riskLevel;
2066
+ const gapList = [];
2067
+ if (card.governance.approvals.length === 0) {
2068
+ gapList.push({ control: "Governance Approvals", severity: "high" });
2069
+ }
2070
+ if (!card.intent.linked) {
2071
+ gapList.push({ control: "Golden Thread Link", severity: "medium" });
2072
+ }
2073
+ if (!card.intent.businessJustification) {
2074
+ gapList.push({ control: "Business Justification", severity: "medium" });
2075
+ }
2076
+ assetGaps.profiles.push({
2077
+ profileId,
2078
+ riskLevel: mappedLevel,
2079
+ gaps: gapList
2080
+ });
2081
+ }
2082
+ gaps.push(assetGaps);
2083
+ }
2084
+ const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
2085
+ if (options.format === "json") {
2086
+ const output = JSON.stringify({ generated: now, gaps }, null, 2);
2087
+ if (options.output) {
2088
+ writeFileSync4(options.output, output, "utf-8");
2089
+ printSuccess(`Report saved to: ${options.output}`);
2090
+ } else {
2091
+ console.log(output);
2092
+ }
2093
+ return;
2094
+ }
2095
+ if (options.format === "yaml") {
2096
+ const output = YAML5.stringify({ generated: now, gaps });
2097
+ if (options.output) {
2098
+ writeFileSync4(options.output, output, "utf-8");
2099
+ printSuccess(`Report saved to: ${options.output}`);
2100
+ } else {
2101
+ console.log(output);
2102
+ }
2103
+ return;
2104
+ }
2105
+ let md = `# Gap Analysis Report
2106
+
2107
+ **Generated:** ${now}
2108
+ **Assets Analyzed:** ${cards.length}
2109
+ **Profiles:** ${profileIds.join(", ")}
2110
+
2111
+ ---
2112
+
2113
+ `;
2114
+ for (const assetGaps of gaps) {
2115
+ md += `## ${assetGaps.assetName}
2116
+
2117
+ `;
2118
+ md += `**Asset ID:** ${assetGaps.assetId}
2119
+
2120
+ `;
2121
+ for (const profile of assetGaps.profiles) {
2122
+ md += `### ${PROFILE_NAMES4[profile.profileId] || profile.profileId}
2123
+
2124
+ `;
2125
+ md += `**Risk Level:** ${profile.riskLevel}
2126
+
2127
+ `;
2128
+ if (profile.gaps.length === 0) {
2129
+ md += "No gaps identified.\n\n";
2130
+ } else {
2131
+ md += "| Gap | Severity |\n";
2132
+ md += "|-----|----------|\n";
2133
+ for (const gap of profile.gaps) {
2134
+ md += `| ${gap.control} | ${gap.severity.toUpperCase()} |
2135
+ `;
2136
+ }
2137
+ md += "\n";
2138
+ }
2139
+ }
2140
+ md += "---\n\n";
2141
+ }
2142
+ if (options.output) {
2143
+ writeFileSync4(options.output, md, "utf-8");
2144
+ printSuccess(`Report saved to: ${options.output}`);
2145
+ } else {
2146
+ console.log(md);
2147
+ }
2148
+ }
2149
+ async function generateCrosswalkReport(assetPath, options) {
2150
+ printHeader();
2151
+ if (!existsSync5(assetPath)) {
2152
+ printError(`Asset card not found: ${assetPath}`);
2153
+ process.exit(1);
2154
+ }
2155
+ let card;
2156
+ try {
2157
+ card = loadAssetCard6(assetPath);
2158
+ } catch (error) {
2159
+ printError(error instanceof Error ? error.message : String(error));
2160
+ process.exit(1);
2161
+ }
2162
+ const profileIds = options.profiles || Object.keys(RISK_MAPPINGS4);
2163
+ const classifications = [];
2164
+ for (const profileId of profileIds) {
2165
+ const mapping = RISK_MAPPINGS4[profileId];
2166
+ if (!mapping) continue;
2167
+ const mappedLevel = mapping[card.classification.riskLevel] || card.classification.riskLevel;
2168
+ classifications.push({
2169
+ profileId,
2170
+ profileName: PROFILE_NAMES4[profileId] || profileId,
2171
+ riskLevel: mappedLevel
2172
+ });
2173
+ }
2174
+ const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
2175
+ if (options.format === "json") {
2176
+ const output = JSON.stringify(
2177
+ {
2178
+ generated: now,
2179
+ asset: { id: card.id, name: card.name },
2180
+ aigrcRiskLevel: card.classification.riskLevel,
2181
+ classifications,
2182
+ controlCrosswalk: CONTROL_CROSSWALK
2183
+ },
2184
+ null,
2185
+ 2
2186
+ );
2187
+ if (options.output) {
2188
+ writeFileSync4(options.output, output, "utf-8");
2189
+ printSuccess(`Report saved to: ${options.output}`);
2190
+ } else {
2191
+ console.log(output);
2192
+ }
2193
+ return;
2194
+ }
2195
+ if (options.format === "yaml") {
2196
+ const output = YAML5.stringify({
2197
+ generated: now,
2198
+ asset: { id: card.id, name: card.name },
2199
+ aigrcRiskLevel: card.classification.riskLevel,
2200
+ classifications,
2201
+ controlCrosswalk: CONTROL_CROSSWALK
2202
+ });
2203
+ if (options.output) {
2204
+ writeFileSync4(options.output, output, "utf-8");
2205
+ printSuccess(`Report saved to: ${options.output}`);
2206
+ } else {
2207
+ console.log(output);
2208
+ }
2209
+ return;
2210
+ }
2211
+ let md = `# Cross-Framework Mapping Report
2212
+
2213
+ **Generated:** ${now}
2214
+ **Asset:** ${card.name} (${card.id})
2215
+ **AIGRC Risk Level:** ${card.classification.riskLevel.toUpperCase()}
2216
+
2217
+ ---
2218
+
2219
+ ## Risk Level Mapping
2220
+
2221
+ | Framework | Risk Level |
2222
+ |-----------|------------|
2223
+ `;
2224
+ for (const c of classifications) {
2225
+ md += `| ${c.profileName} | ${c.riskLevel.toUpperCase()} |
2226
+ `;
2227
+ }
2228
+ md += `
2229
+ ---
2230
+
2231
+ ## Control Crosswalk
2232
+
2233
+ | Category | EU AI Act | US OMB | NIST AI RMF | ISO 42001 |
2234
+ |----------|-----------|--------|-------------|-----------|
2235
+ `;
2236
+ for (const [category, mappings] of Object.entries(CONTROL_CROSSWALK)) {
2237
+ const eu = mappings["eu-ai-act"]?.join(", ") || "-";
2238
+ const us = mappings["us-omb-m24"]?.join(", ") || "-";
2239
+ const nist = mappings["nist-ai-rmf"]?.join(", ") || "-";
2240
+ const iso = mappings["iso-42001"]?.join(", ") || "-";
2241
+ md += `| ${category.replace(/_/g, " ")} | ${eu} | ${us} | ${nist} | ${iso} |
2242
+ `;
2243
+ }
2244
+ md += `
2245
+ ---
2246
+
2247
+ *This report shows how controls and risk levels map across different regulatory frameworks.*
2248
+ `;
2249
+ if (options.output) {
2250
+ writeFileSync4(options.output, md, "utf-8");
2251
+ printSuccess(`Report saved to: ${options.output}`);
2252
+ } else {
2253
+ console.log(md);
2254
+ }
2255
+ }
2256
+ async function generateAuditPackage(options) {
2257
+ printHeader();
2258
+ printSubheader("Generating Audit Package");
2259
+ const spinner = ora8("Collecting assets...").start();
2260
+ let cardPaths = [];
2261
+ if (options.includeAssets && options.includeAssets.length > 0) {
2262
+ cardPaths = options.includeAssets;
2263
+ } else {
2264
+ const cardsDir = join5(process.cwd(), ".aigrc/cards");
2265
+ if (existsSync5(cardsDir)) {
2266
+ cardPaths = readdirSync(cardsDir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml")).map((f) => join5(cardsDir, f));
2267
+ }
2268
+ }
2269
+ if (cardPaths.length === 0) {
2270
+ spinner.fail("No asset cards found");
2271
+ process.exit(1);
2272
+ }
2273
+ const cards = [];
2274
+ for (const path6 of cardPaths) {
2275
+ try {
2276
+ cards.push(loadAssetCard6(path6));
2277
+ } catch {
2278
+ }
2279
+ }
2280
+ spinner.succeed(`Found ${cards.length} asset(s)`);
2281
+ const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
2282
+ const outputDir = options.output || join5(process.cwd(), `.aigrc/audit-${now}`);
2283
+ mkdirSync2(outputDir, { recursive: true });
2284
+ const profileId = options.profile || "eu-ai-act";
2285
+ const profileName = PROFILE_NAMES4[profileId] || profileId;
2286
+ const summary = `# Audit Package: ${profileName}
2287
+
2288
+ **Generated:** ${now}
2289
+ **Profile:** ${profileName}
2290
+ **Total Assets:** ${cards.length}
2291
+
2292
+ ## Executive Summary
2293
+
2294
+ This audit package contains compliance documentation for ${cards.length} AI asset(s) under the ${profileName} framework.
2295
+
2296
+ ## Assets Included
2297
+
2298
+ | Asset Name | Risk Level | Governance Status |
2299
+ |------------|------------|-------------------|
2300
+ ${cards.map((c) => `| ${c.name} | ${c.classification.riskLevel} | ${c.governance.status} |`).join("\n")}
2301
+
2302
+ ## Next Steps
2303
+
2304
+ 1. Review individual asset cards
2305
+ 2. Complete gap analysis
2306
+ 3. Generate required artifacts
2307
+ 4. Obtain necessary approvals
2308
+ `;
2309
+ writeFileSync4(join5(outputDir, "README.md"), summary, "utf-8");
2310
+ const assetsDir = join5(outputDir, "assets");
2311
+ mkdirSync2(assetsDir, { recursive: true });
2312
+ for (const card of cards) {
2313
+ const filename = `${card.id}.yaml`;
2314
+ writeFileSync4(join5(assetsDir, filename), YAML5.stringify(card, { indent: 2 }), "utf-8");
2315
+ }
2316
+ const inventory = {
2317
+ generated: now,
2318
+ profile: profileId,
2319
+ totalAssets: cards.length,
2320
+ byRiskLevel: {
2321
+ minimal: cards.filter((c) => c.classification.riskLevel === "minimal").length,
2322
+ limited: cards.filter((c) => c.classification.riskLevel === "limited").length,
2323
+ high: cards.filter((c) => c.classification.riskLevel === "high").length,
2324
+ unacceptable: cards.filter((c) => c.classification.riskLevel === "unacceptable").length
2325
+ },
2326
+ byStatus: {
2327
+ draft: cards.filter((c) => c.governance.status === "draft").length,
2328
+ approved: cards.filter((c) => c.governance.status === "approved").length,
2329
+ production: cards.filter((c) => c.governance.status === "production").length
2330
+ },
2331
+ assets: cards.map((c) => ({
2332
+ id: c.id,
2333
+ name: c.name,
2334
+ riskLevel: c.classification.riskLevel,
2335
+ status: c.governance.status
2336
+ }))
2337
+ };
2338
+ writeFileSync4(join5(outputDir, "inventory.yaml"), YAML5.stringify(inventory, { indent: 2 }), "utf-8");
2339
+ console.log();
2340
+ printSuccess(`Audit package created: ${outputDir}`);
2341
+ printInfo(` - README.md (Executive Summary)`);
2342
+ printInfo(` - inventory.yaml (Asset Inventory)`);
2343
+ printInfo(` - assets/ (${cards.length} asset cards)`);
2344
+ }
2345
+ function loadActiveProfiles5() {
2346
+ const configPath = join5(process.cwd(), ".aigrc.yaml");
2347
+ if (!existsSync5(configPath)) {
2348
+ return ["eu-ai-act"];
2349
+ }
2350
+ try {
2351
+ const content = readFileSync5(configPath, "utf-8");
2352
+ const config = YAML5.parse(content);
2353
+ return config?.profiles || ["eu-ai-act"];
2354
+ } catch {
2355
+ return ["eu-ai-act"];
2356
+ }
2357
+ }
2358
+
2359
+ // src/bin/aigrc.ts
2360
+ program.name("aigrc").description("AI Governance, Risk, Compliance - CLI Tool").version("0.1.0");
2361
+ program.addCommand(scanCommand);
2362
+ program.addCommand(initCommand);
2363
+ program.addCommand(registerCommand);
2364
+ program.addCommand(validateCommand);
2365
+ program.addCommand(statusCommand);
2366
+ program.addCommand(complianceCommand);
2367
+ program.addCommand(classifyCommand);
2368
+ program.addCommand(checkCommand);
2369
+ program.addCommand(generateCommand);
2370
+ program.addCommand(reportCommand);
2371
+ program.parse();
2372
+ //# sourceMappingURL=aigrc.js.map