@neurcode-ai/cli 0.9.40 → 0.9.42

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.
@@ -67,6 +67,7 @@ const change_contract_1 = require("../utils/change-contract");
67
67
  const runtime_guard_1 = require("../utils/runtime-guard");
68
68
  const artifact_signature_1 = require("../utils/artifact-signature");
69
69
  const policy_1 = require("@neurcode-ai/policy");
70
+ const ai_debt_budget_1 = require("../utils/ai-debt-budget");
70
71
  // Import chalk with fallback
71
72
  let chalk;
72
73
  try {
@@ -480,6 +481,48 @@ function runtimeGuardViolationsToReport(summary) {
480
481
  message: item.message,
481
482
  }));
482
483
  }
484
+ function toAiDebtSummary(evaluation) {
485
+ return {
486
+ mode: evaluation.mode,
487
+ pass: evaluation.pass,
488
+ score: evaluation.score,
489
+ source: evaluation.source,
490
+ metrics: {
491
+ addedTodoFixme: evaluation.metrics.addedTodoFixme,
492
+ addedConsoleLogs: evaluation.metrics.addedConsoleLogs,
493
+ addedAnyTypes: evaluation.metrics.addedAnyTypes,
494
+ largeFilesTouched: evaluation.metrics.largeFilesTouched,
495
+ bloatFiles: evaluation.metrics.bloatFiles,
496
+ },
497
+ thresholds: {
498
+ maxAddedTodoFixme: evaluation.thresholds.maxAddedTodoFixme,
499
+ maxAddedConsoleLogs: evaluation.thresholds.maxAddedConsoleLogs,
500
+ maxAddedAnyTypes: evaluation.thresholds.maxAddedAnyTypes,
501
+ maxLargeFilesTouched: evaluation.thresholds.maxLargeFilesTouched,
502
+ largeFileDeltaLines: evaluation.thresholds.largeFileDeltaLines,
503
+ maxBloatFiles: evaluation.thresholds.maxBloatFiles,
504
+ },
505
+ violations: evaluation.violations.map((item) => ({
506
+ code: item.code,
507
+ metric: item.metric,
508
+ observed: item.observed,
509
+ budget: item.budget,
510
+ message: item.message,
511
+ })),
512
+ };
513
+ }
514
+ function toAiDebtReportViolations(summary) {
515
+ if (summary.mode === 'off' || summary.violations.length === 0) {
516
+ return [];
517
+ }
518
+ const severity = summary.mode === 'enforce' ? 'block' : 'warn';
519
+ return summary.violations.map((item) => ({
520
+ rule: `ai_debt:${item.code}`,
521
+ file: '.neurcode/ai-debt-budget.json',
522
+ severity,
523
+ message: item.message,
524
+ }));
525
+ }
483
526
  function parseSigningKeyRing(raw) {
484
527
  if (!raw || !raw.trim()) {
485
528
  return {};
@@ -1234,8 +1277,16 @@ async function verifyCommand(options) {
1234
1277
  && !isEnabledFlag(process.env.NEURCODE_VERIFY_ALLOW_NON_STRICT_CI)
1235
1278
  && Boolean(options.apiKey || process.env.NEURCODE_API_KEY);
1236
1279
  const strictArtifactMode = explicitStrictArtifactMode || ciEnterpriseDefaultStrict;
1280
+ const runtimeGuardArtifactPath = (0, runtime_guard_1.resolveRuntimeGuardPath)(projectRoot, options.runtimeGuard);
1281
+ const autoRuntimeGuardInStrict = strictArtifactMode
1282
+ && (0, fs_1.existsSync)(runtimeGuardArtifactPath)
1283
+ && !isEnabledFlag(process.env.NEURCODE_VERIFY_DISABLE_AUTO_RUNTIME_GUARD);
1237
1284
  const requireRuntimeGuard = options.requireRuntimeGuard === true
1238
- || isEnabledFlag(process.env.NEURCODE_VERIFY_REQUIRE_RUNTIME_GUARD);
1285
+ || isEnabledFlag(process.env.NEURCODE_VERIFY_REQUIRE_RUNTIME_GUARD)
1286
+ || autoRuntimeGuardInStrict;
1287
+ const aiDebtConfig = (0, ai_debt_budget_1.resolveAiDebtBudgetConfig)(projectRoot, {
1288
+ strictDefault: strictArtifactMode || requireRuntimeGuard,
1289
+ });
1239
1290
  const signingConfig = resolveGovernanceSigningConfig();
1240
1291
  const aiLogSigningKey = signingConfig.signingKey;
1241
1292
  const aiLogSigningKeyId = signingConfig.signingKeyId;
@@ -1440,6 +1491,9 @@ async function verifyCommand(options) {
1440
1491
  if (ciEnterpriseDefaultStrict && !explicitStrictArtifactMode) {
1441
1492
  console.log(chalk.dim(' CI enterprise mode detected: strict deterministic artifact enforcement is auto-enabled (set NEURCODE_VERIFY_ALLOW_NON_STRICT_CI=1 to opt out).'));
1442
1493
  }
1494
+ if (autoRuntimeGuardInStrict && !options.requireRuntimeGuard && !isEnabledFlag(process.env.NEURCODE_VERIFY_REQUIRE_RUNTIME_GUARD)) {
1495
+ console.log(chalk.dim(` Strict mode detected runtime guard artifact: auto-enforcing runtime guard (${runtimeGuardArtifactPath}).`));
1496
+ }
1443
1497
  if (requireSignedArtifacts) {
1444
1498
  console.log(chalk.dim(' Artifact signature enforcement: enabled (set NEURCODE_VERIFY_ALLOW_UNSIGNED_ARTIFACTS=1 to relax)'));
1445
1499
  }
@@ -2156,6 +2210,11 @@ async function verifyCommand(options) {
2156
2210
  const filteredViolations = violations.filter((p) => !shouldIgnore(p));
2157
2211
  // Step D: The Block (only report scope violations for non-ignored files)
2158
2212
  if (filteredViolations.length > 0) {
2213
+ const aiDebtSummaryForScope = toAiDebtSummary((0, ai_debt_budget_1.evaluateAiDebtBudget)({
2214
+ diffFiles,
2215
+ bloatCount: filteredViolations.length,
2216
+ config: aiDebtConfig,
2217
+ }));
2159
2218
  recordVerifyEvent('FAIL', `scope_violation=${filteredViolations.length}`, modifiedFiles, finalPlanId);
2160
2219
  const scopeViolationItems = filteredViolations.map((file) => ({
2161
2220
  file,
@@ -2163,6 +2222,11 @@ async function verifyCommand(options) {
2163
2222
  severity: 'block',
2164
2223
  message: 'File modified outside the plan',
2165
2224
  }));
2225
+ const aiDebtViolationItems = toAiDebtReportViolations(aiDebtSummaryForScope);
2226
+ const scopeViolationReportItems = [
2227
+ ...scopeViolationItems,
2228
+ ...aiDebtViolationItems,
2229
+ ];
2166
2230
  const scopeViolationMessage = `Scope violation: ${filteredViolations.length} file(s) modified outside the plan`;
2167
2231
  if (options.json) {
2168
2232
  // Output JSON for scope violation BEFORE exit. Must include violations for GitHub Action annotations.
@@ -2170,7 +2234,7 @@ async function verifyCommand(options) {
2170
2234
  grade: 'F',
2171
2235
  score: 0,
2172
2236
  verdict: 'FAIL',
2173
- violations: scopeViolationItems,
2237
+ violations: scopeViolationReportItems,
2174
2238
  adherenceScore: 0,
2175
2239
  bloatCount: filteredViolations.length,
2176
2240
  bloatFiles: filteredViolations,
@@ -2180,10 +2244,12 @@ async function verifyCommand(options) {
2180
2244
  scopeGuardPassed: false,
2181
2245
  mode: 'plan_enforced',
2182
2246
  policyOnly: false,
2247
+ aiDebt: aiDebtSummaryForScope,
2183
2248
  ...(governanceResult
2184
2249
  ? buildGovernancePayload(governanceResult, orgGovernanceSettings, {
2185
2250
  changeContract: changeContractSummary,
2186
2251
  compiledPolicy: compiledPolicyMetadata,
2252
+ aiDebt: aiDebtSummaryForScope,
2187
2253
  })
2188
2254
  : {}),
2189
2255
  };
@@ -2191,7 +2257,7 @@ async function verifyCommand(options) {
2191
2257
  emitVerifyJson(jsonOutput);
2192
2258
  await recordVerificationIfRequested(options, config, {
2193
2259
  grade: 'F',
2194
- violations: scopeViolationItems,
2260
+ violations: scopeViolationReportItems,
2195
2261
  verifyResult: {
2196
2262
  adherenceScore: 0,
2197
2263
  verdict: 'FAIL',
@@ -2205,6 +2271,7 @@ async function verifyCommand(options) {
2205
2271
  ? buildGovernancePayload(governanceResult, orgGovernanceSettings, {
2206
2272
  changeContract: changeContractSummary,
2207
2273
  compiledPolicy: compiledPolicyMetadata,
2274
+ aiDebt: aiDebtSummaryForScope,
2208
2275
  })
2209
2276
  : undefined,
2210
2277
  });
@@ -2224,13 +2291,31 @@ async function verifyCommand(options) {
2224
2291
  filteredViolations.forEach(file => {
2225
2292
  console.log(chalk.dim(` neurcode allow ${file}`));
2226
2293
  });
2294
+ if (aiDebtSummaryForScope.mode !== 'off') {
2295
+ console.log('');
2296
+ const header = aiDebtSummaryForScope.mode === 'enforce'
2297
+ ? aiDebtSummaryForScope.pass
2298
+ ? chalk.green('AI Debt Budget: PASS')
2299
+ : chalk.red('AI Debt Budget: BLOCK')
2300
+ : chalk.yellow('AI Debt Budget: ADVISORY');
2301
+ console.log(header);
2302
+ console.log(chalk.dim(` Score: ${aiDebtSummaryForScope.score} | TODO/FIXME +${aiDebtSummaryForScope.metrics.addedTodoFixme} | ` +
2303
+ `console.log +${aiDebtSummaryForScope.metrics.addedConsoleLogs} | any +${aiDebtSummaryForScope.metrics.addedAnyTypes} | ` +
2304
+ `large files ${aiDebtSummaryForScope.metrics.largeFilesTouched} | bloat ${aiDebtSummaryForScope.metrics.bloatFiles}`));
2305
+ if (aiDebtSummaryForScope.violations.length > 0) {
2306
+ aiDebtSummaryForScope.violations.forEach((item) => {
2307
+ const color = aiDebtSummaryForScope.mode === 'enforce' ? chalk.red : chalk.yellow;
2308
+ console.log(color(` • ${item.message}`));
2309
+ });
2310
+ }
2311
+ }
2227
2312
  if (governanceResult) {
2228
2313
  displayGovernanceInsights(governanceResult, { explain: options.explain });
2229
2314
  }
2230
2315
  console.log('');
2231
2316
  await recordVerificationIfRequested(options, config, {
2232
2317
  grade: 'F',
2233
- violations: scopeViolationItems,
2318
+ violations: scopeViolationReportItems,
2234
2319
  verifyResult: {
2235
2320
  adherenceScore: 0,
2236
2321
  verdict: 'FAIL',
@@ -2244,6 +2329,7 @@ async function verifyCommand(options) {
2244
2329
  ? buildGovernancePayload(governanceResult, orgGovernanceSettings, {
2245
2330
  changeContract: changeContractSummary,
2246
2331
  compiledPolicy: compiledPolicyMetadata,
2332
+ aiDebt: aiDebtSummaryForScope,
2247
2333
  })
2248
2334
  : undefined,
2249
2335
  });
@@ -2770,12 +2856,19 @@ async function verifyCommand(options) {
2770
2856
  message: localEvaluation.message,
2771
2857
  };
2772
2858
  }
2859
+ const aiDebtEvaluation = (0, ai_debt_budget_1.evaluateAiDebtBudget)({
2860
+ diffFiles,
2861
+ bloatCount: verifyResult.bloatCount,
2862
+ config: aiDebtConfig,
2863
+ });
2864
+ const aiDebtSummary = toAiDebtSummary(aiDebtEvaluation);
2865
+ const aiDebtHardBlock = aiDebtSummary.mode === 'enforce' && aiDebtSummary.violations.length > 0;
2773
2866
  // Apply custom policy verdict: block from dashboard overrides API verdict
2774
2867
  const policyBlock = policyDecision === 'block' && policyViolations.length > 0;
2775
2868
  const governanceDecisionBlock = governanceResult?.governanceDecision?.decision === 'block';
2776
2869
  const governanceIntegrityBlock = signedLogsRequired && governanceResult ? governanceResult.aiChangeLogIntegrity.valid !== true : false;
2777
2870
  const governanceHardBlock = governanceDecisionBlock || governanceIntegrityBlock;
2778
- const effectiveVerdict = policyBlock || governanceHardBlock
2871
+ const effectiveVerdict = policyBlock || governanceHardBlock || aiDebtHardBlock
2779
2872
  ? 'FAIL'
2780
2873
  : verifyResult.verdict;
2781
2874
  const policyMessageBase = policyBlock
@@ -2786,9 +2879,12 @@ async function verifyCommand(options) {
2786
2879
  : governanceDecisionBlock
2787
2880
  ? governanceResult?.governanceDecision?.summary || 'Governance decision matrix returned BLOCK.'
2788
2881
  : null;
2882
+ const aiDebtBlockReason = aiDebtHardBlock
2883
+ ? `AI debt budget exceeded: ${aiDebtSummary.violations.map((item) => item.message).join(' ')}`
2884
+ : null;
2789
2885
  const effectiveMessage = (policyExceptionsSummary.suppressed > 0
2790
2886
  ? `${policyMessageBase} Policy exceptions suppressed ${policyExceptionsSummary.suppressed} violation(s).`
2791
- : policyMessageBase) + (governanceBlockReason ? ` ${governanceBlockReason}` : '');
2887
+ : policyMessageBase) + (governanceBlockReason ? ` ${governanceBlockReason}` : '') + (aiDebtBlockReason ? ` ${aiDebtBlockReason}` : '');
2792
2888
  // Calculate grade from effective verdict and score
2793
2889
  // CRITICAL: 0/0 planned files = 'F' (Incomplete), not 'B'
2794
2890
  // Bloat automatically drops grade by at least one letter
@@ -2834,10 +2930,11 @@ async function verifyCommand(options) {
2834
2930
  const changedPathsForBrain = diffFiles
2835
2931
  .flatMap((file) => [file.path, file.oldPath])
2836
2932
  .filter((value) => Boolean(value));
2837
- recordVerifyEvent(effectiveVerdict, `adherence=${verifyResult.adherenceScore};bloat=${verifyResult.bloatCount};scopeGuard=${scopeGuardPassed ? 1 : 0};policy=${policyDecision};policyExceptions=${policyExceptionsSummary.suppressed}`, changedPathsForBrain, finalPlanId);
2933
+ recordVerifyEvent(effectiveVerdict, `adherence=${verifyResult.adherenceScore};bloat=${verifyResult.bloatCount};scopeGuard=${scopeGuardPassed ? 1 : 0};policy=${policyDecision};policyExceptions=${policyExceptionsSummary.suppressed};aiDebt=${aiDebtSummary.score}`, changedPathsForBrain, finalPlanId);
2838
2934
  const shouldForceGovernancePass = scopeGuardPassed &&
2839
2935
  !policyBlock &&
2840
2936
  !governanceHardBlock &&
2937
+ !aiDebtHardBlock &&
2841
2938
  (effectiveVerdict === 'PASS' ||
2842
2939
  ((verifyResult.verdict === 'FAIL' || verifyResult.verdict === 'WARN') &&
2843
2940
  policyViolations.length === 0 &&
@@ -2858,7 +2955,8 @@ async function verifyCommand(options) {
2858
2955
  message: v.message,
2859
2956
  ...(v.line != null ? { startLine: v.line } : {}),
2860
2957
  }));
2861
- const violations = [...scopeViolations, ...policyViolationItems];
2958
+ const aiDebtViolationItems = toAiDebtReportViolations(aiDebtSummary);
2959
+ const violations = [...scopeViolations, ...policyViolationItems, ...aiDebtViolationItems];
2862
2960
  const jsonOutput = {
2863
2961
  grade,
2864
2962
  score: verifyResult.adherenceScore,
@@ -2874,12 +2972,14 @@ async function verifyCommand(options) {
2874
2972
  verificationSource: verifySource,
2875
2973
  mode: 'plan_enforced',
2876
2974
  policyOnly: false,
2975
+ aiDebt: aiDebtSummary,
2877
2976
  changeContract: changeContractSummary,
2878
2977
  ...(compiledPolicyMetadata ? { policyCompilation: compiledPolicyMetadata } : {}),
2879
2978
  ...(governanceResult
2880
2979
  ? buildGovernancePayload(governanceResult, orgGovernanceSettings, {
2881
2980
  changeContract: changeContractSummary,
2882
2981
  compiledPolicy: compiledPolicyMetadata,
2982
+ aiDebt: aiDebtSummary,
2883
2983
  })
2884
2984
  : {}),
2885
2985
  policyLock: {
@@ -2922,6 +3022,7 @@ async function verifyCommand(options) {
2922
3022
  ? buildGovernancePayload(governanceResult, orgGovernanceSettings, {
2923
3023
  changeContract: changeContractSummary,
2924
3024
  compiledPolicy: compiledPolicyMetadata,
3025
+ aiDebt: aiDebtSummary,
2925
3026
  })
2926
3027
  : undefined,
2927
3028
  });
@@ -2952,6 +3053,23 @@ async function verifyCommand(options) {
2952
3053
  if (governanceResult) {
2953
3054
  displayGovernanceInsights(governanceResult, { explain: options.explain });
2954
3055
  }
3056
+ if (aiDebtSummary.mode !== 'off') {
3057
+ const header = aiDebtSummary.mode === 'enforce'
3058
+ ? aiDebtSummary.pass
3059
+ ? chalk.green('\nAI Debt Budget: PASS')
3060
+ : chalk.red('\nAI Debt Budget: BLOCK')
3061
+ : chalk.yellow('\nAI Debt Budget: ADVISORY');
3062
+ console.log(header);
3063
+ console.log(chalk.dim(` Score: ${aiDebtSummary.score} | TODO/FIXME +${aiDebtSummary.metrics.addedTodoFixme} | ` +
3064
+ `console.log +${aiDebtSummary.metrics.addedConsoleLogs} | any +${aiDebtSummary.metrics.addedAnyTypes} | ` +
3065
+ `large files ${aiDebtSummary.metrics.largeFilesTouched} | bloat ${aiDebtSummary.metrics.bloatFiles}`));
3066
+ if (aiDebtSummary.violations.length > 0) {
3067
+ aiDebtSummary.violations.forEach((item) => {
3068
+ const color = aiDebtSummary.mode === 'enforce' ? chalk.red : chalk.yellow;
3069
+ console.log(color(` • ${item.message}`));
3070
+ });
3071
+ }
3072
+ }
2955
3073
  console.log(chalk.dim(`\n Policy exceptions source: ${describePolicyExceptionSource(policyExceptionsSummary.sourceMode)}`));
2956
3074
  if (policyExceptionsSummary.suppressed > 0) {
2957
3075
  console.log(chalk.yellow(`\n⚠️ Policy exceptions applied: ${policyExceptionsSummary.suppressed}`));
@@ -2991,6 +3109,7 @@ async function verifyCommand(options) {
2991
3109
  severity: v.severity,
2992
3110
  message: v.message,
2993
3111
  })),
3112
+ ...toAiDebtReportViolations(aiDebtSummary),
2994
3113
  ];
2995
3114
  await recordVerificationIfRequested(options, config, {
2996
3115
  grade,
@@ -3010,6 +3129,7 @@ async function verifyCommand(options) {
3010
3129
  ? buildGovernancePayload(governanceResult, orgGovernanceSettings, {
3011
3130
  changeContract: changeContractSummary,
3012
3131
  compiledPolicy: compiledPolicyMetadata,
3132
+ aiDebt: aiDebtSummary,
3013
3133
  })
3014
3134
  : undefined,
3015
3135
  });
@@ -3352,6 +3472,7 @@ function buildGovernancePayload(governance, orgGovernanceSettings, options) {
3352
3472
  : null,
3353
3473
  ...(options?.compiledPolicy ? { policyCompilation: options.compiledPolicy } : {}),
3354
3474
  ...(options?.changeContract ? { changeContract: options.changeContract } : {}),
3475
+ ...(options?.aiDebt ? { aiDebt: options.aiDebt } : {}),
3355
3476
  };
3356
3477
  }
3357
3478
  function displayGovernanceInsights(governance, options = {}) {