@neurcode-ai/cli 0.9.42 → 0.9.44
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/LICENSE +201 -0
- package/README.md +9 -3
- package/dist/commands/bootstrap.d.ts.map +1 -1
- package/dist/commands/bootstrap.js +16 -89
- package/dist/commands/bootstrap.js.map +1 -1
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +20 -2
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/contract.d.ts.map +1 -1
- package/dist/commands/contract.js +179 -20
- package/dist/commands/contract.js.map +1 -1
- package/dist/commands/doctor.d.ts +13 -3
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +476 -95
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/remediate.d.ts +8 -0
- package/dist/commands/remediate.d.ts.map +1 -1
- package/dist/commands/remediate.js +485 -103
- package/dist/commands/remediate.js.map +1 -1
- package/dist/commands/verify.d.ts.map +1 -1
- package/dist/commands/verify.js +415 -28
- package/dist/commands/verify.js.map +1 -1
- package/dist/config.d.ts +13 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +48 -0
- package/dist/config.js.map +1 -1
- package/dist/index.js +23 -3
- package/dist/index.js.map +1 -1
- package/dist/utils/artifact-signature.d.ts.map +1 -1
- package/dist/utils/artifact-signature.js +21 -0
- package/dist/utils/artifact-signature.js.map +1 -1
- package/dist/utils/cli-json.d.ts +54 -0
- package/dist/utils/cli-json.d.ts.map +1 -0
- package/dist/utils/cli-json.js +152 -0
- package/dist/utils/cli-json.js.map +1 -0
- package/dist/utils/policy-compiler.d.ts +3 -1
- package/dist/utils/policy-compiler.d.ts.map +1 -1
- package/dist/utils/policy-compiler.js +8 -0
- package/dist/utils/policy-compiler.js.map +1 -1
- package/dist/utils/runtime-guard.d.ts +3 -1
- package/dist/utils/runtime-guard.d.ts.map +1 -1
- package/dist/utils/runtime-guard.js +8 -0
- package/dist/utils/runtime-guard.js.map +1 -1
- package/package.json +7 -8
package/dist/commands/verify.js
CHANGED
|
@@ -367,6 +367,152 @@ function getRuntimeIgnoreSetFromEnv() {
|
|
|
367
367
|
.map((item) => toUnixPath(item.trim()))
|
|
368
368
|
.filter(Boolean));
|
|
369
369
|
}
|
|
370
|
+
const INTENT_PROOF_CODE_EXTENSIONS = new Set([
|
|
371
|
+
'.ts',
|
|
372
|
+
'.tsx',
|
|
373
|
+
'.js',
|
|
374
|
+
'.jsx',
|
|
375
|
+
'.mjs',
|
|
376
|
+
'.cjs',
|
|
377
|
+
'.mts',
|
|
378
|
+
'.cts',
|
|
379
|
+
'.py',
|
|
380
|
+
'.java',
|
|
381
|
+
'.go',
|
|
382
|
+
'.rb',
|
|
383
|
+
'.rs',
|
|
384
|
+
]);
|
|
385
|
+
const INTENT_PROOF_IGNORED_DIRECTORIES = new Set([
|
|
386
|
+
'.git',
|
|
387
|
+
'.hg',
|
|
388
|
+
'.svn',
|
|
389
|
+
'.neurcode',
|
|
390
|
+
'node_modules',
|
|
391
|
+
'vendor',
|
|
392
|
+
'dist',
|
|
393
|
+
'build',
|
|
394
|
+
'out',
|
|
395
|
+
'coverage',
|
|
396
|
+
'.next',
|
|
397
|
+
'.turbo',
|
|
398
|
+
'.cache',
|
|
399
|
+
]);
|
|
400
|
+
function isIntentProofSourceFile(pathValue) {
|
|
401
|
+
const lower = pathValue.toLowerCase();
|
|
402
|
+
for (const extension of INTENT_PROOF_CODE_EXTENSIONS) {
|
|
403
|
+
if (lower.endsWith(extension)) {
|
|
404
|
+
return true;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return false;
|
|
408
|
+
}
|
|
409
|
+
function dedupeDeterministicRules(rules) {
|
|
410
|
+
const seen = new Set();
|
|
411
|
+
const out = [];
|
|
412
|
+
for (const rule of rules) {
|
|
413
|
+
const key = [
|
|
414
|
+
rule.id,
|
|
415
|
+
rule.source,
|
|
416
|
+
rule.statement,
|
|
417
|
+
rule.matchToken,
|
|
418
|
+
rule.evaluationMode || '',
|
|
419
|
+
rule.evaluationScope || '',
|
|
420
|
+
typeof rule.minMatchesPerFile === 'number' ? String(rule.minMatchesPerFile) : '',
|
|
421
|
+
typeof rule.maxMatchesPerFile === 'number' ? String(rule.maxMatchesPerFile) : '',
|
|
422
|
+
rule.pathIncludePatterns?.join('|') || '',
|
|
423
|
+
rule.pathExcludePatterns?.join('|') || '',
|
|
424
|
+
].join('::');
|
|
425
|
+
if (seen.has(key))
|
|
426
|
+
continue;
|
|
427
|
+
seen.add(key);
|
|
428
|
+
out.push(rule);
|
|
429
|
+
}
|
|
430
|
+
return out;
|
|
431
|
+
}
|
|
432
|
+
function collectIntentProofFileContents(projectRoot, changedPaths, maxFiles, maxTotalBytes, maxPerFileBytes) {
|
|
433
|
+
const fileContents = {};
|
|
434
|
+
let scannedFiles = 0;
|
|
435
|
+
let scannedBytes = 0;
|
|
436
|
+
let truncated = false;
|
|
437
|
+
const tryAddFile = (relativePath) => {
|
|
438
|
+
if (!relativePath || fileContents[relativePath] !== undefined)
|
|
439
|
+
return;
|
|
440
|
+
if (!isIntentProofSourceFile(relativePath) || isExcludedFile(relativePath))
|
|
441
|
+
return;
|
|
442
|
+
if (scannedFiles >= maxFiles) {
|
|
443
|
+
truncated = true;
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
const absolutePath = (0, path_1.join)(projectRoot, relativePath);
|
|
447
|
+
if (!(0, fs_1.existsSync)(absolutePath))
|
|
448
|
+
return;
|
|
449
|
+
try {
|
|
450
|
+
const stat = (0, fs_1.statSync)(absolutePath);
|
|
451
|
+
if (!stat.isFile())
|
|
452
|
+
return;
|
|
453
|
+
if (stat.size > maxPerFileBytes)
|
|
454
|
+
return;
|
|
455
|
+
if (scannedBytes + stat.size > maxTotalBytes) {
|
|
456
|
+
truncated = true;
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
const content = (0, fs_1.readFileSync)(absolutePath, 'utf-8');
|
|
460
|
+
fileContents[relativePath] = content;
|
|
461
|
+
scannedFiles += 1;
|
|
462
|
+
scannedBytes += stat.size;
|
|
463
|
+
}
|
|
464
|
+
catch {
|
|
465
|
+
// Non-text/unreadable: skip.
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
for (const rawPath of changedPaths) {
|
|
469
|
+
tryAddFile(toUnixPath(rawPath));
|
|
470
|
+
}
|
|
471
|
+
const stack = [projectRoot];
|
|
472
|
+
while (stack.length > 0) {
|
|
473
|
+
const current = stack.pop();
|
|
474
|
+
if (!current)
|
|
475
|
+
continue;
|
|
476
|
+
let entries;
|
|
477
|
+
try {
|
|
478
|
+
entries = (0, fs_1.readdirSync)(current, { withFileTypes: true });
|
|
479
|
+
}
|
|
480
|
+
catch {
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
for (const entry of entries) {
|
|
484
|
+
if (entry.name === '.' || entry.name === '..')
|
|
485
|
+
continue;
|
|
486
|
+
const absolutePath = (0, path_1.join)(current, entry.name);
|
|
487
|
+
const relativePath = toUnixPath(absolutePath.slice(projectRoot.length + 1));
|
|
488
|
+
if (entry.isDirectory()) {
|
|
489
|
+
if (INTENT_PROOF_IGNORED_DIRECTORIES.has(entry.name))
|
|
490
|
+
continue;
|
|
491
|
+
if (isExcludedFile(`${relativePath}/`))
|
|
492
|
+
continue;
|
|
493
|
+
stack.push(absolutePath);
|
|
494
|
+
continue;
|
|
495
|
+
}
|
|
496
|
+
if (!entry.isFile()) {
|
|
497
|
+
continue;
|
|
498
|
+
}
|
|
499
|
+
if (scannedFiles >= maxFiles) {
|
|
500
|
+
truncated = true;
|
|
501
|
+
break;
|
|
502
|
+
}
|
|
503
|
+
tryAddFile(relativePath);
|
|
504
|
+
}
|
|
505
|
+
if (truncated && scannedFiles >= maxFiles) {
|
|
506
|
+
break;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return {
|
|
510
|
+
fileContents,
|
|
511
|
+
scannedFiles,
|
|
512
|
+
scannedBytes,
|
|
513
|
+
truncated,
|
|
514
|
+
};
|
|
515
|
+
}
|
|
370
516
|
async function buildEffectivePolicyRules(client, projectRoot, useDashboardPolicies) {
|
|
371
517
|
const defaultPolicy = (0, policy_engine_1.createDefaultPolicy)();
|
|
372
518
|
const customRules = [];
|
|
@@ -481,6 +627,112 @@ function runtimeGuardViolationsToReport(summary) {
|
|
|
481
627
|
message: item.message,
|
|
482
628
|
}));
|
|
483
629
|
}
|
|
630
|
+
function asObjectRecord(value) {
|
|
631
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
632
|
+
return null;
|
|
633
|
+
}
|
|
634
|
+
return value;
|
|
635
|
+
}
|
|
636
|
+
function asObjectArray(value) {
|
|
637
|
+
if (!Array.isArray(value)) {
|
|
638
|
+
return [];
|
|
639
|
+
}
|
|
640
|
+
return value
|
|
641
|
+
.map((item) => asObjectRecord(item))
|
|
642
|
+
.filter((item) => item !== null);
|
|
643
|
+
}
|
|
644
|
+
function asBooleanFlag(value) {
|
|
645
|
+
return typeof value === 'boolean' ? value : null;
|
|
646
|
+
}
|
|
647
|
+
function asNumberValue(value) {
|
|
648
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : null;
|
|
649
|
+
}
|
|
650
|
+
function asStringValue(value) {
|
|
651
|
+
return typeof value === 'string' && value.trim().length > 0 ? value : null;
|
|
652
|
+
}
|
|
653
|
+
function buildDeterministicLayerSummary(payload) {
|
|
654
|
+
const verdict = asStringValue(payload.verdict) || 'UNKNOWN';
|
|
655
|
+
const mode = asStringValue(payload.mode) || 'unknown';
|
|
656
|
+
const policyOnly = payload.policyOnly === true;
|
|
657
|
+
const scopeGuardPassed = asBooleanFlag(payload.scopeGuardPassed);
|
|
658
|
+
const violations = asObjectArray(payload.violations);
|
|
659
|
+
const policyViolations = violations.filter((entry) => {
|
|
660
|
+
const rule = String(entry.rule || '').toLowerCase();
|
|
661
|
+
return (!rule.includes('scope_guard')
|
|
662
|
+
&& !rule.includes('change_contract')
|
|
663
|
+
&& !rule.includes('runtime_guard')
|
|
664
|
+
&& !rule.includes('deterministic_artifacts_required')
|
|
665
|
+
&& !rule.includes('signed_artifacts_required'));
|
|
666
|
+
});
|
|
667
|
+
const policyBlocking = policyViolations.filter((entry) => String(entry.severity || '').toLowerCase() === 'block');
|
|
668
|
+
const policyWarnings = policyViolations.filter((entry) => String(entry.severity || '').toLowerCase() === 'warn');
|
|
669
|
+
const changeContract = asObjectRecord(payload.changeContract);
|
|
670
|
+
const changeContractValid = asBooleanFlag(changeContract?.valid);
|
|
671
|
+
const changeContractEnforced = changeContract?.enforced === true;
|
|
672
|
+
const changeContractViolations = Array.isArray(changeContract?.violations)
|
|
673
|
+
? (changeContract?.violations).length
|
|
674
|
+
: 0;
|
|
675
|
+
const explicitContractViolations = violations.filter((entry) => {
|
|
676
|
+
const rule = String(entry.rule || '').toLowerCase();
|
|
677
|
+
return rule.includes('scope_guard') || rule.includes('change_contract');
|
|
678
|
+
}).length;
|
|
679
|
+
const runtimeGuard = asObjectRecord(payload.runtimeGuard);
|
|
680
|
+
const runtimeGuardRequired = runtimeGuard?.required === true;
|
|
681
|
+
const runtimeGuardPass = asBooleanFlag(runtimeGuard?.pass);
|
|
682
|
+
const runtimeGuardViolations = Array.isArray(runtimeGuard?.violations)
|
|
683
|
+
? (runtimeGuard?.violations).length
|
|
684
|
+
: violations.filter((entry) => String(entry.rule || '').toLowerCase().includes('runtime_guard')).length;
|
|
685
|
+
const policyCompilation = asObjectRecord(payload.policyCompilation);
|
|
686
|
+
const deterministicRuleCount = asNumberValue(policyCompilation?.deterministicRuleCount);
|
|
687
|
+
const unmatchedStatements = asNumberValue(policyCompilation?.unmatchedStatements);
|
|
688
|
+
let policyGateStatus = 'pass';
|
|
689
|
+
if (policyBlocking.length > 0) {
|
|
690
|
+
policyGateStatus = 'fail';
|
|
691
|
+
}
|
|
692
|
+
else if (policyWarnings.length > 0 || verdict === 'WARN') {
|
|
693
|
+
policyGateStatus = 'warn';
|
|
694
|
+
}
|
|
695
|
+
let contractGateStatus = 'not_applicable';
|
|
696
|
+
if (!policyOnly) {
|
|
697
|
+
contractGateStatus = 'pass';
|
|
698
|
+
if (changeContractEnforced
|
|
699
|
+
&& (changeContractValid === false || changeContractViolations > 0 || explicitContractViolations > 0 || scopeGuardPassed === false)) {
|
|
700
|
+
contractGateStatus = 'fail';
|
|
701
|
+
}
|
|
702
|
+
else if (!changeContractEnforced && (changeContractViolations > 0 || explicitContractViolations > 0)) {
|
|
703
|
+
contractGateStatus = 'warn';
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
let runtimeGuardStatus = 'not_applicable';
|
|
707
|
+
if (runtimeGuardRequired) {
|
|
708
|
+
runtimeGuardStatus = runtimeGuardPass === false || runtimeGuardViolations > 0 ? 'fail' : 'pass';
|
|
709
|
+
}
|
|
710
|
+
else if (runtimeGuardViolations > 0) {
|
|
711
|
+
runtimeGuardStatus = 'fail';
|
|
712
|
+
}
|
|
713
|
+
return {
|
|
714
|
+
policyGate: {
|
|
715
|
+
status: policyGateStatus,
|
|
716
|
+
blockingViolations: policyBlocking.length,
|
|
717
|
+
warningViolations: policyWarnings.length,
|
|
718
|
+
deterministicRuleCount: deterministicRuleCount ?? null,
|
|
719
|
+
unmatchedStatements: unmatchedStatements ?? null,
|
|
720
|
+
},
|
|
721
|
+
contractGate: {
|
|
722
|
+
status: contractGateStatus,
|
|
723
|
+
enforced: changeContractEnforced,
|
|
724
|
+
valid: changeContractValid,
|
|
725
|
+
violationCount: changeContractViolations + explicitContractViolations,
|
|
726
|
+
mode,
|
|
727
|
+
},
|
|
728
|
+
runtimeGuardGate: {
|
|
729
|
+
status: runtimeGuardStatus,
|
|
730
|
+
required: runtimeGuardRequired,
|
|
731
|
+
pass: runtimeGuardPass,
|
|
732
|
+
violationCount: runtimeGuardViolations,
|
|
733
|
+
},
|
|
734
|
+
};
|
|
735
|
+
}
|
|
484
736
|
function toAiDebtSummary(evaluation) {
|
|
485
737
|
return {
|
|
486
738
|
mode: evaluation.mode,
|
|
@@ -544,31 +796,29 @@ function parseSigningKeyRing(raw) {
|
|
|
544
796
|
return out;
|
|
545
797
|
}
|
|
546
798
|
function resolveGovernanceSigningConfig() {
|
|
547
|
-
const
|
|
548
|
-
const envSigningKey = process.env.NEURCODE_GOVERNANCE_SIGNING_KEY?.trim() ||
|
|
549
|
-
process.env.NEURCODE_AI_LOG_SIGNING_KEY?.trim() ||
|
|
550
|
-
'';
|
|
551
|
-
const requestedKeyId = process.env.NEURCODE_GOVERNANCE_SIGNING_KEY_ID?.trim() || '';
|
|
799
|
+
const artifactSigningConfig = (0, artifact_signature_1.resolveGovernanceArtifactSigningConfigFromEnv)();
|
|
552
800
|
const signer = process.env.NEURCODE_GOVERNANCE_SIGNER || process.env.USER || 'neurcode-cli';
|
|
553
|
-
let signingKey = envSigningKey || null;
|
|
554
|
-
let signingKeyId = requestedKeyId || null;
|
|
555
|
-
if (!signingKey && Object.keys(signingKeys).length > 0) {
|
|
556
|
-
if (signingKeyId && signingKeys[signingKeyId]) {
|
|
557
|
-
signingKey = signingKeys[signingKeyId];
|
|
558
|
-
}
|
|
559
|
-
else {
|
|
560
|
-
const fallbackKeyId = Object.keys(signingKeys).sort((a, b) => a.localeCompare(b))[0];
|
|
561
|
-
signingKey = signingKeys[fallbackKeyId];
|
|
562
|
-
signingKeyId = signingKeyId || fallbackKeyId;
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
801
|
return {
|
|
566
|
-
signingKey,
|
|
567
|
-
signingKeyId,
|
|
568
|
-
signingKeys,
|
|
802
|
+
signingKey: artifactSigningConfig.signingKey,
|
|
803
|
+
signingKeyId: artifactSigningConfig.signingKeyId,
|
|
804
|
+
signingKeys: artifactSigningConfig.signingKeys,
|
|
569
805
|
signer,
|
|
570
806
|
};
|
|
571
807
|
}
|
|
808
|
+
function isGitRepository(cwd) {
|
|
809
|
+
try {
|
|
810
|
+
const output = (0, child_process_1.execSync)('git rev-parse --is-inside-work-tree', {
|
|
811
|
+
cwd,
|
|
812
|
+
encoding: 'utf-8',
|
|
813
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
814
|
+
maxBuffer: 1024 * 1024,
|
|
815
|
+
}).trim().toLowerCase();
|
|
816
|
+
return output === 'true';
|
|
817
|
+
}
|
|
818
|
+
catch {
|
|
819
|
+
return false;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
572
822
|
function isSignedAiLogsRequired(orgGovernanceSettings) {
|
|
573
823
|
return (orgGovernanceSettings?.requireSignedAiLogs === true ||
|
|
574
824
|
isEnabledFlag(process.env.NEURCODE_GOVERNANCE_REQUIRE_SIGNED_LOGS) ||
|
|
@@ -738,8 +988,12 @@ async function recordVerificationIfRequested(options, config, payload) {
|
|
|
738
988
|
*/
|
|
739
989
|
async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRoot, config, client, source, scopeTelemetry, projectId, orgGovernanceSettings, aiLogSigningKey, aiLogSigningKeyId, aiLogSigningKeys, aiLogSigner, compiledPolicyMetadata, changeContractSummary) {
|
|
740
990
|
const emitPolicyOnlyJson = (payload) => {
|
|
741
|
-
|
|
991
|
+
const enrichedPayload = {
|
|
742
992
|
...payload,
|
|
993
|
+
deterministicLayers: buildDeterministicLayerSummary(payload),
|
|
994
|
+
};
|
|
995
|
+
console.log(JSON.stringify({
|
|
996
|
+
...enrichedPayload,
|
|
743
997
|
...(compiledPolicyMetadata ? { policyCompilation: compiledPolicyMetadata } : {}),
|
|
744
998
|
changeContract: changeContractSummary,
|
|
745
999
|
scope: scopeTelemetry,
|
|
@@ -1264,10 +1518,45 @@ async function verifyCommand(options) {
|
|
|
1264
1518
|
const emitVerifyJson = (payload) => {
|
|
1265
1519
|
const jsonPayload = {
|
|
1266
1520
|
...payload,
|
|
1521
|
+
deterministicLayers: buildDeterministicLayerSummary(payload),
|
|
1267
1522
|
scope: scopeTelemetry,
|
|
1268
1523
|
};
|
|
1269
1524
|
console.log(JSON.stringify(jsonPayload, null, 2));
|
|
1270
1525
|
};
|
|
1526
|
+
if (!isGitRepository(projectRoot)) {
|
|
1527
|
+
const message = 'Verify requires a git repository. Initialize git (`git init`) or run this command inside an existing git project.';
|
|
1528
|
+
if (options.json) {
|
|
1529
|
+
emitVerifyJson({
|
|
1530
|
+
grade: 'F',
|
|
1531
|
+
score: 0,
|
|
1532
|
+
verdict: 'FAIL',
|
|
1533
|
+
violations: [
|
|
1534
|
+
{
|
|
1535
|
+
file: '.',
|
|
1536
|
+
rule: 'git_repository_required',
|
|
1537
|
+
severity: 'block',
|
|
1538
|
+
message,
|
|
1539
|
+
},
|
|
1540
|
+
],
|
|
1541
|
+
adherenceScore: 0,
|
|
1542
|
+
bloatCount: 0,
|
|
1543
|
+
bloatFiles: [],
|
|
1544
|
+
plannedFilesModified: 0,
|
|
1545
|
+
totalPlannedFiles: 0,
|
|
1546
|
+
message,
|
|
1547
|
+
scopeGuardPassed: false,
|
|
1548
|
+
mode: 'git_repository_required',
|
|
1549
|
+
policyOnly: options.policyOnly === true,
|
|
1550
|
+
});
|
|
1551
|
+
}
|
|
1552
|
+
else {
|
|
1553
|
+
console.log(chalk.red('\n❌ Git Repository Required'));
|
|
1554
|
+
console.log(chalk.red(` ${message}`));
|
|
1555
|
+
console.log(chalk.dim(` Current path: ${projectRoot}`));
|
|
1556
|
+
console.log(chalk.dim(' Next step: git init && git add . && git commit -m "chore: baseline"\n'));
|
|
1557
|
+
}
|
|
1558
|
+
process.exit(1);
|
|
1559
|
+
}
|
|
1271
1560
|
const enforceChangeContract = options.enforceChangeContract === true ||
|
|
1272
1561
|
isEnabledFlag(process.env.NEURCODE_VERIFY_ENFORCE_CHANGE_CONTRACT);
|
|
1273
1562
|
const explicitStrictArtifactMode = options.strictArtifacts === true ||
|
|
@@ -1715,7 +2004,7 @@ async function verifyCommand(options) {
|
|
|
1715
2004
|
// Determine which diff to capture (staged + unstaged for full current work)
|
|
1716
2005
|
let diffText;
|
|
1717
2006
|
if (options.staged) {
|
|
1718
|
-
diffText = (0, child_process_1.execSync)('git diff --
|
|
2007
|
+
diffText = (0, child_process_1.execSync)('git diff --cached', { maxBuffer: 1024 * 1024 * 1024, encoding: 'utf-8' });
|
|
1719
2008
|
}
|
|
1720
2009
|
else if (options.base) {
|
|
1721
2010
|
diffText = (0, git_1.getDiffFromBase)(options.base);
|
|
@@ -1726,7 +2015,7 @@ async function verifyCommand(options) {
|
|
|
1726
2015
|
else {
|
|
1727
2016
|
// Default: combine staged + unstaged to capture all current work
|
|
1728
2017
|
try {
|
|
1729
|
-
const stagedDiff = (0, child_process_1.execSync)('git diff --
|
|
2018
|
+
const stagedDiff = (0, child_process_1.execSync)('git diff --cached', { maxBuffer: 1024 * 1024 * 1024, encoding: 'utf-8' });
|
|
1730
2019
|
const unstagedDiff = (0, child_process_1.execSync)('git diff', { maxBuffer: 1024 * 1024 * 1024, encoding: 'utf-8' });
|
|
1731
2020
|
diffText = stagedDiff + (stagedDiff && unstagedDiff ? '\n' : '') + unstagedDiff;
|
|
1732
2021
|
}
|
|
@@ -2496,6 +2785,11 @@ async function verifyCommand(options) {
|
|
|
2496
2785
|
const hydratedCompiledPolicyRules = effectiveCompiledPolicy
|
|
2497
2786
|
? (0, policy_compiler_1.hydrateCompiledPolicyRules)(effectiveCompiledPolicy)
|
|
2498
2787
|
: [];
|
|
2788
|
+
const deterministicPolicyRules = effectiveCompiledPolicy
|
|
2789
|
+
? [...effectiveCompiledPolicy.statements.policyRules]
|
|
2790
|
+
: effectiveRules.customPolicies
|
|
2791
|
+
.map((policy) => policy.rule_text)
|
|
2792
|
+
.filter((ruleText) => typeof ruleText === 'string' && ruleText.trim().length > 0);
|
|
2499
2793
|
if (effectiveCompiledPolicy?.source.policyLockFingerprint &&
|
|
2500
2794
|
policyLockEvaluation.lockPresent &&
|
|
2501
2795
|
effectiveCompiledPolicy.source.policyLockFingerprint !== (0, policy_packs_1.readPolicyLockFile)(projectRoot).lock?.effective.fingerprint) {
|
|
@@ -2661,6 +2955,17 @@ async function verifyCommand(options) {
|
|
|
2661
2955
|
eventCount: auditIntegrity.count,
|
|
2662
2956
|
},
|
|
2663
2957
|
};
|
|
2958
|
+
let intentProofSummary = {
|
|
2959
|
+
enabled: false,
|
|
2960
|
+
pass: true,
|
|
2961
|
+
checkedRules: 0,
|
|
2962
|
+
repoScopedRules: 0,
|
|
2963
|
+
signatureDriftRules: 0,
|
|
2964
|
+
scannedFiles: 0,
|
|
2965
|
+
scannedBytes: 0,
|
|
2966
|
+
truncated: false,
|
|
2967
|
+
violations: [],
|
|
2968
|
+
};
|
|
2664
2969
|
if (!options.json && effectiveRules.customRules.length > 0) {
|
|
2665
2970
|
console.log(chalk.dim(` Evaluating ${effectiveRules.customRules.length} custom policy rule(s) from dashboard`));
|
|
2666
2971
|
}
|
|
@@ -2692,6 +2997,73 @@ async function verifyCommand(options) {
|
|
|
2692
2997
|
})),
|
|
2693
2998
|
})),
|
|
2694
2999
|
}));
|
|
3000
|
+
const compiledIntentProof = (0, governance_runtime_1.compileDeterministicConstraints)({
|
|
3001
|
+
intentConstraints: intentConstraintsForVerification,
|
|
3002
|
+
policyRules: deterministicPolicyRules,
|
|
3003
|
+
});
|
|
3004
|
+
const proofRules = dedupeDeterministicRules([
|
|
3005
|
+
...compiledIntentProof.rules,
|
|
3006
|
+
...hydratedCompiledPolicyRules,
|
|
3007
|
+
].filter((rule) => rule.evaluationScope === 'repo'
|
|
3008
|
+
|| rule.evaluationMode === 'signature_delta'));
|
|
3009
|
+
if (proofRules.length > 0) {
|
|
3010
|
+
const repoScopedRules = proofRules.filter((rule) => rule.evaluationScope === 'repo');
|
|
3011
|
+
const signatureDriftRules = proofRules.filter((rule) => rule.evaluationMode === 'signature_delta');
|
|
3012
|
+
const maxProofFiles = Math.max(100, Math.floor(Number(process.env.NEURCODE_INTENT_PROOF_MAX_FILES || 2500)));
|
|
3013
|
+
const maxProofBytes = Math.max(1024 * 1024, Math.floor(Number(process.env.NEURCODE_INTENT_PROOF_MAX_BYTES || (32 * 1024 * 1024))));
|
|
3014
|
+
const maxProofPerFileBytes = Math.max(4096, Math.floor(Number(process.env.NEURCODE_INTENT_PROOF_MAX_FILE_BYTES || (768 * 1024))));
|
|
3015
|
+
let proofContents;
|
|
3016
|
+
let proofScannedFiles = 0;
|
|
3017
|
+
let proofScannedBytes = 0;
|
|
3018
|
+
let proofTruncated = false;
|
|
3019
|
+
if (repoScopedRules.length > 0) {
|
|
3020
|
+
const scan = collectIntentProofFileContents(projectRoot, changedFiles.map((file) => file.path), maxProofFiles, maxProofBytes, maxProofPerFileBytes);
|
|
3021
|
+
proofContents = scan.fileContents;
|
|
3022
|
+
proofScannedFiles = scan.scannedFiles;
|
|
3023
|
+
proofScannedBytes = scan.scannedBytes;
|
|
3024
|
+
proofTruncated = scan.truncated;
|
|
3025
|
+
}
|
|
3026
|
+
const proofEvaluation = (0, governance_runtime_1.evaluatePlanVerification)({
|
|
3027
|
+
planFiles: planFilesForVerification.map((path) => ({
|
|
3028
|
+
path,
|
|
3029
|
+
action: 'MODIFY',
|
|
3030
|
+
})),
|
|
3031
|
+
changedFiles,
|
|
3032
|
+
diffStats,
|
|
3033
|
+
extraConstraintRules: proofRules,
|
|
3034
|
+
fileContents: proofContents,
|
|
3035
|
+
});
|
|
3036
|
+
intentProofSummary = {
|
|
3037
|
+
enabled: true,
|
|
3038
|
+
pass: proofEvaluation.constraintViolations.length === 0,
|
|
3039
|
+
checkedRules: proofRules.length,
|
|
3040
|
+
repoScopedRules: repoScopedRules.length,
|
|
3041
|
+
signatureDriftRules: signatureDriftRules.length,
|
|
3042
|
+
scannedFiles: proofScannedFiles,
|
|
3043
|
+
scannedBytes: proofScannedBytes,
|
|
3044
|
+
truncated: proofTruncated,
|
|
3045
|
+
violations: [...proofEvaluation.constraintViolations],
|
|
3046
|
+
};
|
|
3047
|
+
if (proofEvaluation.constraintViolations.length > 0) {
|
|
3048
|
+
const intentProofPolicyViolations = proofEvaluation.constraintViolations.map((violation) => ({
|
|
3049
|
+
file: '.neurcode/intent-proof',
|
|
3050
|
+
rule: 'intent_proof',
|
|
3051
|
+
severity: 'block',
|
|
3052
|
+
message: violation,
|
|
3053
|
+
}));
|
|
3054
|
+
policyViolations.push(...intentProofPolicyViolations);
|
|
3055
|
+
policyDecision = resolvePolicyDecisionFromViolations(policyViolations);
|
|
3056
|
+
}
|
|
3057
|
+
if (!options.json) {
|
|
3058
|
+
if (intentProofSummary.pass) {
|
|
3059
|
+
console.log(chalk.dim(` Intent proof checks passed (${intentProofSummary.checkedRules} rule(s)` +
|
|
3060
|
+
`${intentProofSummary.repoScopedRules > 0 ? `, repo scan ${intentProofSummary.scannedFiles} file(s)` : ''})`));
|
|
3061
|
+
}
|
|
3062
|
+
else {
|
|
3063
|
+
console.log(chalk.red(` Intent proof checks failed (${intentProofSummary.violations.length} violation(s), ${intentProofSummary.checkedRules} rule(s))`));
|
|
3064
|
+
}
|
|
3065
|
+
}
|
|
3066
|
+
}
|
|
2695
3067
|
const changeContractEvaluation = changeContractRead.contract
|
|
2696
3068
|
? (0, change_contract_1.evaluateChangeContract)(changeContractRead.contract, {
|
|
2697
3069
|
planId: finalPlanId,
|
|
@@ -2795,11 +3167,6 @@ async function verifyCommand(options) {
|
|
|
2795
3167
|
try {
|
|
2796
3168
|
let verifySource = 'api';
|
|
2797
3169
|
let verifyResult;
|
|
2798
|
-
const deterministicPolicyRules = effectiveCompiledPolicy
|
|
2799
|
-
? [...effectiveCompiledPolicy.statements.policyRules]
|
|
2800
|
-
: effectiveRules.customPolicies
|
|
2801
|
-
.map((policy) => policy.rule_text)
|
|
2802
|
-
.filter((ruleText) => typeof ruleText === 'string' && ruleText.trim().length > 0);
|
|
2803
3170
|
try {
|
|
2804
3171
|
verifyResult = await client.verifyPlan(finalPlanId, diffStats, changedFiles, projectId, intentConstraintsForVerification, deterministicPolicyRules, 'api', compiledPolicyMetadata, {
|
|
2805
3172
|
async: options.asyncMode === true,
|
|
@@ -2990,6 +3357,7 @@ async function verifyCommand(options) {
|
|
|
2990
3357
|
},
|
|
2991
3358
|
policyExceptions: policyExceptionsSummary,
|
|
2992
3359
|
policyGovernance: policyGovernanceSummary,
|
|
3360
|
+
intentProof: intentProofSummary,
|
|
2993
3361
|
...(runtimeGuardSummary.required ? { runtimeGuard: runtimeGuardSummary } : {}),
|
|
2994
3362
|
...(policyViolations.length > 0 && { policyDecision }),
|
|
2995
3363
|
...(effectiveRules.policyPack
|
|
@@ -3093,6 +3461,25 @@ async function verifyCommand(options) {
|
|
|
3093
3461
|
if (runtimeGuardSummary.required) {
|
|
3094
3462
|
console.log(chalk.dim(`\n Runtime guard: ${runtimeGuardSummary.pass ? 'pass' : 'block'} (${runtimeGuardSummary.path})`));
|
|
3095
3463
|
}
|
|
3464
|
+
if (intentProofSummary.enabled) {
|
|
3465
|
+
const proofHeader = intentProofSummary.pass
|
|
3466
|
+
? chalk.dim(` Intent proof: pass (${intentProofSummary.checkedRules} rule(s)` +
|
|
3467
|
+
`${intentProofSummary.repoScopedRules > 0 ? `, scanned ${intentProofSummary.scannedFiles} file(s)` : ''})`)
|
|
3468
|
+
: chalk.red(`\n⛔ Intent proof enforcement: ${intentProofSummary.violations.length} violation(s) ` +
|
|
3469
|
+
`(${intentProofSummary.checkedRules} rule(s))`);
|
|
3470
|
+
console.log(`\n${proofHeader}`);
|
|
3471
|
+
if (!intentProofSummary.pass) {
|
|
3472
|
+
intentProofSummary.violations.slice(0, 5).forEach((issue) => {
|
|
3473
|
+
console.log(chalk.red(` • ${issue}`));
|
|
3474
|
+
});
|
|
3475
|
+
if (intentProofSummary.violations.length > 5) {
|
|
3476
|
+
console.log(chalk.dim(` ... ${intentProofSummary.violations.length - 5} more violation(s)`));
|
|
3477
|
+
}
|
|
3478
|
+
}
|
|
3479
|
+
else if (intentProofSummary.truncated) {
|
|
3480
|
+
console.log(chalk.dim(' Intent proof repo scan reached configured file/byte limit (truncated).'));
|
|
3481
|
+
}
|
|
3482
|
+
}
|
|
3096
3483
|
}
|
|
3097
3484
|
// Report to Neurcode Cloud if --record flag is set
|
|
3098
3485
|
const filteredBloatForReport = (verifyResult.bloatFiles || []).filter((f) => !shouldIgnore(f));
|