@neurcode-ai/cli 0.9.44 → 0.9.46

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 (181) hide show
  1. package/README.md +7 -3
  2. package/dist/commands/contract.js +47 -0
  3. package/dist/commands/plan.js +40 -0
  4. package/dist/commands/ship.js +10 -3
  5. package/dist/commands/verify.d.ts +2 -0
  6. package/dist/commands/verify.js +251 -118
  7. package/dist/index.js +41 -5
  8. package/dist/utils/advisory-signals.d.ts +20 -0
  9. package/dist/utils/advisory-signals.js +177 -0
  10. package/dist/utils/change-contract.d.ts +105 -1
  11. package/dist/utils/change-contract.js +685 -12
  12. package/dist/utils/diff-symbols.d.ts +10 -0
  13. package/dist/utils/diff-symbols.js +218 -0
  14. package/dist/utils/governance.d.ts +1 -0
  15. package/dist/utils/governance.js +1 -1
  16. package/dist/utils/plan-symbols.d.ts +17 -0
  17. package/dist/utils/plan-symbols.js +209 -0
  18. package/package.json +6 -14
  19. package/LICENSE +0 -201
  20. package/dist/api-client.d.ts.map +0 -1
  21. package/dist/api-client.js.map +0 -1
  22. package/dist/commands/allow.d.ts.map +0 -1
  23. package/dist/commands/allow.js.map +0 -1
  24. package/dist/commands/apply.d.ts.map +0 -1
  25. package/dist/commands/apply.js.map +0 -1
  26. package/dist/commands/approve.d.ts.map +0 -1
  27. package/dist/commands/approve.js.map +0 -1
  28. package/dist/commands/ask.d.ts.map +0 -1
  29. package/dist/commands/ask.js.map +0 -1
  30. package/dist/commands/audit.d.ts.map +0 -1
  31. package/dist/commands/audit.js.map +0 -1
  32. package/dist/commands/bootstrap.d.ts.map +0 -1
  33. package/dist/commands/bootstrap.js.map +0 -1
  34. package/dist/commands/brain.d.ts.map +0 -1
  35. package/dist/commands/brain.js.map +0 -1
  36. package/dist/commands/check.d.ts.map +0 -1
  37. package/dist/commands/check.js.map +0 -1
  38. package/dist/commands/config.d.ts.map +0 -1
  39. package/dist/commands/config.js.map +0 -1
  40. package/dist/commands/contract.d.ts.map +0 -1
  41. package/dist/commands/contract.js.map +0 -1
  42. package/dist/commands/doctor.d.ts.map +0 -1
  43. package/dist/commands/doctor.js.map +0 -1
  44. package/dist/commands/feedback.d.ts.map +0 -1
  45. package/dist/commands/feedback.js.map +0 -1
  46. package/dist/commands/guard.d.ts.map +0 -1
  47. package/dist/commands/guard.js.map +0 -1
  48. package/dist/commands/init.d.ts.map +0 -1
  49. package/dist/commands/init.js.map +0 -1
  50. package/dist/commands/login.d.ts.map +0 -1
  51. package/dist/commands/login.js.map +0 -1
  52. package/dist/commands/logout.d.ts.map +0 -1
  53. package/dist/commands/logout.js.map +0 -1
  54. package/dist/commands/map.d.ts.map +0 -1
  55. package/dist/commands/map.js.map +0 -1
  56. package/dist/commands/plan-slo.d.ts.map +0 -1
  57. package/dist/commands/plan-slo.js.map +0 -1
  58. package/dist/commands/plan.d.ts.map +0 -1
  59. package/dist/commands/plan.js.map +0 -1
  60. package/dist/commands/policy.d.ts.map +0 -1
  61. package/dist/commands/policy.js.map +0 -1
  62. package/dist/commands/prompt.d.ts.map +0 -1
  63. package/dist/commands/prompt.js.map +0 -1
  64. package/dist/commands/refactor.d.ts.map +0 -1
  65. package/dist/commands/refactor.js.map +0 -1
  66. package/dist/commands/remediate.d.ts.map +0 -1
  67. package/dist/commands/remediate.js.map +0 -1
  68. package/dist/commands/repo.d.ts.map +0 -1
  69. package/dist/commands/repo.js.map +0 -1
  70. package/dist/commands/revert.d.ts.map +0 -1
  71. package/dist/commands/revert.js.map +0 -1
  72. package/dist/commands/security.d.ts.map +0 -1
  73. package/dist/commands/security.js.map +0 -1
  74. package/dist/commands/session.d.ts.map +0 -1
  75. package/dist/commands/session.js.map +0 -1
  76. package/dist/commands/ship.d.ts.map +0 -1
  77. package/dist/commands/ship.js.map +0 -1
  78. package/dist/commands/simulate.d.ts.map +0 -1
  79. package/dist/commands/simulate.js.map +0 -1
  80. package/dist/commands/verify.d.ts.map +0 -1
  81. package/dist/commands/verify.js.map +0 -1
  82. package/dist/commands/watch.d.ts.map +0 -1
  83. package/dist/commands/watch.js.map +0 -1
  84. package/dist/commands/whoami.d.ts.map +0 -1
  85. package/dist/commands/whoami.js.map +0 -1
  86. package/dist/config.d.ts.map +0 -1
  87. package/dist/config.js.map +0 -1
  88. package/dist/index.d.ts.map +0 -1
  89. package/dist/index.js.map +0 -1
  90. package/dist/rules.d.ts.map +0 -1
  91. package/dist/rules.js.map +0 -1
  92. package/dist/services/integrations/TicketService.d.ts.map +0 -1
  93. package/dist/services/integrations/TicketService.js.map +0 -1
  94. package/dist/services/mapper/ProjectScanner.d.ts.map +0 -1
  95. package/dist/services/mapper/ProjectScanner.js.map +0 -1
  96. package/dist/services/project-knowledge-service.d.ts.map +0 -1
  97. package/dist/services/project-knowledge-service.js.map +0 -1
  98. package/dist/services/security/SecurityGuard.d.ts.map +0 -1
  99. package/dist/services/security/SecurityGuard.js.map +0 -1
  100. package/dist/services/toolbox-service.d.ts.map +0 -1
  101. package/dist/services/toolbox-service.js.map +0 -1
  102. package/dist/services/watch/BlobStore.d.ts.map +0 -1
  103. package/dist/services/watch/BlobStore.js.map +0 -1
  104. package/dist/services/watch/CommandPoller.d.ts.map +0 -1
  105. package/dist/services/watch/CommandPoller.js.map +0 -1
  106. package/dist/services/watch/Journal.d.ts.map +0 -1
  107. package/dist/services/watch/Journal.js.map +0 -1
  108. package/dist/services/watch/Sentinel.d.ts.map +0 -1
  109. package/dist/services/watch/Sentinel.js.map +0 -1
  110. package/dist/services/watch/Syncer.d.ts.map +0 -1
  111. package/dist/services/watch/Syncer.js.map +0 -1
  112. package/dist/utils/ROILogger.d.ts.map +0 -1
  113. package/dist/utils/ROILogger.js.map +0 -1
  114. package/dist/utils/RelevanceScorer.d.ts.map +0 -1
  115. package/dist/utils/RelevanceScorer.js.map +0 -1
  116. package/dist/utils/ai-debt-budget.d.ts.map +0 -1
  117. package/dist/utils/ai-debt-budget.js.map +0 -1
  118. package/dist/utils/artifact-signature.d.ts.map +0 -1
  119. package/dist/utils/artifact-signature.js.map +0 -1
  120. package/dist/utils/ask-cache.d.ts.map +0 -1
  121. package/dist/utils/ask-cache.js.map +0 -1
  122. package/dist/utils/box.d.ts.map +0 -1
  123. package/dist/utils/box.js.map +0 -1
  124. package/dist/utils/brain-context.d.ts.map +0 -1
  125. package/dist/utils/brain-context.js.map +0 -1
  126. package/dist/utils/breakage-simulator.d.ts.map +0 -1
  127. package/dist/utils/breakage-simulator.js.map +0 -1
  128. package/dist/utils/change-contract.d.ts.map +0 -1
  129. package/dist/utils/change-contract.js.map +0 -1
  130. package/dist/utils/cli-json.d.ts.map +0 -1
  131. package/dist/utils/cli-json.js.map +0 -1
  132. package/dist/utils/custom-policy-rules.d.ts.map +0 -1
  133. package/dist/utils/custom-policy-rules.js.map +0 -1
  134. package/dist/utils/git.d.ts.map +0 -1
  135. package/dist/utils/git.js.map +0 -1
  136. package/dist/utils/gitignore.d.ts.map +0 -1
  137. package/dist/utils/gitignore.js.map +0 -1
  138. package/dist/utils/governance.d.ts.map +0 -1
  139. package/dist/utils/governance.js.map +0 -1
  140. package/dist/utils/ignore.d.ts.map +0 -1
  141. package/dist/utils/ignore.js.map +0 -1
  142. package/dist/utils/manual-approvals.d.ts.map +0 -1
  143. package/dist/utils/manual-approvals.js.map +0 -1
  144. package/dist/utils/messages.d.ts.map +0 -1
  145. package/dist/utils/messages.js.map +0 -1
  146. package/dist/utils/neurcode-context.d.ts.map +0 -1
  147. package/dist/utils/neurcode-context.js.map +0 -1
  148. package/dist/utils/plan-cache.d.ts.map +0 -1
  149. package/dist/utils/plan-cache.js.map +0 -1
  150. package/dist/utils/plan-slo.d.ts.map +0 -1
  151. package/dist/utils/plan-slo.js.map +0 -1
  152. package/dist/utils/policy-audit.d.ts.map +0 -1
  153. package/dist/utils/policy-audit.js.map +0 -1
  154. package/dist/utils/policy-compiler.d.ts.map +0 -1
  155. package/dist/utils/policy-compiler.js.map +0 -1
  156. package/dist/utils/policy-exceptions.d.ts.map +0 -1
  157. package/dist/utils/policy-exceptions.js.map +0 -1
  158. package/dist/utils/policy-governance.d.ts.map +0 -1
  159. package/dist/utils/policy-governance.js.map +0 -1
  160. package/dist/utils/policy-packs.d.ts.map +0 -1
  161. package/dist/utils/policy-packs.js.map +0 -1
  162. package/dist/utils/project-detector.d.ts.map +0 -1
  163. package/dist/utils/project-detector.js.map +0 -1
  164. package/dist/utils/project-root.d.ts.map +0 -1
  165. package/dist/utils/project-root.js.map +0 -1
  166. package/dist/utils/repo-links.d.ts.map +0 -1
  167. package/dist/utils/repo-links.js.map +0 -1
  168. package/dist/utils/restore.d.ts.map +0 -1
  169. package/dist/utils/restore.js.map +0 -1
  170. package/dist/utils/runtime-guard.d.ts.map +0 -1
  171. package/dist/utils/runtime-guard.js.map +0 -1
  172. package/dist/utils/scope-telemetry.d.ts.map +0 -1
  173. package/dist/utils/scope-telemetry.js.map +0 -1
  174. package/dist/utils/secret-masking.d.ts.map +0 -1
  175. package/dist/utils/secret-masking.js.map +0 -1
  176. package/dist/utils/state.d.ts.map +0 -1
  177. package/dist/utils/state.js.map +0 -1
  178. package/dist/utils/tier.d.ts.map +0 -1
  179. package/dist/utils/tier.js.map +0 -1
  180. package/dist/utils/user-context.d.ts.map +0 -1
  181. package/dist/utils/user-context.js.map +0 -1
@@ -51,7 +51,6 @@ const path_1 = require("path");
51
51
  const fs_1 = require("fs");
52
52
  const state_1 = require("../utils/state");
53
53
  const ROILogger_1 = require("../utils/ROILogger");
54
- const box_1 = require("../utils/box");
55
54
  const ignore_1 = require("../utils/ignore");
56
55
  const project_root_1 = require("../utils/project-root");
57
56
  const brain_context_1 = require("../utils/brain-context");
@@ -64,6 +63,8 @@ const policy_audit_1 = require("../utils/policy-audit");
64
63
  const governance_1 = require("../utils/governance");
65
64
  const policy_compiler_1 = require("../utils/policy-compiler");
66
65
  const change_contract_1 = require("../utils/change-contract");
66
+ const diff_symbols_1 = require("../utils/diff-symbols");
67
+ const advisory_signals_1 = require("../utils/advisory-signals");
67
68
  const runtime_guard_1 = require("../utils/runtime-guard");
68
69
  const artifact_signature_1 = require("../utils/artifact-signature");
69
70
  const policy_1 = require("@neurcode-ai/policy");
@@ -820,9 +821,13 @@ function isGitRepository(cwd) {
820
821
  }
821
822
  }
822
823
  function isSignedAiLogsRequired(orgGovernanceSettings) {
823
- return (orgGovernanceSettings?.requireSignedAiLogs === true ||
824
- isEnabledFlag(process.env.NEURCODE_GOVERNANCE_REQUIRE_SIGNED_LOGS) ||
825
- isEnabledFlag(process.env.NEURCODE_AI_LOG_REQUIRE_SIGNED));
824
+ const explicitRequirement = isEnabledFlag(process.env.NEURCODE_GOVERNANCE_REQUIRE_SIGNED_LOGS) ||
825
+ isEnabledFlag(process.env.NEURCODE_AI_LOG_REQUIRE_SIGNED);
826
+ if (explicitRequirement) {
827
+ return true;
828
+ }
829
+ const honorOrgRequirement = isEnabledFlag(process.env.NEURCODE_GOVERNANCE_ENFORCE_ORG_SIGNED_LOG_REQUIREMENT);
830
+ return honorOrgRequirement && orgGovernanceSettings?.requireSignedAiLogs === true;
826
831
  }
827
832
  function policyLockMismatchMessage(mismatches) {
828
833
  if (mismatches.length === 0) {
@@ -1011,6 +1016,7 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
1011
1016
  if (!options.json) {
1012
1017
  console.log(chalk.cyan('🛡️ General Governance mode (policy only, no plan linked)\n'));
1013
1018
  }
1019
+ const signedLogsRequired = isSignedAiLogsRequired(orgGovernanceSettings);
1014
1020
  const governanceAnalysis = (0, governance_1.evaluateGovernance)({
1015
1021
  projectRoot,
1016
1022
  task: 'Policy-only verification',
@@ -1018,6 +1024,7 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
1018
1024
  diffFiles,
1019
1025
  contextCandidates: diffFiles.map((file) => file.path),
1020
1026
  orgGovernance: orgGovernanceSettings,
1027
+ requireSignedAiLogs: signedLogsRequired,
1021
1028
  signingKey: aiLogSigningKey,
1022
1029
  signingKeyId: aiLogSigningKeyId,
1023
1030
  signingKeys: aiLogSigningKeys,
@@ -1028,7 +1035,6 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
1028
1035
  changeContract: changeContractSummary,
1029
1036
  });
1030
1037
  const contextPolicyViolations = governanceAnalysis.contextPolicy.violations.filter((item) => !ignoreFilter(item.file));
1031
- const signedLogsRequired = isSignedAiLogsRequired(orgGovernanceSettings);
1032
1038
  if (signedLogsRequired && !governanceAnalysis.aiChangeLogIntegrity.valid) {
1033
1039
  const message = `AI change-log integrity check failed: ${governanceAnalysis.aiChangeLogIntegrity.issues.join('; ') || 'unknown issue'}`;
1034
1040
  if (options.json) {
@@ -2245,6 +2251,7 @@ async function verifyCommand(options) {
2245
2251
  diffFiles,
2246
2252
  contextCandidates: diffFiles.map((file) => file.path),
2247
2253
  orgGovernance: orgGovernanceSettings,
2254
+ requireSignedAiLogs: signedLogsRequired,
2248
2255
  signingKey: aiLogSigningKey,
2249
2256
  signingKeyId: aiLogSigningKeyId,
2250
2257
  signingKeys: aiLogSigningKeys,
@@ -2308,6 +2315,9 @@ async function verifyCommand(options) {
2308
2315
  console.log(chalk.cyan('\n📊 Analyzing changes against plan...'));
2309
2316
  console.log(chalk.dim(` Found ${summary.totalFiles} file(s) changed`));
2310
2317
  console.log(chalk.dim(` ${summary.totalAdded} lines added, ${summary.totalRemoved} lines removed\n`));
2318
+ if (options.demo) {
2319
+ console.log(chalk.dim(' Demo mode enabled: showing extra context while keeping drift output short and grouped.\n'));
2320
+ }
2311
2321
  }
2312
2322
  const runPolicyOnlyModeAndExit = async (source) => {
2313
2323
  const exitCode = await executePolicyOnlyMode(options, diffFiles, shouldIgnore, projectRoot, config, client, source, scopeTelemetry, projectId || undefined, orgGovernanceSettings, aiLogSigningKey, aiLogSigningKeyId, aiLogSigningKeys, aiLogSigner, compiledPolicyMetadata, changeContractSummary);
@@ -2400,10 +2410,104 @@ async function verifyCommand(options) {
2400
2410
  });
2401
2411
  process.exit(1);
2402
2412
  }
2403
- if (!options.json) {
2404
- console.log(chalk.yellow('⚠️ No Plan ID found. Falling back to General Governance (Policy Only).'));
2413
+ let autoContractPath = null;
2414
+ if (!changeContractRead.contract && !strictArtifactMode) {
2415
+ try {
2416
+ const fallbackPlanId = `advisory_${Date.now()}`;
2417
+ const advisoryContract = buildMinimalAdvisoryContractFromDiff(diffFiles, fallbackPlanId);
2418
+ autoContractPath = (0, change_contract_1.writeChangeContract)(projectRoot, advisoryContract, options.changeContract);
2419
+ changeContractSummary = {
2420
+ path: autoContractPath,
2421
+ exists: true,
2422
+ enforced: false,
2423
+ valid: true,
2424
+ planId: advisoryContract.planId,
2425
+ contractId: advisoryContract.contractId,
2426
+ coverage: {
2427
+ expectedFiles: advisoryContract.expectedFiles.length,
2428
+ changedFiles: diffFiles.length,
2429
+ outOfContractFiles: 0,
2430
+ missingExpectedFiles: 0,
2431
+ blockedFilesTouched: 0,
2432
+ actionMismatches: 0,
2433
+ expectedSymbols: advisoryContract.expectedSymbols?.length || 0,
2434
+ changedSymbols: 0,
2435
+ missingExpectedSymbols: 0,
2436
+ blockedSymbolsTouched: 0,
2437
+ symbolActionMismatches: 0,
2438
+ symbolRenameMatches: 0,
2439
+ toleratedUnexpectedFiles: 0,
2440
+ toleratedMissingExpectedSymbols: 0,
2441
+ },
2442
+ signature: changeContractSummary.signature,
2443
+ violations: [],
2444
+ };
2445
+ }
2446
+ catch {
2447
+ autoContractPath = null;
2448
+ }
2405
2449
  }
2406
- await runPolicyOnlyModeAndExit('fallback_missing_plan');
2450
+ const message = 'No plan linked yet. Ran advisory verification for quick first-run experience. ' +
2451
+ 'Use `neurcode plan` and `neurcode contract import --auto-detect --write-change-contract` for full enforcement.';
2452
+ const advisorySignals = (0, advisory_signals_1.evaluateAdvisorySignals)({
2453
+ diffFiles,
2454
+ summary,
2455
+ });
2456
+ const advisoryWarnCount = advisorySignals.filter((item) => item.severity === 'warn').length;
2457
+ const advisoryVerdict = advisoryWarnCount > 0 ? 'WARN' : 'PASS';
2458
+ const advisoryGrade = advisoryWarnCount > 0 ? 'C' : 'B';
2459
+ const advisoryScore = advisoryWarnCount > 0 ? 60 : 70;
2460
+ const advisoryViolations = advisorySignals.map((item) => ({
2461
+ file: item.files[0] || '.',
2462
+ rule: `advisory:${item.code.toLowerCase()}`,
2463
+ severity: item.severity === 'warn' ? 'warn' : 'allow',
2464
+ message: `${item.title}: ${item.detail}`,
2465
+ }));
2466
+ recordVerifyEvent(advisoryVerdict, `advisory_missing_plan;signals=${advisorySignals.length};warn=${advisoryWarnCount}`, diffFiles.map((f) => f.path));
2467
+ if (options.json) {
2468
+ emitVerifyJson({
2469
+ grade: advisoryGrade,
2470
+ score: advisoryScore,
2471
+ verdict: advisoryVerdict,
2472
+ violations: advisoryViolations,
2473
+ adherenceScore: advisoryScore,
2474
+ bloatCount: 0,
2475
+ bloatFiles: [],
2476
+ plannedFilesModified: 0,
2477
+ totalPlannedFiles: 0,
2478
+ message,
2479
+ scopeGuardPassed: true,
2480
+ mode: 'advisory_missing_plan',
2481
+ advisoryMode: true,
2482
+ advisorySignals,
2483
+ policyOnly: true,
2484
+ policyOnlySource: 'fallback_missing_plan',
2485
+ ...(autoContractPath
2486
+ ? {
2487
+ changeContract: {
2488
+ ...changeContractSummary,
2489
+ path: autoContractPath,
2490
+ },
2491
+ }
2492
+ : {
2493
+ changeContract: changeContractSummary,
2494
+ }),
2495
+ });
2496
+ }
2497
+ else {
2498
+ printFirstRunAdvisoryMessage(options.demo === true);
2499
+ printAdvisorySignals(advisorySignals, options.demo === true);
2500
+ if (autoContractPath) {
2501
+ console.log(chalk.green(`✅ Auto-generated minimal advisory contract: ${autoContractPath}`));
2502
+ }
2503
+ else if (!changeContractRead.contract) {
2504
+ console.log(chalk.yellow('⚠️ Could not auto-generate advisory contract; continuing without contract.'));
2505
+ }
2506
+ console.log(chalk.dim('Next steps: neurcode plan "<intent>"'));
2507
+ console.log(chalk.dim(' neurcode contract import --auto-detect --write-change-contract'));
2508
+ console.log(chalk.dim(`\nSummary: ${message}\n`));
2509
+ }
2510
+ process.exit(0);
2407
2511
  }
2408
2512
  if (!planId) {
2409
2513
  throw new Error('Plan ID resolution failed unexpectedly');
@@ -2449,6 +2553,7 @@ async function verifyCommand(options) {
2449
2553
  diffFiles,
2450
2554
  contextCandidates: planFiles,
2451
2555
  orgGovernance: orgGovernanceSettings,
2556
+ requireSignedAiLogs: signedLogsRequired,
2452
2557
  signingKey: aiLogSigningKey,
2453
2558
  signingKeyId: aiLogSigningKeyId,
2454
2559
  signingKeys: aiLogSigningKeys,
@@ -2997,6 +3102,7 @@ async function verifyCommand(options) {
2997
3102
  })),
2998
3103
  })),
2999
3104
  }));
3105
+ const changedSymbols = (0, diff_symbols_1.extractDeclaredSymbolsFromDiff)(diffFiles);
3000
3106
  const compiledIntentProof = (0, governance_runtime_1.compileDeterministicConstraints)({
3001
3107
  intentConstraints: intentConstraintsForVerification,
3002
3108
  policyRules: deterministicPolicyRules,
@@ -3068,6 +3174,16 @@ async function verifyCommand(options) {
3068
3174
  ? (0, change_contract_1.evaluateChangeContract)(changeContractRead.contract, {
3069
3175
  planId: finalPlanId,
3070
3176
  changedFiles: changedFiles.map((file) => file.path),
3177
+ changedFileEntries: changedFiles.map((file) => ({
3178
+ path: file.path,
3179
+ changeType: file.changeType,
3180
+ })),
3181
+ changedSymbols: changedSymbols.map((symbol) => ({
3182
+ name: symbol.name,
3183
+ type: symbol.type,
3184
+ action: symbol.action,
3185
+ file: symbol.file,
3186
+ })),
3071
3187
  policyLockFingerprint: (0, policy_packs_1.readPolicyLockFile)(projectRoot).lock?.effective.fingerprint || null,
3072
3188
  compiledPolicyFingerprint: effectiveCompiledPolicy?.fingerprint || null,
3073
3189
  })
@@ -3086,6 +3202,8 @@ async function verifyCommand(options) {
3086
3202
  code: item.code,
3087
3203
  message: item.message,
3088
3204
  file: item.file,
3205
+ symbol: item.symbol,
3206
+ symbolType: item.symbolType,
3089
3207
  expected: item.expected,
3090
3208
  actual: item.actual,
3091
3209
  })),
@@ -3097,9 +3215,8 @@ async function verifyCommand(options) {
3097
3215
  severity: 'block',
3098
3216
  message: item.message,
3099
3217
  }));
3100
- const message = `Change contract enforcement failed: ${changeContractEvaluation.violations
3101
- .map((item) => item.message)
3102
- .join('; ')}`;
3218
+ const message = `Implementation deviates from intended contract (` +
3219
+ `${changeContractEvaluation.violations.length} violation(s)).`;
3103
3220
  if (options.json) {
3104
3221
  emitVerifyJson({
3105
3222
  grade: 'F',
@@ -3120,11 +3237,7 @@ async function verifyCommand(options) {
3120
3237
  });
3121
3238
  }
3122
3239
  else {
3123
- console.log(chalk.red('\n⛔ Change contract enforcement failed'));
3124
- changeContractEvaluation.violations.forEach((item) => {
3125
- console.log(chalk.red(` • ${item.message}`));
3126
- });
3127
- console.log(chalk.dim(` Contract path: ${changeContractRead.path}`));
3240
+ displayChangeContractDrift(changeContractSummary, { advisory: false });
3128
3241
  }
3129
3242
  await recordVerificationIfRequested(options, config, {
3130
3243
  grade: 'F',
@@ -3148,13 +3261,7 @@ async function verifyCommand(options) {
3148
3261
  process.exit(2);
3149
3262
  }
3150
3263
  else if (!changeContractEvaluation.valid && !options.json) {
3151
- console.log(chalk.yellow('\n⚠️ Change contract drift detected (advisory mode)'));
3152
- changeContractEvaluation.violations.slice(0, 5).forEach((item) => {
3153
- console.log(chalk.yellow(` • ${item.message}`));
3154
- });
3155
- if (changeContractEvaluation.violations.length > 5) {
3156
- console.log(chalk.dim(` ... ${changeContractEvaluation.violations.length - 5} more violation(s)`));
3157
- }
3264
+ displayChangeContractDrift(changeContractSummary, { advisory: true });
3158
3265
  }
3159
3266
  }
3160
3267
  // Call verify API
@@ -3912,111 +4019,137 @@ function displayGovernanceInsights(governance, options = {}) {
3912
4019
  });
3913
4020
  }
3914
4021
  }
4022
+ function displayChangeContractDrift(summary, options = { advisory: false }) {
4023
+ const groups = (0, change_contract_1.groupChangeContractViolations)(summary.violations.map((item) => ({
4024
+ code: item.code,
4025
+ message: item.message,
4026
+ ...(item.file ? { file: item.file } : {}),
4027
+ ...(item.symbol ? { symbol: item.symbol } : {}),
4028
+ ...(item.symbolType ? { symbolType: item.symbolType } : {}),
4029
+ ...(item.expected ? { expected: item.expected } : {}),
4030
+ ...(item.actual ? { actual: item.actual } : {}),
4031
+ })));
4032
+ if (groups.length === 0)
4033
+ return;
4034
+ const maxItemsPerGroup = options.maxItemsPerGroup ?? 12;
4035
+ const header = options.advisory
4036
+ ? chalk.yellow('\nWARN ⚠️ Change contract drift detected')
4037
+ : chalk.red('\nFAIL ❌ Change contract enforcement failed');
4038
+ console.log(header);
4039
+ for (const group of groups) {
4040
+ console.log(chalk.white(`\n${group.title}:`));
4041
+ group.items.slice(0, maxItemsPerGroup).forEach((entry) => {
4042
+ console.log(` - ${entry}`);
4043
+ });
4044
+ if (group.items.length > maxItemsPerGroup) {
4045
+ console.log(chalk.dim(` - ... ${group.items.length - maxItemsPerGroup} more`));
4046
+ }
4047
+ console.log(chalk.dim(` Why it matters: ${group.impact}`));
4048
+ }
4049
+ console.log(chalk.dim('\nSummary:'));
4050
+ console.log(chalk.dim('Implementation deviates from intended contract.'));
4051
+ console.log(chalk.dim(`Contract path: ${summary.path}`));
4052
+ }
3915
4053
  /**
3916
4054
  * Display verification results in a formatted report card
3917
4055
  */
3918
4056
  function displayVerifyResults(result, policyViolations) {
3919
- // Calculate grade/score
3920
- // CRITICAL: 0/0 planned files = 'F' (Incomplete)
3921
- // Bloat automatically drops grade by at least one letter
3922
- let grade;
3923
- let gradeColor;
3924
- if (result.totalPlannedFiles === 0 && result.plannedFilesModified === 0) {
3925
- // Special case: No planned files = Incomplete (F)
3926
- grade = 'F';
3927
- gradeColor = chalk.red;
3928
- }
3929
- else if (result.verdict === 'PASS') {
3930
- grade = 'A';
3931
- gradeColor = chalk.green;
3932
- }
3933
- else if (result.verdict === 'WARN') {
3934
- // Base grade calculation
3935
- let baseGrade = result.adherenceScore >= 70 ? 'B' : result.adherenceScore >= 50 ? 'C' : 'D';
3936
- // Bloat drops grade by one letter (B -> C, C -> D, D -> F)
3937
- if (result.bloatCount > 0) {
3938
- if (baseGrade === 'B')
3939
- baseGrade = 'C';
3940
- else if (baseGrade === 'C')
3941
- baseGrade = 'D';
3942
- else if (baseGrade === 'D')
3943
- baseGrade = 'F';
3944
- }
3945
- grade = baseGrade;
3946
- gradeColor = chalk.yellow;
3947
- }
3948
- else {
3949
- grade = 'F';
3950
- gradeColor = chalk.red;
3951
- }
3952
- // Calculate estimated time saved (5 minutes per VERIFY_PASS)
3953
- const estimatedMinutesSaved = result.verdict === 'PASS' ? 5 : 0;
3954
- // Calculate policy compliance percentage
3955
- const policyCompliance = result.bloatCount === 0 ? 100 : Math.max(0, 100 - (result.bloatCount * 10));
3956
- // Display Governance Badge for PASS and FAIL verdicts (high visibility)
3957
- if (result.verdict === 'PASS' || result.verdict === 'FAIL') {
3958
- console.log('\n');
3959
- const borderColor = result.verdict === 'PASS' ? 'green' : 'red';
3960
- const gradeColorFunc = result.verdict === 'PASS' ? chalk.green.bold : chalk.red.bold;
3961
- const badgeContent = [
3962
- `${chalk.bold.white('Governance Badge')}`,
3963
- '',
3964
- `${chalk.cyan('Grade:')} ${gradeColorFunc(grade)} ${chalk.dim(`(${result.adherenceScore}%)`)}`,
3965
- result.verdict === 'PASS' ? `${chalk.cyan('Estimated Time Saved:')} ${chalk.green.bold(`${estimatedMinutesSaved}m`)}` : '',
3966
- `${chalk.cyan('Policy Compliance:')} ${result.verdict === 'PASS' ? chalk.green.bold(`${policyCompliance}%`) : chalk.red.bold(`${policyCompliance}%`)}`,
3967
- ].filter(line => line !== '').join('\n');
3968
- console.log((0, box_1.createBox)(badgeContent, {
3969
- borderColor,
3970
- titleColor: 'white',
3971
- padding: 2,
3972
- }));
3973
- console.log('');
3974
- }
3975
- console.log(chalk.bold.cyan('📋 Plan Adherence Report\n'));
3976
- console.log('━'.repeat(50));
3977
- const scoreDisplay = gradeColor(`Grade: ${grade} (${result.adherenceScore}%)`);
3978
- if (result.verdict === 'PASS') {
3979
- console.log(chalk.green('✅'), scoreDisplay);
4057
+ const verdictLabel = result.verdict === 'PASS'
4058
+ ? chalk.green('PASS ')
4059
+ : result.verdict === 'WARN'
4060
+ ? chalk.yellow('WARN ⚠️')
4061
+ : chalk.red('FAIL ❌');
4062
+ const plannedText = `${result.plannedFilesModified}/${result.totalPlannedFiles}`;
4063
+ console.log(`\n${verdictLabel}`);
4064
+ console.log(chalk.dim(`Plan adherence: ${plannedText} files (${result.adherenceScore}%)`));
4065
+ const maxItems = 20;
4066
+ if (result.bloatCount > 0) {
4067
+ console.log(chalk.red('\nOut-of-scope changes:'));
4068
+ result.bloatFiles.slice(0, maxItems).forEach((file) => {
4069
+ console.log(` - ${file}`);
4070
+ });
4071
+ if (result.bloatFiles.length > maxItems) {
4072
+ console.log(chalk.dim(` - ... ${result.bloatFiles.length - maxItems} more`));
4073
+ }
3980
4074
  }
3981
- else if (result.verdict === 'WARN') {
3982
- console.log(chalk.yellow('⚠️ '), scoreDisplay);
4075
+ if (policyViolations && policyViolations.length > 0) {
4076
+ const blocking = policyViolations.filter((item) => item.severity === 'block');
4077
+ const warnings = policyViolations.filter((item) => item.severity !== 'block');
4078
+ if (blocking.length > 0) {
4079
+ console.log(chalk.red('\nBlocking policy violations:'));
4080
+ blocking.slice(0, maxItems).forEach((item) => {
4081
+ console.log(` - ${item.file}: ${item.message || item.rule}`);
4082
+ });
4083
+ if (blocking.length > maxItems) {
4084
+ console.log(chalk.dim(` - ... ${blocking.length - maxItems} more`));
4085
+ }
4086
+ }
4087
+ if (warnings.length > 0) {
4088
+ console.log(chalk.yellow('\nPolicy warnings:'));
4089
+ warnings.slice(0, maxItems).forEach((item) => {
4090
+ console.log(` - ${item.file}: ${item.message || item.rule}`);
4091
+ });
4092
+ if (warnings.length > maxItems) {
4093
+ console.log(chalk.dim(` - ... ${warnings.length - maxItems} more`));
4094
+ }
4095
+ }
3983
4096
  }
3984
- else {
3985
- console.log(chalk.red(''), scoreDisplay);
4097
+ if (result.bloatCount === 0 && (!policyViolations || policyViolations.length === 0)) {
4098
+ console.log(chalk.green('\nNo drift detected.'));
3986
4099
  }
3987
- console.log('');
3988
- console.log(chalk.bold.white('Adherence:'));
3989
- console.log(` ${result.plannedFilesModified}/${result.totalPlannedFiles} planned files modified`);
3990
- console.log(` ${result.adherenceScore}% adherence to plan`);
3991
- // Display bloat
3992
- if (result.bloatCount > 0) {
3993
- console.log('');
3994
- console.log(chalk.bold.red(`🚫 Bloat Detected: ${result.bloatCount} unexpected file(s)`));
3995
- console.log(chalk.red(' Blocked Bloat:'));
3996
- result.bloatFiles.forEach(file => {
3997
- console.log(chalk.red(` • ${file}`));
3998
- });
4100
+ console.log(chalk.dim(`\nSummary: ${result.message}\n`));
4101
+ }
4102
+ function printFirstRunAdvisoryMessage(demoMode) {
4103
+ console.log(chalk.cyan('\nNeurcode first-run advisory mode'));
4104
+ console.log(chalk.dim('Neurcode checks if your AI-generated code matches your intended plan.'));
4105
+ console.log(chalk.dim('To get full enforcement:'));
4106
+ console.log(chalk.dim('1. Define a plan'));
4107
+ console.log(chalk.dim('2. Generate a contract'));
4108
+ console.log(chalk.dim('Running in advisory mode for now.\n'));
4109
+ if (demoMode) {
4110
+ console.log(chalk.dim('Demo mode: this run is intentionally non-blocking to make evaluation easy.'));
3999
4111
  }
4000
- else {
4001
- console.log('');
4002
- console.log(chalk.green('✅ No bloat detected - all changes match the plan'));
4112
+ }
4113
+ function printAdvisorySignals(signals, demoMode) {
4114
+ if (signals.length === 0) {
4115
+ if (demoMode) {
4116
+ console.log(chalk.dim('No high-signal advisory findings detected for this diff.'));
4117
+ }
4118
+ return;
4003
4119
  }
4004
- // Display custom policy violations from dashboard
4005
- if (policyViolations && policyViolations.length > 0) {
4006
- console.log('');
4007
- const blockCount = policyViolations.filter(v => v.severity === 'block').length;
4008
- const label = blockCount > 0
4009
- ? chalk.bold.red(`🚫 Custom Policy Violations: ${policyViolations.length} (${blockCount} blocking)`)
4010
- : chalk.bold.yellow(`⚠️ Custom Policy Warnings: ${policyViolations.length}`);
4011
- console.log(label);
4012
- policyViolations.forEach(v => {
4013
- const lineColor = v.severity === 'block' ? chalk.red : chalk.yellow;
4014
- console.log(lineColor(` • ${v.file}: ${v.message || v.rule}`));
4120
+ console.log(chalk.yellow('\nAdvisory findings (non-blocking):'));
4121
+ for (const signal of signals) {
4122
+ const severityLabel = signal.severity === 'warn' ? chalk.yellow('[warn]') : chalk.dim('[info]');
4123
+ console.log(`${severityLabel} ${signal.title}`);
4124
+ console.log(chalk.dim(` ${signal.detail}`));
4125
+ signal.files.forEach((file) => {
4126
+ console.log(chalk.dim(` - ${file}`));
4015
4127
  });
4016
4128
  }
4017
- console.log('');
4018
- console.log('━'.repeat(50));
4019
- console.log(chalk.dim(result.message));
4020
- console.log('');
4129
+ }
4130
+ function buildMinimalAdvisoryContractFromDiff(diffFiles, fallbackPlanId) {
4131
+ const expectedFiles = [...new Set(diffFiles.map((file) => toUnixPath(file.path)).filter(Boolean))];
4132
+ const planFiles = expectedFiles.map((path) => {
4133
+ const entry = diffFiles.find((file) => toUnixPath(file.path) === path);
4134
+ const changeType = entry?.changeType;
4135
+ const action = changeType === 'add' ? 'CREATE' : 'MODIFY';
4136
+ return {
4137
+ path,
4138
+ action: action,
4139
+ reason: 'Auto-generated advisory baseline from current diff',
4140
+ };
4141
+ });
4142
+ return (0, change_contract_1.createChangeContract)({
4143
+ planId: fallbackPlanId,
4144
+ intent: 'Advisory baseline generated from current repository diff',
4145
+ expectedFiles,
4146
+ planFiles,
4147
+ options: {
4148
+ enforceExpectedFiles: false,
4149
+ enforceActionMatching: false,
4150
+ enforceExpectedSymbols: false,
4151
+ enforceSymbolActionMatching: false,
4152
+ },
4153
+ });
4021
4154
  }
4022
4155
  //# sourceMappingURL=verify.js.map
package/dist/index.js CHANGED
@@ -62,12 +62,15 @@ catch (error) {
62
62
  const program = new commander_1.Command();
63
63
  const CORE_WORKFLOW_STEPS = [
64
64
  '1) neurcode init',
65
- '2) neurcode plan "Describe the change"',
66
- '3) neurcode prompt',
67
- '4) neurcode verify --record',
68
- '5) neurcode ship "Goal" --max-fix-attempts 2',
65
+ '2) neurcode login',
66
+ '3) neurcode verify --plan-id <id> --enforce-change-contract',
67
+ '4) neurcode fix --plan-id <id> (optional)',
69
68
  ];
70
69
  const ADVANCED_WORKFLOW_HINTS = [
70
+ 'neurcode plan "Describe the change"',
71
+ 'neurcode contract import --auto-detect --write-change-contract',
72
+ 'neurcode prompt',
73
+ 'neurcode ship "Goal" --max-fix-attempts 2',
71
74
  'neurcode ask "<question>"',
72
75
  'neurcode simulate --base origin/main',
73
76
  'neurcode guard start && neurcode guard check --staged',
@@ -121,7 +124,7 @@ showWelcomeIfNeeded().catch(() => {
121
124
  });
122
125
  program
123
126
  .command('start')
124
- .description('Show guided Neurcode flow (init -> plan -> prompt -> verify -> ship)')
127
+ .description('Show guided Neurcode flow (init -> login -> verify -> fix)')
125
128
  .option('--run-init', 'Run `neurcode init` immediately after showing the guide')
126
129
  .option('--json', 'Output machine-readable onboarding metadata')
127
130
  .action(async (options) => {
@@ -643,6 +646,37 @@ program
643
646
  json: options.json === true,
644
647
  });
645
648
  });
649
+ program
650
+ .command('fix')
651
+ .description('Primary alias for remediation loop (verify -> fix -> verify)')
652
+ .option('--goal <text>', 'Goal text for remediation loop')
653
+ .option('--plan-id <id>', 'Plan ID for verify scope checks')
654
+ .option('--project-id <id>', 'Project ID override')
655
+ .option('--max-fix-attempts <n>', 'Maximum remediation attempts (default: 2)', (val) => parseInt(val, 10))
656
+ .option('--policy-only', 'Run in policy-only verification mode')
657
+ .option('--require-plan', 'Fail verify if plan context is missing')
658
+ .option('--strict-artifacts', 'Require deterministic compiled-policy/change-contract artifacts')
659
+ .option('--no-strict-artifacts', 'Disable strict deterministic artifact enforcement')
660
+ .option('--enforce-change-contract', 'Require change contract enforcement')
661
+ .option('--no-enforce-change-contract', 'Disable change contract enforcement')
662
+ .option('--require-runtime-guard', 'Require runtime guard checks before each remediation attempt')
663
+ .option('--no-record', 'Disable cloud recording during verify/ship runs')
664
+ .option('--json', 'Output machine-readable JSON')
665
+ .action((options) => {
666
+ (0, remediate_1.remediateCommand)({
667
+ goal: options.goal,
668
+ planId: options.planId,
669
+ projectId: options.projectId,
670
+ maxFixAttempts: Number.isFinite(options.maxFixAttempts) ? options.maxFixAttempts : undefined,
671
+ policyOnly: options.policyOnly === true,
672
+ requirePlan: options.requirePlan === true,
673
+ strictArtifacts: options.strictArtifacts !== false,
674
+ enforceChangeContract: options.enforceChangeContract !== false,
675
+ requireRuntimeGuard: options.requireRuntimeGuard === true,
676
+ noRecord: options.record === false,
677
+ json: options.json === true,
678
+ });
679
+ });
646
680
  program
647
681
  .command('verify')
648
682
  .description('Verify plan adherence - Compare current changes against an Architect Plan')
@@ -668,6 +702,7 @@ program
668
702
  .option('--head', 'Verify changes against HEAD')
669
703
  .option('--base <ref>', 'Verify changes against a specific base ref')
670
704
  .option('--explain', 'Include AI change justification details in human-readable output')
705
+ .option('--demo', 'Demo mode: print extra explanatory output')
671
706
  .option('--json', 'Output results as JSON')
672
707
  .option('--record', 'Report verification results to Neurcode Cloud')
673
708
  .option('--api-key <key>', 'Neurcode API Key (overrides config and env var)')
@@ -679,6 +714,7 @@ program
679
714
  staged: options.staged,
680
715
  head: options.head,
681
716
  base: options.base,
717
+ demo: options.demo === true,
682
718
  explain: options.explain === true,
683
719
  json: options.json,
684
720
  record: options.record,
@@ -0,0 +1,20 @@
1
+ import type { DiffFile } from '@neurcode-ai/diff-parser';
2
+ export type AdvisorySignalSeverity = 'info' | 'warn';
3
+ export interface AdvisorySignal {
4
+ code: 'SENSITIVE_DOMAIN_SPAN' | 'DIRECT_DB_IN_REQUEST_LAYER' | 'LARGE_CHANGE_SURFACE' | 'CODE_WITHOUT_TEST_UPDATES' | 'INFRA_AND_APP_MIXED' | 'POSSIBLE_SECRET_ADDITION';
5
+ severity: AdvisorySignalSeverity;
6
+ title: string;
7
+ detail: string;
8
+ files: string[];
9
+ }
10
+ interface AdvisoryInput {
11
+ diffFiles: DiffFile[];
12
+ summary?: {
13
+ totalFiles: number;
14
+ totalAdded: number;
15
+ totalRemoved: number;
16
+ };
17
+ }
18
+ export declare function evaluateAdvisorySignals(input: AdvisoryInput): AdvisorySignal[];
19
+ export {};
20
+ //# sourceMappingURL=advisory-signals.d.ts.map