@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.
- package/dist/commands/brain.d.ts.map +1 -1
- package/dist/commands/brain.js +537 -6
- package/dist/commands/brain.js.map +1 -1
- package/dist/commands/ops.d.ts +5 -0
- package/dist/commands/ops.d.ts.map +1 -1
- package/dist/commands/ops.js +32 -2
- package/dist/commands/ops.js.map +1 -1
- package/dist/commands/policy.d.ts.map +1 -1
- package/dist/commands/policy.js +346 -0
- package/dist/commands/policy.js.map +1 -1
- package/dist/commands/quickstart.d.ts.map +1 -1
- package/dist/commands/quickstart.js +11 -6
- package/dist/commands/quickstart.js.map +1 -1
- package/dist/commands/runtime-adapter.d.ts +2 -1
- package/dist/commands/runtime-adapter.d.ts.map +1 -1
- package/dist/commands/runtime-adapter.js +51 -2
- package/dist/commands/runtime-adapter.js.map +1 -1
- package/dist/commands/session-hook.d.ts +13 -0
- package/dist/commands/session-hook.d.ts.map +1 -1
- package/dist/commands/session-hook.js +115 -15
- package/dist/commands/session-hook.js.map +1 -1
- package/dist/commands/session.d.ts +5 -0
- package/dist/commands/session.d.ts.map +1 -1
- package/dist/commands/session.js +328 -53
- package/dist/commands/session.js.map +1 -1
- package/dist/commands/verify-output.d.ts +2 -0
- package/dist/commands/verify-output.d.ts.map +1 -1
- package/dist/commands/verify-output.js +4 -0
- package/dist/commands/verify-output.js.map +1 -1
- package/dist/commands/verify.d.ts.map +1 -1
- package/dist/commands/verify.js +108 -24
- package/dist/commands/verify.js.map +1 -1
- package/dist/governance/structural-on-diff.d.ts +11 -0
- package/dist/governance/structural-on-diff.d.ts.map +1 -1
- package/dist/governance/structural-on-diff.js +38 -5
- package/dist/governance/structural-on-diff.js.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/runtime-build.json +5 -5
- package/dist/utils/agent-adapter-setup.js +1 -1
- package/dist/utils/agent-adapter-setup.js.map +1 -1
- package/dist/utils/agent-guard.d.ts +1 -0
- package/dist/utils/agent-guard.d.ts.map +1 -1
- package/dist/utils/agent-guard.js +18 -6
- package/dist/utils/agent-guard.js.map +1 -1
- package/dist/utils/brain-context.d.ts.map +1 -1
- package/dist/utils/brain-context.js +11 -2
- package/dist/utils/brain-context.js.map +1 -1
- package/dist/utils/git-coverage.d.ts.map +1 -1
- package/dist/utils/git-coverage.js +1 -0
- package/dist/utils/git-coverage.js.map +1 -1
- package/dist/utils/local-repo-brain.d.ts +7 -0
- package/dist/utils/local-repo-brain.d.ts.map +1 -1
- package/dist/utils/local-repo-brain.js +22 -5
- package/dist/utils/local-repo-brain.js.map +1 -1
- package/dist/utils/proposed-change-analysis.d.ts +20 -0
- package/dist/utils/proposed-change-analysis.d.ts.map +1 -0
- package/dist/utils/proposed-change-analysis.js +450 -0
- package/dist/utils/proposed-change-analysis.js.map +1 -0
- package/dist/utils/repo-intelligence-v2.d.ts +28 -0
- package/dist/utils/repo-intelligence-v2.d.ts.map +1 -0
- package/dist/utils/repo-intelligence-v2.js +215 -0
- package/dist/utils/repo-intelligence-v2.js.map +1 -0
- package/dist/utils/structural-understanding.d.ts +2 -2
- package/dist/utils/structural-understanding.d.ts.map +1 -1
- package/dist/utils/structural-understanding.js +1 -1
- package/dist/utils/structural-understanding.js.map +1 -1
- package/dist/utils/team-memory-path-hygiene.d.ts +4 -0
- package/dist/utils/team-memory-path-hygiene.d.ts.map +1 -0
- package/dist/utils/team-memory-path-hygiene.js +50 -0
- package/dist/utils/team-memory-path-hygiene.js.map +1 -0
- package/dist/utils/v0-governance.d.ts +8 -1
- package/dist/utils/v0-governance.d.ts.map +1 -1
- package/dist/utils/v0-governance.js +96 -17
- package/dist/utils/v0-governance.js.map +1 -1
- package/package.json +6 -5
package/dist/commands/policy.js
CHANGED
|
@@ -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')
|