@neurcode-ai/cli 0.18.0 → 0.19.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/commands/brain.d.ts.map +1 -1
  2. package/dist/commands/brain.js +537 -6
  3. package/dist/commands/brain.js.map +1 -1
  4. package/dist/commands/ops.d.ts +5 -0
  5. package/dist/commands/ops.d.ts.map +1 -1
  6. package/dist/commands/ops.js +32 -2
  7. package/dist/commands/ops.js.map +1 -1
  8. package/dist/commands/policy.d.ts.map +1 -1
  9. package/dist/commands/policy.js +346 -0
  10. package/dist/commands/policy.js.map +1 -1
  11. package/dist/commands/quickstart.d.ts.map +1 -1
  12. package/dist/commands/quickstart.js +11 -6
  13. package/dist/commands/quickstart.js.map +1 -1
  14. package/dist/commands/runtime-adapter.d.ts +2 -1
  15. package/dist/commands/runtime-adapter.d.ts.map +1 -1
  16. package/dist/commands/runtime-adapter.js +51 -2
  17. package/dist/commands/runtime-adapter.js.map +1 -1
  18. package/dist/commands/session-hook.d.ts +13 -0
  19. package/dist/commands/session-hook.d.ts.map +1 -1
  20. package/dist/commands/session-hook.js +115 -15
  21. package/dist/commands/session-hook.js.map +1 -1
  22. package/dist/commands/session.d.ts +5 -0
  23. package/dist/commands/session.d.ts.map +1 -1
  24. package/dist/commands/session.js +328 -53
  25. package/dist/commands/session.js.map +1 -1
  26. package/dist/commands/verify-output.d.ts +2 -0
  27. package/dist/commands/verify-output.d.ts.map +1 -1
  28. package/dist/commands/verify-output.js +4 -0
  29. package/dist/commands/verify-output.js.map +1 -1
  30. package/dist/commands/verify.d.ts.map +1 -1
  31. package/dist/commands/verify.js +108 -24
  32. package/dist/commands/verify.js.map +1 -1
  33. package/dist/governance/structural-on-diff.d.ts +11 -0
  34. package/dist/governance/structural-on-diff.d.ts.map +1 -1
  35. package/dist/governance/structural-on-diff.js +38 -5
  36. package/dist/governance/structural-on-diff.js.map +1 -1
  37. package/dist/index.js +4 -4
  38. package/dist/index.js.map +1 -1
  39. package/dist/runtime-build.json +5 -5
  40. package/dist/utils/agent-adapter-setup.js +1 -1
  41. package/dist/utils/agent-adapter-setup.js.map +1 -1
  42. package/dist/utils/agent-guard.d.ts +1 -0
  43. package/dist/utils/agent-guard.d.ts.map +1 -1
  44. package/dist/utils/agent-guard.js +18 -6
  45. package/dist/utils/agent-guard.js.map +1 -1
  46. package/dist/utils/brain-context.d.ts.map +1 -1
  47. package/dist/utils/brain-context.js +11 -2
  48. package/dist/utils/brain-context.js.map +1 -1
  49. package/dist/utils/git-coverage.d.ts.map +1 -1
  50. package/dist/utils/git-coverage.js +1 -0
  51. package/dist/utils/git-coverage.js.map +1 -1
  52. package/dist/utils/local-repo-brain.d.ts +7 -0
  53. package/dist/utils/local-repo-brain.d.ts.map +1 -1
  54. package/dist/utils/local-repo-brain.js +22 -5
  55. package/dist/utils/local-repo-brain.js.map +1 -1
  56. package/dist/utils/proposed-change-analysis.d.ts +20 -0
  57. package/dist/utils/proposed-change-analysis.d.ts.map +1 -0
  58. package/dist/utils/proposed-change-analysis.js +450 -0
  59. package/dist/utils/proposed-change-analysis.js.map +1 -0
  60. package/dist/utils/repo-intelligence-v2.d.ts +28 -0
  61. package/dist/utils/repo-intelligence-v2.d.ts.map +1 -0
  62. package/dist/utils/repo-intelligence-v2.js +215 -0
  63. package/dist/utils/repo-intelligence-v2.js.map +1 -0
  64. package/dist/utils/structural-understanding.d.ts +2 -2
  65. package/dist/utils/structural-understanding.d.ts.map +1 -1
  66. package/dist/utils/structural-understanding.js +1 -1
  67. package/dist/utils/structural-understanding.js.map +1 -1
  68. package/dist/utils/team-memory-path-hygiene.d.ts +4 -0
  69. package/dist/utils/team-memory-path-hygiene.d.ts.map +1 -0
  70. package/dist/utils/team-memory-path-hygiene.js +50 -0
  71. package/dist/utils/team-memory-path-hygiene.js.map +1 -0
  72. package/dist/utils/v0-governance.d.ts +8 -1
  73. package/dist/utils/v0-governance.d.ts.map +1 -1
  74. package/dist/utils/v0-governance.js +96 -17
  75. package/dist/utils/v0-governance.js.map +1 -1
  76. package/package.json +6 -5
@@ -1,6 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.policyCommand = policyCommand;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ const brain_1 = require("@neurcode-ai/brain");
7
+ const policy_engine_1 = require("@neurcode-ai/policy-engine");
4
8
  const project_root_1 = require("../utils/project-root");
5
9
  const config_1 = require("../config");
6
10
  const api_client_1 = require("../api-client");
@@ -11,6 +15,9 @@ const policy_audit_1 = require("../utils/policy-audit");
11
15
  const policy_packs_1 = require("../utils/policy-packs");
12
16
  const policy_compiler_1 = require("../utils/policy-compiler");
13
17
  const artifact_signature_1 = require("../utils/artifact-signature");
18
+ const proposed_change_analysis_1 = require("../utils/proposed-change-analysis");
19
+ const repo_intelligence_v2_1 = require("../utils/repo-intelligence-v2");
20
+ const v0_governance_1 = require("../utils/v0-governance");
14
21
  // Import chalk with fallback
15
22
  let chalk;
16
23
  try {
@@ -89,6 +96,107 @@ function normalizeListLimit(value, fallback, min, max) {
89
96
  return fallback;
90
97
  return Math.max(min, Math.min(max, Math.floor(Number(value))));
91
98
  }
99
+ const STRUCTURAL_POLICY_DEFAULT_PATH = '.neurcode/structural-policy-v2.json';
100
+ function resolveStructuralPolicyPath(cwd, input) {
101
+ const selected = input?.trim() || STRUCTURAL_POLICY_DEFAULT_PATH;
102
+ return (0, node_path_1.isAbsolute)(selected) ? selected : (0, node_path_1.join)(cwd, selected);
103
+ }
104
+ function readJsonValue(path) {
105
+ try {
106
+ return JSON.parse((0, node_fs_1.readFileSync)(path, 'utf8'));
107
+ }
108
+ catch (error) {
109
+ const detail = error instanceof Error ? error.message : String(error);
110
+ throw new Error(`Invalid JSON in ${path}: ${detail}`);
111
+ }
112
+ }
113
+ function normalizeRepositoryPath(cwd, input) {
114
+ const absolutePath = (0, node_path_1.isAbsolute)(input) ? (0, node_path_1.resolve)(input) : (0, node_path_1.resolve)(cwd, input);
115
+ const relativePath = (0, node_path_1.relative)(cwd, absolutePath).replace(/\\/g, '/');
116
+ if (!relativePath || relativePath === '..' || relativePath.startsWith('../')) {
117
+ throw new Error(`Path is outside repository root: ${input}`);
118
+ }
119
+ return { relativePath, absolutePath };
120
+ }
121
+ function structuralExitCode(verdict) {
122
+ if (verdict === 'block')
123
+ return 2;
124
+ if (verdict === 'not_evaluated')
125
+ return 3;
126
+ return 0;
127
+ }
128
+ function structuralStatements(value, previous) {
129
+ return [...previous, value];
130
+ }
131
+ async function evaluateStructuralPolicyPath(input) {
132
+ const target = normalizeRepositoryPath(input.cwd, input.targetPath);
133
+ let graph = (0, brain_1.readRepositoryGraph)(input.cwd);
134
+ let freshness = await (0, brain_1.repositoryGraphStatus)(input.cwd);
135
+ if (input.index !== false && (!graph || freshness.state !== 'fresh')) {
136
+ graph = (await (0, brain_1.indexRepositoryGraph)({ repoRoot: input.cwd })).graph;
137
+ freshness = graph.freshness;
138
+ }
139
+ else if (graph) {
140
+ graph = { ...graph, freshness };
141
+ }
142
+ const operation = input.operation
143
+ ?? ((0, node_fs_1.existsSync)(target.absolutePath) ? 'update' : 'create');
144
+ let proposedSource = null;
145
+ let sourceKind = 'not_available';
146
+ if (!input.pathOnly && operation !== 'delete') {
147
+ const contentPath = input.contentFile
148
+ ? ((0, node_path_1.isAbsolute)(input.contentFile) ? input.contentFile : (0, node_path_1.resolve)(input.cwd, input.contentFile))
149
+ : target.absolutePath;
150
+ if ((0, node_fs_1.existsSync)(contentPath)) {
151
+ proposedSource = (0, node_fs_1.readFileSync)(contentPath, 'utf8');
152
+ sourceKind = input.contentFile ? 'write_content' : 'post_write_disk_read';
153
+ }
154
+ }
155
+ const analysis = (0, proposed_change_analysis_1.analyzeProposedChange)({
156
+ repoRoot: input.cwd,
157
+ filePath: target.relativePath,
158
+ proposedSource,
159
+ sourceKind,
160
+ adapterId: 'neurcode-cli',
161
+ timing: sourceKind === 'post_write_disk_read' ? 'after_write' : 'before_write',
162
+ sessionId: null,
163
+ planRevision: null,
164
+ });
165
+ analysis.envelope.target.operation = operation;
166
+ const approvalsValue = input.approvalsFile
167
+ ? readJsonValue((0, node_path_1.isAbsolute)(input.approvalsFile) ? input.approvalsFile : (0, node_path_1.resolve)(input.cwd, input.approvalsFile))
168
+ : [];
169
+ if (!Array.isArray(approvalsValue))
170
+ throw new Error('Approvals file must contain a JSON array.');
171
+ const approvals = approvalsValue
172
+ .filter((value) => Boolean(value)
173
+ && typeof value === 'object'
174
+ && typeof value.path === 'string'
175
+ && Array.isArray(value.owners)
176
+ && typeof value.approvedBy === 'string')
177
+ .map((value) => ({
178
+ path: value.path,
179
+ owners: value.owners.filter((owner) => typeof owner === 'string'),
180
+ approvedBy: value.approvedBy,
181
+ }));
182
+ const repoIntelligence = await (0, repo_intelligence_v2_1.evaluateLocalRepoIntelligenceV2)({
183
+ repoRoot: input.cwd,
184
+ change: analysis.envelope,
185
+ approvals,
186
+ policyPath: input.artifactPath,
187
+ });
188
+ if (!repoIntelligence.policyConfigured) {
189
+ throw new Error(`Structural policy artifact is missing or invalid: ${repoIntelligence.policyPath}. ` +
190
+ 'Run `neurcode policy structural-compile`.');
191
+ }
192
+ return {
193
+ artifactPath: resolveStructuralPolicyPath(input.cwd, input.artifactPath),
194
+ graphId: repoIntelligence.evidence.graph.graphId,
195
+ envelope: analysis.envelope,
196
+ evaluation: repoIntelligence.evaluation,
197
+ evidence: repoIntelligence.evidence,
198
+ };
199
+ }
92
200
  async function resolveCustomPolicies(client, includeDashboardPolicies, requireDashboardPolicies) {
93
201
  if (!includeDashboardPolicies) {
94
202
  return {
@@ -487,6 +595,244 @@ function policyCommand(program) {
487
595
  }
488
596
  process.exit(pass ? 0 : 1);
489
597
  });
598
+ policy
599
+ .command('duplicate-mode [mode]')
600
+ .description('Inspect or set deterministic duplicate-symbol enforcement: off | warn | block')
601
+ .option('--json', 'Output stable machine-readable JSON')
602
+ .action((mode, options) => {
603
+ const cwd = (0, project_root_1.resolveNeurcodeProjectRoot)(process.cwd());
604
+ try {
605
+ if (mode !== undefined && !['off', 'warn', 'block'].includes(mode)) {
606
+ throw new Error('Invalid duplicate mode. Use one of: off, warn, block.');
607
+ }
608
+ const updated = mode
609
+ ? (0, v0_governance_1.setRepoSymbolDuplicateMode)(cwd, mode)
610
+ : null;
611
+ const config = (0, v0_governance_1.readRuntimeGovernanceConfig)(cwd);
612
+ if (config.error)
613
+ throw new Error(`Invalid governance configuration: ${config.error}`);
614
+ const profile = (0, v0_governance_1.ensureFreshGovernanceProfile)(cwd).profile;
615
+ const payload = {
616
+ ok: true,
617
+ repoSymbolDuplicateMode: profile.runtimeConfig.repoSymbolDuplicateMode,
618
+ source: config.exists ? 'governance_config' : 'default',
619
+ configPath: config.path,
620
+ profilePath: (0, node_path_1.join)(cwd, '.neurcode', 'profile.json'),
621
+ profileHash: profile.profileHash,
622
+ updated: Boolean(updated),
623
+ };
624
+ if (options.json)
625
+ console.log(JSON.stringify(payload, null, 2));
626
+ else {
627
+ console.log(chalk.bold('\n🛡️ Duplicate Symbol Policy\n'));
628
+ console.log(chalk.dim(`Effective mode: ${payload.repoSymbolDuplicateMode}`));
629
+ console.log(chalk.dim(`Source: ${payload.source}`));
630
+ console.log(chalk.dim(`Config: ${payload.configPath}`));
631
+ console.log(chalk.dim('Evidence: exact source-free symbol name/language facts only; semantic resemblance never blocks.'));
632
+ }
633
+ }
634
+ catch (error) {
635
+ const message = error instanceof Error ? error.message : String(error);
636
+ if (options.json)
637
+ console.log(JSON.stringify({ ok: false, error: message, exitCode: 1 }, null, 2));
638
+ else
639
+ console.error(chalk.red(`\n❌ ${message}\n`));
640
+ process.exitCode = 1;
641
+ }
642
+ });
643
+ policy
644
+ .command('structural-compile')
645
+ .description('Compile bounded Repository Policy V2 rules into a deterministic source-free artifact')
646
+ .option('--input <path>', 'JSON file containing organizationRules, repositoryRules, and naturalLanguageStatements')
647
+ .option('--statement <text>', 'Add a bounded natural-language statement', structuralStatements, [])
648
+ .option('--output <path>', `Output artifact (default: ${STRUCTURAL_POLICY_DEFAULT_PATH})`)
649
+ .option('--require-deterministic', 'Fail when any statement is advisory, not evaluated, or rejected')
650
+ .option('--json', 'Output stable machine-readable JSON')
651
+ .action((options) => {
652
+ const cwd = (0, project_root_1.resolveNeurcodeProjectRoot)(process.cwd());
653
+ try {
654
+ const fromFile = options.input
655
+ ? readJsonValue((0, node_path_1.isAbsolute)(options.input) ? options.input : (0, node_path_1.resolve)(cwd, options.input))
656
+ : {};
657
+ if (!fromFile || typeof fromFile !== 'object' || Array.isArray(fromFile)) {
658
+ throw new Error('Structural policy input must be a JSON object.');
659
+ }
660
+ const parsed = fromFile;
661
+ const naturalLanguageStatements = [
662
+ ...(Array.isArray(parsed.naturalLanguageStatements)
663
+ ? parsed.naturalLanguageStatements.filter((value) => typeof value === 'string')
664
+ : []),
665
+ ...(options.statement ?? []),
666
+ ];
667
+ const compilation = (0, policy_engine_1.compileStructuralPolicies)({
668
+ organizationRules: Array.isArray(parsed.organizationRules) ? parsed.organizationRules : [],
669
+ repositoryRules: Array.isArray(parsed.repositoryRules) ? parsed.repositoryRules : [],
670
+ naturalLanguageStatements,
671
+ });
672
+ const artifact = (0, policy_engine_1.createStructuralPolicyArtifact)(compilation);
673
+ const outputPath = resolveStructuralPolicyPath(cwd, options.output);
674
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(outputPath), { recursive: true });
675
+ (0, node_fs_1.writeFileSync)(outputPath, `${JSON.stringify(artifact, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 });
676
+ const incomplete = compilation.advisory.length
677
+ + compilation.notEvaluated.length
678
+ + compilation.rejected.length;
679
+ const exitCode = compilation.rejected.length > 0
680
+ || (options.requireDeterministic && incomplete > 0)
681
+ ? 4
682
+ : 0;
683
+ const payload = {
684
+ ok: exitCode === 0,
685
+ path: outputPath,
686
+ artifact,
687
+ counts: {
688
+ compiled: compilation.compiled.length,
689
+ advisory: compilation.advisory.length,
690
+ notEvaluated: compilation.notEvaluated.length,
691
+ rejected: compilation.rejected.length,
692
+ },
693
+ exitCode,
694
+ };
695
+ if (options.json) {
696
+ console.log(JSON.stringify(payload, null, 2));
697
+ }
698
+ else {
699
+ console.log(chalk.bold('\n🛡️ Structural Policy V2 Compilation\n'));
700
+ console.log(chalk.dim(`Artifact: ${outputPath}`));
701
+ console.log(chalk.dim(`Compiled: ${payload.counts.compiled}`));
702
+ console.log(chalk.dim(`Advisory: ${payload.counts.advisory}`));
703
+ console.log(chalk.dim(`Not evaluated: ${payload.counts.notEvaluated}`));
704
+ console.log(chalk.dim(`Rejected: ${payload.counts.rejected}`));
705
+ for (const rule of compilation.compiled) {
706
+ console.log(chalk.dim(` ${rule.ruleId} · ${rule.family} · ${rule.mode}`));
707
+ }
708
+ }
709
+ process.exitCode = exitCode;
710
+ }
711
+ catch (error) {
712
+ const message = error instanceof Error ? error.message : String(error);
713
+ if (options.json)
714
+ console.log(JSON.stringify({ ok: false, error: message, exitCode: 1 }, null, 2));
715
+ else
716
+ console.error(chalk.red(`\n❌ ${message}\n`));
717
+ process.exitCode = 1;
718
+ }
719
+ });
720
+ policy
721
+ .command('structural-test <path>')
722
+ .alias('check-change')
723
+ .description('Evaluate a proposed or current local file against Structural Policy V2')
724
+ .option('--artifact <path>', `Compiled artifact (default: ${STRUCTURAL_POLICY_DEFAULT_PATH})`)
725
+ .option('--content-file <path>', 'Local proposed content file; raw content is parsed locally and not retained')
726
+ .option('--operation <type>', 'create | update | delete | rename')
727
+ .option('--path-only', 'Evaluate the honest path-only host case without proposed content')
728
+ .option('--approvals <path>', 'JSON array of exact source-free approvals')
729
+ .option('--no-index', 'Do not create or refresh Repository Graph V2')
730
+ .option('--json', 'Output stable machine-readable JSON')
731
+ .action(async (path, options) => {
732
+ const cwd = (0, project_root_1.resolveNeurcodeProjectRoot)(process.cwd());
733
+ try {
734
+ const result = await evaluateStructuralPolicyPath({
735
+ cwd,
736
+ targetPath: path,
737
+ artifactPath: options.artifact,
738
+ contentFile: options.contentFile,
739
+ operation: options.operation,
740
+ pathOnly: options.pathOnly,
741
+ index: options.index,
742
+ approvalsFile: options.approvals,
743
+ });
744
+ const exitCode = structuralExitCode(result.evaluation.verdict);
745
+ const payload = { ok: exitCode === 0, ...result, exitCode };
746
+ if (options.json) {
747
+ console.log(JSON.stringify(payload, null, 2));
748
+ }
749
+ else {
750
+ console.log(chalk.bold('\n🛡️ Structural Policy V2 Test\n'));
751
+ console.log(chalk.dim(`Path: ${result.envelope.target.path}`));
752
+ console.log(chalk.dim(`Verdict: ${result.evaluation.verdict}`));
753
+ console.log(chalk.dim(`Truth: ${result.evaluation.truth}`));
754
+ console.log(chalk.dim(`Host timing: ${result.envelope.host.capability} / ${result.envelope.host.timing}`));
755
+ console.log(chalk.dim(`Evidence: ${result.envelope.content.availabilityReason}`));
756
+ console.log(chalk.dim('Enforcement: explicit CLI evaluation; observed result, not a hard host pre-write gate'));
757
+ console.log(chalk.dim(`Evaluated: ${result.evaluation.evaluatedRuleIds.length}`));
758
+ console.log(chalk.dim(`Not evaluated: ${result.evaluation.notEvaluatedRuleIds.length}`));
759
+ console.log(chalk.dim(`Advisory: ${result.evidence.advisory.length} (never blocking)`));
760
+ result.evaluation.findings.forEach((item) => {
761
+ console.log(chalk.yellow(` [${item.verdict}] ${item.ruleId}: ${item.explanation}`));
762
+ console.log(chalk.dim(` ${item.remediation}`));
763
+ });
764
+ }
765
+ process.exitCode = exitCode;
766
+ }
767
+ catch (error) {
768
+ const message = error instanceof Error ? error.message : String(error);
769
+ if (options.json)
770
+ console.log(JSON.stringify({ ok: false, error: message, exitCode: 1 }, null, 2));
771
+ else
772
+ console.error(chalk.red(`\n❌ ${message}\n`));
773
+ process.exitCode = 1;
774
+ }
775
+ });
776
+ policy
777
+ .command('structural-explain <path>')
778
+ .description('Explain matched facts, deterministic rules, verdicts, and remediation for a path')
779
+ .option('--artifact <path>', `Compiled artifact (default: ${STRUCTURAL_POLICY_DEFAULT_PATH})`)
780
+ .option('--content-file <path>', 'Local proposed content file')
781
+ .option('--operation <type>', 'create | update | delete | rename')
782
+ .option('--path-only', 'Explain the path-only not-evaluated boundary')
783
+ .option('--approvals <path>', 'JSON array of exact source-free approvals')
784
+ .option('--no-index', 'Do not create or refresh Repository Graph V2')
785
+ .option('--json', 'Output stable machine-readable JSON')
786
+ .action(async (path, options) => {
787
+ const cwd = (0, project_root_1.resolveNeurcodeProjectRoot)(process.cwd());
788
+ try {
789
+ const result = await evaluateStructuralPolicyPath({
790
+ cwd,
791
+ targetPath: path,
792
+ artifactPath: options.artifact,
793
+ contentFile: options.contentFile,
794
+ operation: options.operation,
795
+ pathOnly: options.pathOnly,
796
+ index: options.index,
797
+ approvalsFile: options.approvals,
798
+ });
799
+ const exitCode = structuralExitCode(result.evaluation.verdict);
800
+ if (options.json) {
801
+ console.log(JSON.stringify({ ok: exitCode === 0, ...result, exitCode }, null, 2));
802
+ }
803
+ else {
804
+ console.log(chalk.bold(`\n🛡️ Structural Policy V2 Explain: ${result.envelope.target.path}\n`));
805
+ console.log(chalk.dim(`Classification: ${result.evaluation.truth}`));
806
+ console.log(chalk.dim(`Verdict: ${result.evaluation.verdict}`));
807
+ console.log(chalk.dim(`Graph: ${result.graphId ?? 'not available'} (${result.evaluation.graphFreshness.state})`));
808
+ console.log(chalk.dim(`Evidence: ${result.envelope.content.availabilityReason}`));
809
+ console.log(chalk.dim('Enforcement: explicit CLI evaluation; observed result, not a hard host pre-write gate'));
810
+ if (result.evaluation.findings.length === 0) {
811
+ console.log(chalk.dim(result.evaluation.verdict === 'not_evaluated'
812
+ ? 'Required facts were unavailable; no deterministic pass is claimed.'
813
+ : 'No deterministic structural violations matched.'));
814
+ }
815
+ for (const item of result.evaluation.findings) {
816
+ console.log(chalk.yellow(`\n${item.ruleId} · ${item.family} · ${item.verdict}`));
817
+ console.log(` ${item.explanation}`);
818
+ console.log(chalk.dim(` Matched facts: ${item.matchedFacts.map((fact) => fact.factId).join(', ')}`));
819
+ console.log(chalk.dim(` Remediation: ${item.remediation}`));
820
+ }
821
+ if (result.evaluation.notEvaluatedRuleIds.length > 0) {
822
+ console.log(chalk.dim(`\nNot evaluated rules: ${result.evaluation.notEvaluatedRuleIds.join(', ')}`));
823
+ }
824
+ }
825
+ process.exitCode = exitCode;
826
+ }
827
+ catch (error) {
828
+ const message = error instanceof Error ? error.message : String(error);
829
+ if (options.json)
830
+ console.log(JSON.stringify({ ok: false, error: message, exitCode: 1 }, null, 2));
831
+ else
832
+ console.error(chalk.red(`\n❌ ${message}\n`));
833
+ process.exitCode = 1;
834
+ }
835
+ });
490
836
  policy
491
837
  .command('compile')
492
838
  .description('Compile deterministic policy constraints into a committed artifact')