@neurcode-ai/cli 0.9.47 → 0.9.49

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 (202) hide show
  1. package/LICENSE +201 -0
  2. package/dist/api-client.d.ts.map +1 -0
  3. package/dist/api-client.js.map +1 -0
  4. package/dist/commands/allow.d.ts.map +1 -0
  5. package/dist/commands/allow.js.map +1 -0
  6. package/dist/commands/apply.d.ts.map +1 -0
  7. package/dist/commands/apply.js.map +1 -0
  8. package/dist/commands/approve.d.ts.map +1 -0
  9. package/dist/commands/approve.js.map +1 -0
  10. package/dist/commands/ask.d.ts.map +1 -0
  11. package/dist/commands/ask.js.map +1 -0
  12. package/dist/commands/audit.d.ts.map +1 -0
  13. package/dist/commands/audit.js.map +1 -0
  14. package/dist/commands/bootstrap.d.ts.map +1 -0
  15. package/dist/commands/bootstrap.js.map +1 -0
  16. package/dist/commands/brain.d.ts.map +1 -0
  17. package/dist/commands/brain.js.map +1 -0
  18. package/dist/commands/check.d.ts.map +1 -0
  19. package/dist/commands/check.js.map +1 -0
  20. package/dist/commands/config.d.ts.map +1 -0
  21. package/dist/commands/config.js.map +1 -0
  22. package/dist/commands/contract.d.ts.map +1 -0
  23. package/dist/commands/contract.js.map +1 -0
  24. package/dist/commands/doctor.d.ts.map +1 -0
  25. package/dist/commands/doctor.js.map +1 -0
  26. package/dist/commands/feedback.d.ts.map +1 -0
  27. package/dist/commands/feedback.js.map +1 -0
  28. package/dist/commands/fix.d.ts +12 -0
  29. package/dist/commands/fix.d.ts.map +1 -0
  30. package/dist/commands/fix.js +380 -0
  31. package/dist/commands/fix.js.map +1 -0
  32. package/dist/commands/generate.d.ts +7 -0
  33. package/dist/commands/generate.d.ts.map +1 -0
  34. package/dist/commands/generate.js +117 -0
  35. package/dist/commands/generate.js.map +1 -0
  36. package/dist/commands/guard.d.ts.map +1 -0
  37. package/dist/commands/guard.js.map +1 -0
  38. package/dist/commands/init.d.ts.map +1 -0
  39. package/dist/commands/init.js.map +1 -0
  40. package/dist/commands/login.d.ts.map +1 -0
  41. package/dist/commands/login.js.map +1 -0
  42. package/dist/commands/logout.d.ts.map +1 -0
  43. package/dist/commands/logout.js.map +1 -0
  44. package/dist/commands/map.d.ts.map +1 -0
  45. package/dist/commands/map.js.map +1 -0
  46. package/dist/commands/plan-show.d.ts +6 -0
  47. package/dist/commands/plan-show.d.ts.map +1 -0
  48. package/dist/commands/plan-show.js +33 -0
  49. package/dist/commands/plan-show.js.map +1 -0
  50. package/dist/commands/plan-slo.d.ts.map +1 -0
  51. package/dist/commands/plan-slo.js.map +1 -0
  52. package/dist/commands/plan.d.ts.map +1 -0
  53. package/dist/commands/plan.js.map +1 -0
  54. package/dist/commands/policy.d.ts.map +1 -0
  55. package/dist/commands/policy.js.map +1 -0
  56. package/dist/commands/prompt.d.ts.map +1 -0
  57. package/dist/commands/prompt.js.map +1 -0
  58. package/dist/commands/refactor.d.ts.map +1 -0
  59. package/dist/commands/refactor.js.map +1 -0
  60. package/dist/commands/remediate.d.ts.map +1 -0
  61. package/dist/commands/remediate.js.map +1 -0
  62. package/dist/commands/repo.d.ts.map +1 -0
  63. package/dist/commands/repo.js.map +1 -0
  64. package/dist/commands/revert.d.ts.map +1 -0
  65. package/dist/commands/revert.js.map +1 -0
  66. package/dist/commands/security.d.ts.map +1 -0
  67. package/dist/commands/security.js.map +1 -0
  68. package/dist/commands/session.d.ts.map +1 -0
  69. package/dist/commands/session.js.map +1 -0
  70. package/dist/commands/ship.d.ts.map +1 -0
  71. package/dist/commands/ship.js.map +1 -0
  72. package/dist/commands/simulate.d.ts.map +1 -0
  73. package/dist/commands/simulate.js.map +1 -0
  74. package/dist/commands/start-intent.d.ts +6 -0
  75. package/dist/commands/start-intent.d.ts.map +1 -0
  76. package/dist/commands/start-intent.js +65 -0
  77. package/dist/commands/start-intent.js.map +1 -0
  78. package/dist/commands/verify.d.ts.map +1 -0
  79. package/dist/commands/verify.js +668 -145
  80. package/dist/commands/verify.js.map +1 -0
  81. package/dist/commands/watch.d.ts.map +1 -0
  82. package/dist/commands/watch.js.map +1 -0
  83. package/dist/commands/whoami.d.ts.map +1 -0
  84. package/dist/commands/whoami.js.map +1 -0
  85. package/dist/config.d.ts.map +1 -0
  86. package/dist/config.js.map +1 -0
  87. package/dist/index.d.ts.map +1 -0
  88. package/dist/index.js +115 -55
  89. package/dist/index.js.map +1 -0
  90. package/dist/mcp/context-injector.d.ts +45 -0
  91. package/dist/mcp/context-injector.d.ts.map +1 -0
  92. package/dist/mcp/context-injector.js +587 -0
  93. package/dist/mcp/context-injector.js.map +1 -0
  94. package/dist/mcp/proximity.d.ts +3 -0
  95. package/dist/mcp/proximity.d.ts.map +1 -0
  96. package/dist/mcp/proximity.js +135 -0
  97. package/dist/mcp/proximity.js.map +1 -0
  98. package/dist/rules.d.ts.map +1 -0
  99. package/dist/rules.js.map +1 -0
  100. package/dist/services/integrations/TicketService.d.ts.map +1 -0
  101. package/dist/services/integrations/TicketService.js.map +1 -0
  102. package/dist/services/mapper/ProjectScanner.d.ts.map +1 -0
  103. package/dist/services/mapper/ProjectScanner.js.map +1 -0
  104. package/dist/services/project-knowledge-service.d.ts.map +1 -0
  105. package/dist/services/project-knowledge-service.js.map +1 -0
  106. package/dist/services/security/SecurityGuard.d.ts.map +1 -0
  107. package/dist/services/security/SecurityGuard.js.map +1 -0
  108. package/dist/services/toolbox-service.d.ts.map +1 -0
  109. package/dist/services/toolbox-service.js.map +1 -0
  110. package/dist/services/watch/BlobStore.d.ts.map +1 -0
  111. package/dist/services/watch/BlobStore.js.map +1 -0
  112. package/dist/services/watch/CommandPoller.d.ts.map +1 -0
  113. package/dist/services/watch/CommandPoller.js.map +1 -0
  114. package/dist/services/watch/Journal.d.ts.map +1 -0
  115. package/dist/services/watch/Journal.js.map +1 -0
  116. package/dist/services/watch/Sentinel.d.ts.map +1 -0
  117. package/dist/services/watch/Sentinel.js.map +1 -0
  118. package/dist/services/watch/Syncer.d.ts.map +1 -0
  119. package/dist/services/watch/Syncer.js.map +1 -0
  120. package/dist/utils/ROILogger.d.ts.map +1 -0
  121. package/dist/utils/ROILogger.js.map +1 -0
  122. package/dist/utils/RelevanceScorer.d.ts.map +1 -0
  123. package/dist/utils/RelevanceScorer.js.map +1 -0
  124. package/dist/utils/advisory-signals.d.ts.map +1 -0
  125. package/dist/utils/advisory-signals.js.map +1 -0
  126. package/dist/utils/ai-debt-budget.d.ts.map +1 -0
  127. package/dist/utils/ai-debt-budget.js.map +1 -0
  128. package/dist/utils/artifact-signature.d.ts.map +1 -0
  129. package/dist/utils/artifact-signature.js.map +1 -0
  130. package/dist/utils/ask-cache.d.ts.map +1 -0
  131. package/dist/utils/ask-cache.js.map +1 -0
  132. package/dist/utils/box.d.ts.map +1 -0
  133. package/dist/utils/box.js.map +1 -0
  134. package/dist/utils/brain-context.d.ts.map +1 -0
  135. package/dist/utils/brain-context.js.map +1 -0
  136. package/dist/utils/breakage-simulator.d.ts.map +1 -0
  137. package/dist/utils/breakage-simulator.js.map +1 -0
  138. package/dist/utils/change-contract.d.ts.map +1 -0
  139. package/dist/utils/change-contract.js.map +1 -0
  140. package/dist/utils/cli-json.d.ts.map +1 -0
  141. package/dist/utils/cli-json.js.map +1 -0
  142. package/dist/utils/custom-policy-rules.d.ts.map +1 -0
  143. package/dist/utils/custom-policy-rules.js.map +1 -0
  144. package/dist/utils/diff-symbols.d.ts.map +1 -0
  145. package/dist/utils/diff-symbols.js.map +1 -0
  146. package/dist/utils/git.d.ts +8 -0
  147. package/dist/utils/git.d.ts.map +1 -0
  148. package/dist/utils/git.js +55 -0
  149. package/dist/utils/git.js.map +1 -0
  150. package/dist/utils/gitignore.d.ts.map +1 -0
  151. package/dist/utils/gitignore.js.map +1 -0
  152. package/dist/utils/governance.d.ts.map +1 -0
  153. package/dist/utils/governance.js.map +1 -0
  154. package/dist/utils/ignore.d.ts.map +1 -0
  155. package/dist/utils/ignore.js.map +1 -0
  156. package/dist/utils/manual-approvals.d.ts.map +1 -0
  157. package/dist/utils/manual-approvals.js.map +1 -0
  158. package/dist/utils/messages.d.ts.map +1 -0
  159. package/dist/utils/messages.js.map +1 -0
  160. package/dist/utils/neurcode-context.d.ts.map +1 -0
  161. package/dist/utils/neurcode-context.js.map +1 -0
  162. package/dist/utils/plan-cache.d.ts.map +1 -0
  163. package/dist/utils/plan-cache.js.map +1 -0
  164. package/dist/utils/plan-slo.d.ts.map +1 -0
  165. package/dist/utils/plan-slo.js.map +1 -0
  166. package/dist/utils/plan-symbols.d.ts.map +1 -0
  167. package/dist/utils/plan-symbols.js.map +1 -0
  168. package/dist/utils/plan-sync.d.ts +34 -0
  169. package/dist/utils/plan-sync.d.ts.map +1 -0
  170. package/dist/utils/plan-sync.js +265 -0
  171. package/dist/utils/plan-sync.js.map +1 -0
  172. package/dist/utils/policy-audit.d.ts.map +1 -0
  173. package/dist/utils/policy-audit.js.map +1 -0
  174. package/dist/utils/policy-compiler.d.ts.map +1 -0
  175. package/dist/utils/policy-compiler.js.map +1 -0
  176. package/dist/utils/policy-exceptions.d.ts.map +1 -0
  177. package/dist/utils/policy-exceptions.js.map +1 -0
  178. package/dist/utils/policy-governance.d.ts.map +1 -0
  179. package/dist/utils/policy-governance.js.map +1 -0
  180. package/dist/utils/policy-packs.d.ts.map +1 -0
  181. package/dist/utils/policy-packs.js.map +1 -0
  182. package/dist/utils/project-detector.d.ts.map +1 -0
  183. package/dist/utils/project-detector.js.map +1 -0
  184. package/dist/utils/project-root.d.ts.map +1 -0
  185. package/dist/utils/project-root.js.map +1 -0
  186. package/dist/utils/repo-links.d.ts.map +1 -0
  187. package/dist/utils/repo-links.js.map +1 -0
  188. package/dist/utils/restore.d.ts.map +1 -0
  189. package/dist/utils/restore.js.map +1 -0
  190. package/dist/utils/runtime-guard.d.ts.map +1 -0
  191. package/dist/utils/runtime-guard.js.map +1 -0
  192. package/dist/utils/scope-telemetry.d.ts.map +1 -0
  193. package/dist/utils/scope-telemetry.js.map +1 -0
  194. package/dist/utils/secret-masking.d.ts.map +1 -0
  195. package/dist/utils/secret-masking.js.map +1 -0
  196. package/dist/utils/state.d.ts.map +1 -0
  197. package/dist/utils/state.js.map +1 -0
  198. package/dist/utils/tier.d.ts.map +1 -0
  199. package/dist/utils/tier.js.map +1 -0
  200. package/dist/utils/user-context.d.ts.map +1 -0
  201. package/dist/utils/user-context.js.map +1 -0
  202. package/package.json +14 -6
@@ -55,6 +55,7 @@ const ignore_1 = require("../utils/ignore");
55
55
  const project_root_1 = require("../utils/project-root");
56
56
  const brain_context_1 = require("../utils/brain-context");
57
57
  const scope_telemetry_1 = require("../utils/scope-telemetry");
58
+ const plan_sync_1 = require("../utils/plan-sync");
58
59
  const policy_packs_1 = require("../utils/policy-packs");
59
60
  const custom_policy_rules_1 = require("../utils/custom-policy-rules");
60
61
  const policy_exceptions_1 = require("../utils/policy-exceptions");
@@ -251,6 +252,15 @@ async function probeApiRuntimeCompatibility(apiUrl) {
251
252
  */
252
253
  const IGNORED_METADATA_FILE_PATTERN = /(^|\/)neurcode\.config\.json$/i;
253
254
  const IGNORED_DIRECTORIES = ['.git/', 'node_modules/'];
255
+ const IGNORED_GOVERNANCE_ARTIFACT_PATTERNS = [
256
+ /(^|\/)neurcode\.policy\.compiled\.json$/i,
257
+ /(^|\/)neurcode\.policy\.audit\.log\.jsonl$/i,
258
+ /(^|\/)neurcode\.policy\.lock\.json$/i,
259
+ /(^|\/)neurcode\.policy\.governance\.json$/i,
260
+ /(^|\/)neurcode\.policy\.exceptions\.json$/i,
261
+ /(^|\/)neurcode\.policy\.json$/i,
262
+ /(^|\/)neurcode\.rules\.json$/i,
263
+ ];
254
264
  function isExcludedFile(filePath) {
255
265
  // Normalize path separators (handle both / and \)
256
266
  const normalizedPath = filePath.replace(/\\/g, '/');
@@ -262,6 +272,12 @@ function isExcludedFile(filePath) {
262
272
  if (/(^|\/)\.neurcode\//.test(normalizedPath)) {
263
273
  return true;
264
274
  }
275
+ // Ignore generated / governance artifacts written by Neurcode itself
276
+ for (const pattern of IGNORED_GOVERNANCE_ARTIFACT_PATTERNS) {
277
+ if (pattern.test(normalizedPath)) {
278
+ return true;
279
+ }
280
+ }
265
281
  // Check if path starts with any excluded prefix
266
282
  const excludedPrefixes = [...IGNORED_DIRECTORIES];
267
283
  // Check prefixes
@@ -657,6 +673,303 @@ function asNumberValue(value) {
657
673
  function asStringValue(value) {
658
674
  return typeof value === 'string' && value.trim().length > 0 ? value : null;
659
675
  }
676
+ const EXPEDITE_FOLLOW_UP_CHECKLIST = [
677
+ 'Add validation back',
678
+ 'Move logic to proper layer',
679
+ 'Remove temporary code',
680
+ ];
681
+ function containsAnyToken(value, tokens) {
682
+ const normalized = value.toLowerCase();
683
+ return tokens.some((token) => normalized.includes(token));
684
+ }
685
+ function isSecurityOrAuthViolation(fileRaw, policyRaw, messageRaw) {
686
+ const combined = `${fileRaw} ${policyRaw} ${messageRaw}`.toLowerCase();
687
+ return containsAnyToken(combined, [
688
+ 'auth',
689
+ 'authentication',
690
+ 'authorization',
691
+ 'security',
692
+ 'permission',
693
+ 'access control',
694
+ 'access_control',
695
+ 'token',
696
+ 'secret',
697
+ 'credential',
698
+ 'encryption',
699
+ 'encrypt',
700
+ 'decrypt',
701
+ 'csrf',
702
+ 'xss',
703
+ 'sql injection',
704
+ 'sqli',
705
+ 'insecure',
706
+ 'vulnerability',
707
+ ]);
708
+ }
709
+ function isCriticalScopeBreach(fileRaw, messageRaw) {
710
+ const combined = `${fileRaw} ${messageRaw}`.toLowerCase();
711
+ return containsAnyToken(combined, [
712
+ 'auth',
713
+ 'security',
714
+ 'secret',
715
+ 'token',
716
+ 'credential',
717
+ 'permission',
718
+ 'infra/terraform',
719
+ 'terraform',
720
+ 'k8s',
721
+ 'helm',
722
+ 'migration',
723
+ 'database/migration',
724
+ 'policy',
725
+ 'contract',
726
+ ]);
727
+ }
728
+ function resolveExpediteModeFromPayload(payload) {
729
+ const explicit = asBooleanFlag(payload.expediteMode);
730
+ if (explicit !== null) {
731
+ return explicit;
732
+ }
733
+ const message = asStringValue(payload.message) || '';
734
+ return containsAnyToken(message, ['hotfix', 'urgent', 'prod down', 'incident', 'expedite']);
735
+ }
736
+ function toVerifySeverity(value) {
737
+ const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';
738
+ if (normalized === 'critical' || normalized === 'block')
739
+ return 'critical';
740
+ if (normalized === 'high')
741
+ return 'high';
742
+ if (normalized === 'warn'
743
+ || normalized === 'warning'
744
+ || normalized === 'medium'
745
+ || normalized === 'low') {
746
+ return 'warning';
747
+ }
748
+ return 'info';
749
+ }
750
+ function toVerifyVerdict(value) {
751
+ const normalized = typeof value === 'string' ? value.trim().toUpperCase() : '';
752
+ if (normalized === 'PASS' || normalized === 'WARN' || normalized === 'FAIL') {
753
+ return normalized;
754
+ }
755
+ return 'FAIL';
756
+ }
757
+ function normalizeScopeIssueMessage(rawMessage) {
758
+ const message = asStringValue(rawMessage);
759
+ return message || 'File modified outside intended scope';
760
+ }
761
+ function pushVerifyIssue(target, seen, key, value) {
762
+ if (seen.has(key))
763
+ return;
764
+ seen.add(key);
765
+ target.push(value);
766
+ }
767
+ function dedupeTriageItems(items) {
768
+ const seen = new Set();
769
+ const output = [];
770
+ for (const item of items) {
771
+ const key = `${item.source}|${item.file.toLowerCase()}|${item.policy.toLowerCase()}|${item.message.toLowerCase()}`;
772
+ if (seen.has(key))
773
+ continue;
774
+ seen.add(key);
775
+ output.push(item);
776
+ }
777
+ return output;
778
+ }
779
+ function toCanonicalVerifyOutput(payload) {
780
+ const verdict = toVerifyVerdict(payload.verdict);
781
+ const violations = [];
782
+ const warnings = [];
783
+ const scopeIssues = [];
784
+ const seenViolations = new Set();
785
+ const seenWarnings = new Set();
786
+ const seenScopeIssues = new Set();
787
+ const addScopeIssue = (fileRaw, messageRaw) => {
788
+ const file = asStringValue(fileRaw) || 'unknown';
789
+ const message = normalizeScopeIssueMessage(messageRaw);
790
+ const key = file.toLowerCase();
791
+ pushVerifyIssue(scopeIssues, seenScopeIssues, key, { file, message });
792
+ };
793
+ const addWarning = (fileRaw, messageRaw, policyRaw) => {
794
+ const file = asStringValue(fileRaw) || 'unknown';
795
+ const message = asStringValue(messageRaw) || 'Warning detected';
796
+ const policy = asStringValue(policyRaw) || 'warning';
797
+ const key = `${file.toLowerCase()}|${message.toLowerCase()}|${policy.toLowerCase()}`;
798
+ pushVerifyIssue(warnings, seenWarnings, key, { file, message, policy });
799
+ };
800
+ const addViolation = (fileRaw, messageRaw, policyRaw, severityRaw) => {
801
+ const file = asStringValue(fileRaw) || 'unknown';
802
+ const message = asStringValue(messageRaw) || 'Policy violation detected';
803
+ const policy = asStringValue(policyRaw) || 'unknown_policy';
804
+ const severity = toVerifySeverity(severityRaw);
805
+ const key = `${file.toLowerCase()}|${message.toLowerCase()}|${policy.toLowerCase()}|${severity}`;
806
+ pushVerifyIssue(violations, seenViolations, key, { file, message, policy, severity });
807
+ };
808
+ const rawScopeIssues = Array.isArray(payload.scopeIssues) ? payload.scopeIssues : [];
809
+ for (const item of rawScopeIssues) {
810
+ const record = asObjectRecord(item);
811
+ if (record) {
812
+ addScopeIssue(record.file, record.message);
813
+ }
814
+ else {
815
+ addScopeIssue(item, null);
816
+ }
817
+ }
818
+ const rawBloatFiles = Array.isArray(payload.bloatFiles) ? payload.bloatFiles : [];
819
+ for (const item of rawBloatFiles) {
820
+ addScopeIssue(item, null);
821
+ }
822
+ const rawWarnings = Array.isArray(payload.warnings) ? payload.warnings : [];
823
+ for (const item of rawWarnings) {
824
+ const record = asObjectRecord(item);
825
+ if (record) {
826
+ addWarning(record.file, record.message, record.policy ?? record.rule);
827
+ }
828
+ else if (typeof item === 'string') {
829
+ addWarning('unknown', item, 'warning');
830
+ }
831
+ }
832
+ const rawViolations = Array.isArray(payload.violations) ? payload.violations : [];
833
+ for (const item of rawViolations) {
834
+ const record = asObjectRecord(item);
835
+ if (!record)
836
+ continue;
837
+ const file = record.file;
838
+ const message = record.message;
839
+ const policy = record.policy ?? record.rule;
840
+ const severity = toVerifySeverity(record.severity);
841
+ const combined = `${String(policy || '').toLowerCase()} ${String(message || '').toLowerCase()}`;
842
+ const isScopeIssue = combined.includes('scope_guard')
843
+ || combined.includes('scope')
844
+ || combined.includes('outside the plan')
845
+ || combined.includes('out of scope');
846
+ if (isScopeIssue) {
847
+ addScopeIssue(file, message);
848
+ continue;
849
+ }
850
+ if (severity === 'warning' || severity === 'info') {
851
+ addWarning(file, message, policy);
852
+ continue;
853
+ }
854
+ addViolation(file, message, policy, severity);
855
+ }
856
+ const payloadMessage = asStringValue(payload.message);
857
+ if (payloadMessage
858
+ && violations.length === 0
859
+ && warnings.length === 0
860
+ && scopeIssues.length === 0) {
861
+ addWarning('unknown', payloadMessage, 'verify_result');
862
+ }
863
+ const summaryRecord = asObjectRecord(payload.summary);
864
+ const fileSet = new Set();
865
+ for (const violation of violations)
866
+ fileSet.add(violation.file);
867
+ for (const warning of warnings)
868
+ fileSet.add(warning.file);
869
+ for (const scopeIssue of scopeIssues)
870
+ fileSet.add(scopeIssue.file);
871
+ const totalFilesChanged = (() => {
872
+ const fromSummary = summaryRecord ? asNumberValue(summaryRecord.totalFilesChanged) : null;
873
+ if (fromSummary !== null)
874
+ return Math.max(0, Math.floor(fromSummary));
875
+ const blastRadius = asObjectRecord(payload.blastRadius);
876
+ const fromBlastRadius = blastRadius ? asNumberValue(blastRadius.filesChanged) : null;
877
+ if (fromBlastRadius !== null)
878
+ return Math.max(0, Math.floor(fromBlastRadius));
879
+ return fileSet.size;
880
+ })();
881
+ const driftScoreRaw = asNumberValue(payload.driftScore);
882
+ const driftScore = driftScoreRaw === null
883
+ ? undefined
884
+ : Math.max(0, Math.min(100, Math.round(driftScoreRaw)));
885
+ const expediteModeUsed = resolveExpediteModeFromPayload(payload);
886
+ const scopeTriageItems = scopeIssues.map((item) => ({
887
+ file: item.file,
888
+ message: item.message,
889
+ policy: 'scope_guard',
890
+ severity: 'block',
891
+ source: 'scope',
892
+ }));
893
+ const violationTriageItems = violations.map((item) => ({
894
+ file: item.file,
895
+ message: item.message,
896
+ policy: item.policy,
897
+ severity: item.severity,
898
+ source: 'violation',
899
+ }));
900
+ const warningTriageItems = warnings.map((item) => ({
901
+ file: item.file,
902
+ message: item.message,
903
+ policy: item.policy,
904
+ severity: 'warning',
905
+ source: 'warning',
906
+ }));
907
+ const defaultBlockingItems = dedupeTriageItems([
908
+ ...scopeTriageItems,
909
+ ...violationTriageItems.filter((item) => item.severity === 'critical' || item.severity === 'high'),
910
+ ]);
911
+ const defaultAdvisoryItems = dedupeTriageItems([
912
+ ...warningTriageItems,
913
+ ...violationTriageItems.filter((item) => item.severity === 'warning' || item.severity === 'info'),
914
+ ]);
915
+ const expediteBlockingItems = dedupeTriageItems([
916
+ ...scopeTriageItems.filter((item) => isCriticalScopeBreach(item.file, item.message)),
917
+ ...violationTriageItems.filter((item) => isSecurityOrAuthViolation(item.file, item.policy, item.message)),
918
+ ...warningTriageItems
919
+ .filter((item) => isSecurityOrAuthViolation(item.file, item.policy, item.message))
920
+ .map((item) => ({
921
+ ...item,
922
+ source: 'violation',
923
+ })),
924
+ ]);
925
+ const expediteItems = dedupeTriageItems([
926
+ ...scopeTriageItems
927
+ .filter((item) => !isCriticalScopeBreach(item.file, item.message))
928
+ .map((item) => ({
929
+ ...item,
930
+ source: 'expedite',
931
+ })),
932
+ ...violationTriageItems
933
+ .filter((item) => !isSecurityOrAuthViolation(item.file, item.policy, item.message))
934
+ .map((item) => ({
935
+ ...item,
936
+ source: 'expedite',
937
+ })),
938
+ ...warningTriageItems
939
+ .filter((item) => !isSecurityOrAuthViolation(item.file, item.policy, item.message))
940
+ .map((item) => ({
941
+ ...item,
942
+ source: 'expedite',
943
+ })),
944
+ ]);
945
+ const blockingItems = expediteModeUsed ? expediteBlockingItems : defaultBlockingItems;
946
+ const advisoryItems = expediteModeUsed ? expediteItems : defaultAdvisoryItems;
947
+ return {
948
+ verdict,
949
+ summary: {
950
+ totalFilesChanged,
951
+ totalViolations: violations.length,
952
+ totalWarnings: warnings.length,
953
+ totalScopeIssues: scopeIssues.length,
954
+ },
955
+ violations,
956
+ warnings,
957
+ scopeIssues,
958
+ blockingCount: blockingItems.length,
959
+ advisoryCount: advisoryItems.length,
960
+ blockingItems,
961
+ advisoryItems,
962
+ expediteModeUsed,
963
+ expediteCount: expediteModeUsed ? expediteItems.length : 0,
964
+ expediteItems: expediteModeUsed ? expediteItems : [],
965
+ expediteFollowUpChecklist: expediteModeUsed ? [...EXPEDITE_FOLLOW_UP_CHECKLIST] : [],
966
+ ...(expediteModeUsed ? { expediteNote: 'Expedite Mode used' } : {}),
967
+ ...(typeof driftScore === 'number' ? { driftScore } : {}),
968
+ };
969
+ }
970
+ function emitCanonicalVerifyJson(payload) {
971
+ console.log(JSON.stringify(toCanonicalVerifyOutput(payload), null, 2));
972
+ }
660
973
  function buildDeterministicLayerSummary(payload) {
661
974
  const verdict = asStringValue(payload.verdict) || 'UNKNOWN';
662
975
  const mode = asStringValue(payload.mode) || 'unknown';
@@ -826,6 +1139,13 @@ function isGitRepository(cwd) {
826
1139
  return false;
827
1140
  }
828
1141
  }
1142
+ function resolveVerifyExpediteMode(projectRoot) {
1143
+ if (isEnabledFlag(process.env.NEURCODE_EXPEDITE_MODE) || isEnabledFlag(process.env.NEURCODE_MCP_EXPEDITE_MODE)) {
1144
+ return true;
1145
+ }
1146
+ const branchName = (0, git_1.detectCurrentGitBranch)(projectRoot) || process.env.GITHUB_REF_NAME || '';
1147
+ return containsAnyToken(branchName, ['hotfix', 'urgent', 'prod-down', 'prod_down', 'prod down', 'incident', 'expedite']);
1148
+ }
829
1149
  function isSignedAiLogsRequired(orgGovernanceSettings) {
830
1150
  const explicitRequirement = isEnabledFlag(process.env.NEURCODE_GOVERNANCE_REQUIRE_SIGNED_LOGS) ||
831
1151
  isEnabledFlag(process.env.NEURCODE_AI_LOG_REQUIRE_SIGNED);
@@ -997,18 +1317,12 @@ async function recordVerificationIfRequested(options, config, payload) {
997
1317
  * Execute policy-only verification (General Governance mode)
998
1318
  * Returns the exit code to use
999
1319
  */
1000
- async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRoot, config, client, source, scopeTelemetry, projectId, orgGovernanceSettings, aiLogSigningKey, aiLogSigningKeyId, aiLogSigningKeys, aiLogSigner, compiledPolicyMetadata, changeContractSummary) {
1320
+ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRoot, config, client, source, scopeTelemetry, projectId, orgGovernanceSettings, aiLogSigningKey, aiLogSigningKeyId, aiLogSigningKeys, aiLogSigner, expediteModeEnabled, compiledPolicyArtifact, compiledPolicyMetadata, changeContractSummary) {
1001
1321
  const emitPolicyOnlyJson = (payload) => {
1002
- const enrichedPayload = {
1322
+ emitCanonicalVerifyJson({
1003
1323
  ...payload,
1004
- deterministicLayers: buildDeterministicLayerSummary(payload),
1005
- };
1006
- console.log(JSON.stringify({
1007
- ...enrichedPayload,
1008
- ...(compiledPolicyMetadata ? { policyCompilation: compiledPolicyMetadata } : {}),
1009
- changeContract: changeContractSummary,
1010
- scope: scopeTelemetry,
1011
- }, null, 2));
1324
+ expediteMode: expediteModeEnabled,
1325
+ });
1012
1326
  };
1013
1327
  const policyOnlyVerificationSource = 'policy_only';
1014
1328
  const recordPolicyOnlyVerification = async (payload) => recordVerificationIfRequested(options, config, {
@@ -1022,13 +1336,15 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
1022
1336
  if (!options.json) {
1023
1337
  console.log(chalk.cyan('🛡️ General Governance mode (policy only, no plan linked)\n'));
1024
1338
  }
1339
+ const diffFilesForPolicy = diffFiles.filter((f) => !ignoreFilter(f.path));
1340
+ const expectedPolicyOnlyFiles = diffFilesForPolicy.map((file) => file.path);
1025
1341
  const signedLogsRequired = isSignedAiLogsRequired(orgGovernanceSettings);
1026
1342
  const governanceAnalysis = (0, governance_1.evaluateGovernance)({
1027
1343
  projectRoot,
1028
1344
  task: 'Policy-only verification',
1029
- expectedFiles: [],
1030
- diffFiles,
1031
- contextCandidates: diffFiles.map((file) => file.path),
1345
+ expectedFiles: expectedPolicyOnlyFiles,
1346
+ diffFiles: diffFilesForPolicy,
1347
+ contextCandidates: expectedPolicyOnlyFiles,
1032
1348
  orgGovernance: orgGovernanceSettings,
1033
1349
  requireSignedAiLogs: signedLogsRequired,
1034
1350
  signingKey: aiLogSigningKey,
@@ -1333,13 +1649,52 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
1333
1649
  if (!options.json && effectiveRules.policyPack && effectiveRules.policyPackRules.length > 0) {
1334
1650
  console.log(chalk.dim(` Evaluating policy pack: ${effectiveRules.policyPack.packName} (${effectiveRules.policyPack.packId}@${effectiveRules.policyPack.version}, ${effectiveRules.policyPackRules.length} rule(s))`));
1335
1651
  }
1336
- const diffFilesForPolicy = diffFiles.filter((f) => {
1337
- const ignored = ignoreFilter(f.path);
1338
- return !ignored;
1339
- });
1340
1652
  const policyResult = (0, policy_engine_1.evaluateRules)(diffFilesForPolicy, effectiveRules.allRules);
1341
1653
  policyViolations = (policyResult.violations || []);
1342
1654
  policyViolations = policyViolations.filter((v) => !ignoreFilter(v.file));
1655
+ const compiledDeterministicRules = compiledPolicyArtifact
1656
+ ? (0, policy_compiler_1.hydrateCompiledPolicyRules)(compiledPolicyArtifact)
1657
+ : [];
1658
+ const compiledPolicyRuleStatements = compiledPolicyArtifact
1659
+ ? [...compiledPolicyArtifact.statements.policyRules]
1660
+ : [];
1661
+ if (compiledDeterministicRules.length > 0 || compiledPolicyRuleStatements.length > 0) {
1662
+ const fileContents = {};
1663
+ for (const file of diffFilesForPolicy) {
1664
+ const absolutePath = (0, path_1.join)(projectRoot, file.path);
1665
+ if (!(0, fs_1.existsSync)(absolutePath)) {
1666
+ continue;
1667
+ }
1668
+ try {
1669
+ fileContents[file.path] = (0, fs_1.readFileSync)(absolutePath, 'utf-8');
1670
+ }
1671
+ catch {
1672
+ // Best-effort: deterministic checks can still run against diff lines.
1673
+ }
1674
+ }
1675
+ const deterministicEvaluation = (0, governance_runtime_1.evaluatePlanVerification)({
1676
+ planFiles: diffFilesForPolicy.map((file) => ({
1677
+ path: file.path,
1678
+ action: 'MODIFY',
1679
+ })),
1680
+ changedFiles: diffFilesForPolicy,
1681
+ policyRules: compiledPolicyRuleStatements.length > 0 ? compiledPolicyRuleStatements : undefined,
1682
+ extraConstraintRules: compiledDeterministicRules.length > 0 ? compiledDeterministicRules : undefined,
1683
+ fileContents,
1684
+ });
1685
+ if (deterministicEvaluation.constraintViolations.length > 0) {
1686
+ const deterministicViolations = deterministicEvaluation.constraintViolations.map((message) => {
1687
+ const matchedFile = diffFilesForPolicy.find((file) => message.includes(file.path))?.path || '.neurcode/policy-compiled';
1688
+ return {
1689
+ file: matchedFile,
1690
+ rule: 'compiled_policy:deterministic_constraint',
1691
+ severity: 'block',
1692
+ message,
1693
+ };
1694
+ });
1695
+ policyViolations.push(...deterministicViolations);
1696
+ }
1697
+ }
1343
1698
  const localPolicyGovernance = (0, policy_governance_1.readPolicyGovernanceConfig)(projectRoot);
1344
1699
  const governance = (0, policy_governance_1.mergePolicyGovernanceWithOrgOverrides)(localPolicyGovernance, orgGovernanceSettings?.policyGovernance);
1345
1700
  const auditIntegrity = (0, policy_audit_1.verifyPolicyAuditIntegrity)(projectRoot);
@@ -1526,14 +1881,15 @@ async function verifyCommand(options) {
1526
1881
  try {
1527
1882
  const rootResolution = (0, project_root_1.resolveNeurcodeProjectRootWithTrace)(process.cwd());
1528
1883
  const projectRoot = rootResolution.projectRoot;
1884
+ const localPlanSync = (0, plan_sync_1.ensureLocalPlan)(projectRoot);
1885
+ const localPlanExpectedFiles = [...localPlanSync.expectedFiles];
1886
+ const expediteModeEnabled = resolveVerifyExpediteMode(projectRoot);
1529
1887
  const scopeTelemetry = (0, scope_telemetry_1.buildScopeTelemetryPayload)(rootResolution);
1530
1888
  const emitVerifyJson = (payload) => {
1531
- const jsonPayload = {
1889
+ emitCanonicalVerifyJson({
1532
1890
  ...payload,
1533
- deterministicLayers: buildDeterministicLayerSummary(payload),
1534
- scope: scopeTelemetry,
1535
- };
1536
- console.log(JSON.stringify(jsonPayload, null, 2));
1891
+ expediteMode: expediteModeEnabled,
1892
+ });
1537
1893
  };
1538
1894
  if (!isGitRepository(projectRoot)) {
1539
1895
  const message = 'Verify requires a git repository. Initialize git (`git init`) or run this command inside an existing git project.';
@@ -2013,34 +2369,44 @@ async function verifyCommand(options) {
2013
2369
  });
2014
2370
  process.exit(2);
2015
2371
  }
2016
- // Determine which diff to capture (staged + unstaged for full current work)
2372
+ // Determine which diff to capture.
2017
2373
  let diffText;
2374
+ let diffContextLabel = '';
2018
2375
  if (options.staged) {
2019
2376
  diffText = (0, child_process_1.execSync)('git diff --cached', { maxBuffer: 1024 * 1024 * 1024, encoding: 'utf-8' });
2377
+ diffContextLabel = 'staged changes';
2020
2378
  }
2021
2379
  else if (options.base) {
2022
2380
  diffText = (0, git_1.getDiffFromBase)(options.base);
2381
+ diffContextLabel = `working tree vs ${options.base}`;
2023
2382
  }
2024
2383
  else if (options.head) {
2025
2384
  diffText = (0, child_process_1.execSync)('git diff HEAD', { maxBuffer: 1024 * 1024 * 1024, encoding: 'utf-8' });
2385
+ diffContextLabel = 'working tree vs HEAD';
2026
2386
  }
2027
2387
  else {
2028
- // Default: combine staged + unstaged to capture all current work
2029
- try {
2030
- const stagedDiff = (0, child_process_1.execSync)('git diff --cached', { maxBuffer: 1024 * 1024 * 1024, encoding: 'utf-8' });
2031
- const unstagedDiff = (0, child_process_1.execSync)('git diff', { maxBuffer: 1024 * 1024 * 1024, encoding: 'utf-8' });
2032
- diffText = stagedDiff + (stagedDiff && unstagedDiff ? '\n' : '') + unstagedDiff;
2388
+ // Default: resolve a PR-like base context first (origin/main or origin/master).
2389
+ // Fallback to staged diff when base context cannot be resolved.
2390
+ const defaultContext = (0, git_1.resolveDefaultDiffContext)(projectRoot);
2391
+ if (defaultContext.mode === 'base' && defaultContext.baseRef) {
2392
+ diffText = (0, git_1.getDiffFromBase)(defaultContext.baseRef);
2393
+ diffContextLabel = defaultContext.currentBranch
2394
+ ? `${defaultContext.currentBranch} vs ${defaultContext.baseRef}`
2395
+ : `working tree vs ${defaultContext.baseRef}`;
2033
2396
  }
2034
- catch {
2035
- // Fallback to HEAD if git commands fail
2036
- diffText = (0, child_process_1.execSync)('git diff HEAD', { maxBuffer: 1024 * 1024 * 1024, encoding: 'utf-8' });
2397
+ else {
2398
+ diffText = (0, child_process_1.execSync)('git diff --cached', { maxBuffer: 1024 * 1024 * 1024, encoding: 'utf-8' });
2399
+ diffContextLabel = 'staged changes (fallback)';
2037
2400
  }
2038
2401
  }
2402
+ if (!options.json && diffContextLabel) {
2403
+ console.log(chalk.dim(` Diff context: ${diffContextLabel}`));
2404
+ }
2039
2405
  const untrackedDiffFiles = getUntrackedDiffFiles(projectRoot);
2040
2406
  if (!diffText.trim() && untrackedDiffFiles.length === 0) {
2041
2407
  if (!options.json) {
2042
- console.log(chalk.yellow('⚠️ No changes detected'));
2043
- console.log(chalk.dim(' Make sure you have staged or unstaged changes to verify'));
2408
+ console.log(chalk.yellow('⚠️ No changes detected in current diff context.'));
2409
+ console.log(chalk.dim(' Tip: Ensure changes are staged or run against a base branch.'));
2044
2410
  }
2045
2411
  else {
2046
2412
  emitVerifyJson({
@@ -2053,7 +2419,7 @@ async function verifyCommand(options) {
2053
2419
  bloatFiles: [],
2054
2420
  plannedFilesModified: 0,
2055
2421
  totalPlannedFiles: 0,
2056
- message: 'No changes detected',
2422
+ message: 'No changes detected in current diff context.',
2057
2423
  scopeGuardPassed: false,
2058
2424
  });
2059
2425
  }
@@ -2083,7 +2449,8 @@ async function verifyCommand(options) {
2083
2449
  const summary = (0, diff_parser_1.getDiffSummary)(diffFiles);
2084
2450
  if (diffFiles.length === 0) {
2085
2451
  if (!options.json) {
2086
- console.log(chalk.yellow('⚠️ No file changes detected in diff'));
2452
+ console.log(chalk.yellow('⚠️ No changes detected in current diff context.'));
2453
+ console.log(chalk.dim(' Tip: Ensure changes are staged or run against a base branch.'));
2087
2454
  }
2088
2455
  else {
2089
2456
  emitVerifyJson({
@@ -2096,7 +2463,7 @@ async function verifyCommand(options) {
2096
2463
  bloatFiles: [],
2097
2464
  plannedFilesModified: 0,
2098
2465
  totalPlannedFiles: 0,
2099
- message: 'No file changes detected in diff',
2466
+ message: 'No changes detected in current diff context.',
2100
2467
  scopeGuardPassed: false,
2101
2468
  });
2102
2469
  }
@@ -2318,7 +2685,7 @@ async function verifyCommand(options) {
2318
2685
  process.exit(2);
2319
2686
  }
2320
2687
  if (!options.json) {
2321
- console.log(chalk.cyan('\n📊 Analyzing changes against plan...'));
2688
+ console.log(chalk.cyan('\n📊 Analyzing change set...'));
2322
2689
  console.log(chalk.dim(` Found ${summary.totalFiles} file(s) changed`));
2323
2690
  console.log(chalk.dim(` ${summary.totalAdded} lines added, ${summary.totalRemoved} lines removed\n`));
2324
2691
  if (options.demo) {
@@ -2326,7 +2693,7 @@ async function verifyCommand(options) {
2326
2693
  }
2327
2694
  }
2328
2695
  const runPolicyOnlyModeAndExit = async (source) => {
2329
- const exitCode = await executePolicyOnlyMode(options, diffFiles, shouldIgnore, projectRoot, config, client, source, scopeTelemetry, projectId || undefined, orgGovernanceSettings, aiLogSigningKey, aiLogSigningKeyId, aiLogSigningKeys, aiLogSigner, compiledPolicyMetadata, changeContractSummary);
2696
+ const exitCode = await executePolicyOnlyMode(options, diffFiles, shouldIgnore, projectRoot, config, client, source, scopeTelemetry, projectId || undefined, orgGovernanceSettings, aiLogSigningKey, aiLogSigningKeyId, aiLogSigningKeys, aiLogSigner, expediteModeEnabled, compiledPolicyRead.artifact, compiledPolicyMetadata, changeContractSummary);
2330
2697
  const changedFiles = diffFiles.map((f) => f.path);
2331
2698
  const verdict = exitCode === 2 ? 'FAIL' : exitCode === 1 ? 'WARN' : 'PASS';
2332
2699
  recordVerifyEvent(verdict, `policy_only_source=${source};exit=${exitCode}`, changedFiles);
@@ -2341,6 +2708,7 @@ async function verifyCommand(options) {
2341
2708
  const requirePlan = options.requirePlan === true
2342
2709
  || process.env.NEURCODE_VERIFY_REQUIRE_PLAN === '1'
2343
2710
  || strictArtifactMode;
2711
+ let useLocalPlanSync = false;
2344
2712
  // Get planId: Priority 1: options flag, Priority 2: state file (.neurcode/config.json), Priority 3: legacy config
2345
2713
  let planId = options.planId;
2346
2714
  if (!planId) {
@@ -2373,6 +2741,19 @@ async function verifyCommand(options) {
2373
2741
  }
2374
2742
  }
2375
2743
  }
2744
+ if (planId === 'local-plan-sync' && localPlanExpectedFiles.length > 0) {
2745
+ useLocalPlanSync = true;
2746
+ if (!options.json) {
2747
+ console.log(chalk.dim(` Using Plan Sync from .neurcode/plan.json (${localPlanExpectedFiles.length} expected file(s))`));
2748
+ }
2749
+ }
2750
+ if (!planId && localPlanExpectedFiles.length > 0) {
2751
+ planId = 'local-plan-sync';
2752
+ useLocalPlanSync = true;
2753
+ if (!options.json) {
2754
+ console.log(chalk.dim(` Using Plan Sync from .neurcode/plan.json (${localPlanExpectedFiles.length} expected file(s))`));
2755
+ }
2756
+ }
2376
2757
  // If no planId found, either enforce strict requirement or fall back to policy-only mode.
2377
2758
  if (!planId) {
2378
2759
  if (requirePlan) {
@@ -2527,37 +2908,60 @@ async function verifyCommand(options) {
2527
2908
  }
2528
2909
  // Track if scope guard passed - this takes priority over AI grading
2529
2910
  let scopeGuardPassed = false;
2911
+ let scopeGuardExpediteBypass = false;
2530
2912
  let governanceResult = null;
2531
2913
  let planFilesForVerification = [];
2532
2914
  let intentConstraintsForVerification;
2533
2915
  try {
2534
2916
  // Step A: Get Modified Files (already have from diffFiles)
2535
2917
  const modifiedFiles = diffFiles.map(f => f.path);
2536
- // Step B: Fetch Plan and Session Data
2537
- const planData = await client.getPlan(finalPlanId);
2538
- // Extract original intent from plan (for constraint checking)
2539
- const originalIntent = planData.intent || '';
2540
- const planTitle = typeof planData.content.title === 'string'
2541
- ? planData.content.title?.trim()
2542
- : '';
2543
- const planSummary = typeof planData.content.summary === 'string' ? planData.content.summary.trim() : '';
2544
- const governanceTask = planTitle || planSummary || originalIntent || 'Plan verification';
2545
- // Get approved files from plan (only files with action CREATE or MODIFY)
2546
- const planFiles = planData.content.files
2547
- .filter(f => f.action === 'CREATE' || f.action === 'MODIFY')
2548
- .map(f => f.path);
2549
- planFilesForVerification = [...planFiles];
2918
+ // Step B: Resolve plan scope from remote plan or local Plan Sync.
2919
+ let originalIntent = '';
2920
+ let governanceTask = 'Plan verification';
2921
+ let planFiles = [];
2922
+ let planDependencies = [];
2923
+ let remotePlanSessionId = null;
2924
+ if (useLocalPlanSync) {
2925
+ const localIntent = (localPlanSync.intent || '').trim();
2926
+ const localConstraintText = localPlanSync.constraints.length > 0
2927
+ ? localPlanSync.constraints.join('; ')
2928
+ : '';
2929
+ planFiles = [...localPlanExpectedFiles];
2930
+ originalIntent = localIntent || localConstraintText;
2931
+ governanceTask = localIntent
2932
+ ? `Local Plan Sync: ${localIntent}`
2933
+ : 'Local Plan Sync verification';
2934
+ if (!options.json) {
2935
+ console.log(chalk.dim(` Plan Sync scope loaded: ${planFiles.length} file(s)`));
2936
+ }
2937
+ }
2938
+ else {
2939
+ const planData = await client.getPlan(finalPlanId);
2940
+ // Extract original intent from plan (for constraint checking)
2941
+ originalIntent = planData.intent || '';
2942
+ const planTitle = typeof planData.content.title === 'string'
2943
+ ? planData.content.title?.trim()
2944
+ : '';
2945
+ const planSummary = typeof planData.content.summary === 'string' ? planData.content.summary.trim() : '';
2946
+ governanceTask = planTitle || planSummary || originalIntent || 'Plan verification';
2947
+ // Get approved files from plan (only files with action CREATE or MODIFY)
2948
+ planFiles = planData.content.files
2949
+ .filter((f) => f.action === 'CREATE' || f.action === 'MODIFY')
2950
+ .map((f) => f.path);
2951
+ planDependencies = Array.isArray(planData.content.dependencies)
2952
+ ? planData.content.dependencies.filter((item) => typeof item === 'string')
2953
+ : [];
2954
+ remotePlanSessionId = planData.sessionId || null;
2955
+ }
2956
+ planFilesForVerification = [...new Set([...planFiles, ...localPlanExpectedFiles])];
2550
2957
  intentConstraintsForVerification = originalIntent || undefined;
2551
- const planDependencies = Array.isArray(planData.content.dependencies)
2552
- ? planData.content.dependencies.filter((item) => typeof item === 'string')
2553
- : [];
2554
2958
  governanceResult = (0, governance_1.evaluateGovernance)({
2555
2959
  projectRoot,
2556
2960
  task: governanceTask,
2557
- expectedFiles: planFiles,
2961
+ expectedFiles: planFilesForVerification,
2558
2962
  expectedDependencies: planDependencies,
2559
2963
  diffFiles,
2560
- contextCandidates: planFiles,
2964
+ contextCandidates: planFilesForVerification,
2561
2965
  orgGovernance: orgGovernanceSettings,
2562
2966
  requireSignedAiLogs: signedLogsRequired,
2563
2967
  signingKey: aiLogSigningKey,
@@ -2570,8 +2974,8 @@ async function verifyCommand(options) {
2570
2974
  // This is the session_id string needed to fetch the session
2571
2975
  let sessionIdString = (0, state_1.getSessionId)() || configData.sessionId || configData.lastSessionId || null;
2572
2976
  // Fallback: Use sessionId from plan if not in config
2573
- if (!sessionIdString && planData.sessionId) {
2574
- sessionIdString = planData.sessionId;
2977
+ if (!sessionIdString && remotePlanSessionId) {
2978
+ sessionIdString = remotePlanSessionId;
2575
2979
  if ((process.env.DEBUG || process.env.VERBOSE) && !options.json) {
2576
2980
  console.log(chalk.dim(` Using sessionId from plan: ${sessionIdString.substring(0, 8)}...`));
2577
2981
  }
@@ -2605,17 +3009,24 @@ async function verifyCommand(options) {
2605
3009
  }
2606
3010
  }
2607
3011
  // Step C: The Intersection Logic
2608
- const approvedSet = new Set([...planFiles, ...allowedFiles]);
3012
+ const approvedSet = new Set([...planFilesForVerification, ...allowedFiles]);
2609
3013
  const violations = modifiedFiles.filter(f => !approvedSet.has(f));
2610
3014
  const filteredViolations = violations.filter((p) => !shouldIgnore(p));
2611
3015
  // Step D: The Block (only report scope violations for non-ignored files)
2612
3016
  if (filteredViolations.length > 0) {
3017
+ const criticalScopeViolations = expediteModeEnabled
3018
+ ? filteredViolations.filter((file) => isCriticalScopeBreach(file, 'File modified outside the plan'))
3019
+ : filteredViolations;
3020
+ const expediteScopeViolations = expediteModeEnabled
3021
+ ? filteredViolations.filter((file) => !criticalScopeViolations.includes(file))
3022
+ : [];
3023
+ const shouldBlockForScope = !expediteModeEnabled || criticalScopeViolations.length > 0;
2613
3024
  const aiDebtSummaryForScope = toAiDebtSummary((0, ai_debt_budget_1.evaluateAiDebtBudget)({
2614
3025
  diffFiles,
2615
3026
  bloatCount: filteredViolations.length,
2616
3027
  config: aiDebtConfig,
2617
3028
  }));
2618
- recordVerifyEvent('FAIL', `scope_violation=${filteredViolations.length}`, modifiedFiles, finalPlanId);
3029
+ recordVerifyEvent(shouldBlockForScope ? 'FAIL' : 'WARN', `${shouldBlockForScope ? 'scope_violation' : 'scope_expedite'}=${filteredViolations.length}`, modifiedFiles, finalPlanId);
2619
3030
  const scopeViolationItems = filteredViolations.map((file) => ({
2620
3031
  file,
2621
3032
  rule: 'scope_guard',
@@ -2627,8 +3038,10 @@ async function verifyCommand(options) {
2627
3038
  ...scopeViolationItems,
2628
3039
  ...aiDebtViolationItems,
2629
3040
  ];
2630
- const scopeViolationMessage = `Scope violation: ${filteredViolations.length} file(s) modified outside the plan`;
2631
- if (options.json) {
3041
+ const scopeViolationMessage = shouldBlockForScope
3042
+ ? `Scope violation: ${criticalScopeViolations.length} critical file(s) modified outside the plan`
3043
+ : `Expedite scope warning: ${expediteScopeViolations.length} non-critical file(s) modified outside the plan`;
3044
+ if (shouldBlockForScope && options.json) {
2632
3045
  // Output JSON for scope violation BEFORE exit. Must include violations for GitHub Action annotations.
2633
3046
  const jsonOutput = {
2634
3047
  grade: 'F',
@@ -2639,12 +3052,13 @@ async function verifyCommand(options) {
2639
3052
  bloatCount: filteredViolations.length,
2640
3053
  bloatFiles: filteredViolations,
2641
3054
  plannedFilesModified: 0,
2642
- totalPlannedFiles: planFiles.length,
3055
+ totalPlannedFiles: planFilesForVerification.length,
2643
3056
  message: scopeViolationMessage,
2644
3057
  scopeGuardPassed: false,
2645
3058
  mode: 'plan_enforced',
2646
3059
  policyOnly: false,
2647
3060
  aiDebt: aiDebtSummaryForScope,
3061
+ ...(expediteModeEnabled ? { expediteMode: true } : {}),
2648
3062
  ...(governanceResult
2649
3063
  ? buildGovernancePayload(governanceResult, orgGovernanceSettings, {
2650
3064
  changeContract: changeContractSummary,
@@ -2677,18 +3091,25 @@ async function verifyCommand(options) {
2677
3091
  });
2678
3092
  process.exit(1);
2679
3093
  }
2680
- else {
3094
+ else if (shouldBlockForScope) {
2681
3095
  // Human-readable output only when NOT in json mode
2682
3096
  console.log(chalk.red('\n⛔ SCOPE VIOLATION'));
2683
3097
  console.log(chalk.red('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
2684
3098
  console.log(chalk.red('The following files were modified but are not in the plan:'));
2685
3099
  console.log('');
2686
- filteredViolations.forEach(file => {
3100
+ criticalScopeViolations.forEach(file => {
2687
3101
  console.log(chalk.red(` • ${file}`));
2688
3102
  });
3103
+ if (expediteModeEnabled && expediteScopeViolations.length > 0) {
3104
+ console.log('');
3105
+ console.log(chalk.yellow('Non-critical scope files (can be followed up under expedite mode):'));
3106
+ expediteScopeViolations.forEach((file) => {
3107
+ console.log(chalk.yellow(` • ${file}`));
3108
+ });
3109
+ }
2689
3110
  console.log('');
2690
3111
  console.log(chalk.yellow('To unblock these files, run:'));
2691
- filteredViolations.forEach(file => {
3112
+ criticalScopeViolations.forEach(file => {
2692
3113
  console.log(chalk.dim(` neurcode allow ${file}`));
2693
3114
  });
2694
3115
  if (aiDebtSummaryForScope.mode !== 'off') {
@@ -2735,11 +3156,30 @@ async function verifyCommand(options) {
2735
3156
  });
2736
3157
  process.exit(1);
2737
3158
  }
3159
+ else {
3160
+ scopeGuardExpediteBypass = true;
3161
+ if (!options.json) {
3162
+ console.log(chalk.yellow('\n⚠️ Expedite scope relaxation applied (non-critical scope only).'));
3163
+ expediteScopeViolations.forEach((file) => {
3164
+ console.log(chalk.yellow(` • ${file}`));
3165
+ });
3166
+ console.log(chalk.dim(' Follow-up checklist:'));
3167
+ EXPEDITE_FOLLOW_UP_CHECKLIST.forEach((item) => {
3168
+ console.log(chalk.dim(` - ${item}`));
3169
+ });
3170
+ console.log(chalk.dim(' Note: Expedite Mode used\n'));
3171
+ }
3172
+ }
2738
3173
  }
2739
3174
  // Scope guard passed - all files are approved or allowed
2740
3175
  scopeGuardPassed = true;
2741
3176
  if (!options.json) {
2742
- console.log(chalk.green('✅ All modified files are approved or allowed'));
3177
+ if (scopeGuardExpediteBypass) {
3178
+ console.log(chalk.green('✅ Scope guard passed with expedite relaxation for non-critical scope changes'));
3179
+ }
3180
+ else {
3181
+ console.log(chalk.green('✅ All modified files are approved or allowed'));
3182
+ }
2743
3183
  console.log('');
2744
3184
  }
2745
3185
  }
@@ -2823,7 +3263,7 @@ async function verifyCommand(options) {
2823
3263
  const lockViolationItems = toPolicyLockViolations(policyLockEvaluation.mismatches);
2824
3264
  recordVerifyEvent('FAIL', 'policy_lock_mismatch', diffFiles.map((f) => f.path), finalPlanId);
2825
3265
  if (options.json) {
2826
- console.log(JSON.stringify({
3266
+ emitVerifyJson({
2827
3267
  grade: 'F',
2828
3268
  score: 0,
2829
3269
  verdict: 'FAIL',
@@ -2851,7 +3291,7 @@ async function verifyCommand(options) {
2851
3291
  path: policyLockEvaluation.lockPath,
2852
3292
  mismatches: policyLockEvaluation.mismatches,
2853
3293
  },
2854
- }, null, 2));
3294
+ });
2855
3295
  }
2856
3296
  else {
2857
3297
  console.log(chalk.red('\n❌ Policy lock baseline mismatch'));
@@ -3270,35 +3710,20 @@ async function verifyCommand(options) {
3270
3710
  displayChangeContractDrift(changeContractSummary, { advisory: true });
3271
3711
  }
3272
3712
  }
3273
- // Call verify API
3713
+ // Call verify API (or deterministic local evaluation for Plan Sync scope mode)
3274
3714
  if (!options.json) {
3275
- console.log(chalk.dim(' Sending to Neurcode API...\n'));
3276
- if (options.asyncMode) {
3277
- console.log(chalk.dim(' Queue-backed verification enabled (async job mode).'));
3715
+ if (useLocalPlanSync) {
3716
+ console.log(chalk.dim(' Using local Plan Sync deterministic verification (no API plan lookup).\n'));
3717
+ }
3718
+ else {
3719
+ console.log(chalk.dim(' Sending to Neurcode API...\n'));
3720
+ if (options.asyncMode) {
3721
+ console.log(chalk.dim(' Queue-backed verification enabled (async job mode).'));
3722
+ }
3278
3723
  }
3279
3724
  }
3280
3725
  try {
3281
- let verifySource = 'api';
3282
- let verifyResult;
3283
- try {
3284
- verifyResult = await client.verifyPlan(finalPlanId, diffStats, changedFiles, projectId, intentConstraintsForVerification, deterministicPolicyRules, 'api', compiledPolicyMetadata, {
3285
- async: options.asyncMode === true,
3286
- pollIntervalMs: Number.isFinite(options.verifyJobPollMs) ? options.verifyJobPollMs : undefined,
3287
- timeoutMs: Number.isFinite(options.verifyJobTimeoutMs) ? options.verifyJobTimeoutMs : undefined,
3288
- idempotencyKey: options.verifyIdempotencyKey,
3289
- maxAttempts: Number.isFinite(options.verifyJobMaxAttempts) ? options.verifyJobMaxAttempts : undefined,
3290
- });
3291
- }
3292
- catch (verifyApiError) {
3293
- if (planFilesForVerification.length === 0) {
3294
- throw verifyApiError;
3295
- }
3296
- verifySource = 'local_fallback';
3297
- if (!options.json) {
3298
- const fallbackReason = verifyApiError instanceof Error ? verifyApiError.message : String(verifyApiError);
3299
- console.log(chalk.yellow('⚠️ Verify API unavailable, using local deterministic fallback.'));
3300
- console.log(chalk.dim(` Reason: ${fallbackReason}`));
3301
- }
3726
+ const runLocalDeterministicVerification = () => {
3302
3727
  const localFileContents = {};
3303
3728
  for (const file of changedFiles) {
3304
3729
  const absolutePath = (0, path_1.join)(projectRoot, file.path);
@@ -3324,7 +3749,7 @@ async function verifyCommand(options) {
3324
3749
  extraConstraintRules: hydratedCompiledPolicyRules.length > 0 ? hydratedCompiledPolicyRules : undefined,
3325
3750
  fileContents: localFileContents,
3326
3751
  });
3327
- verifyResult = {
3752
+ return {
3328
3753
  verificationId: `local-fallback-${Date.now()}`,
3329
3754
  adherenceScore: localEvaluation.adherenceScore,
3330
3755
  bloatCount: localEvaluation.bloatCount,
@@ -3335,6 +3760,35 @@ async function verifyCommand(options) {
3335
3760
  diffSummary: localEvaluation.diffSummary,
3336
3761
  message: localEvaluation.message,
3337
3762
  };
3763
+ };
3764
+ let verifySource = 'api';
3765
+ let verifyResult;
3766
+ if (useLocalPlanSync) {
3767
+ verifySource = 'local_fallback';
3768
+ verifyResult = runLocalDeterministicVerification();
3769
+ }
3770
+ else {
3771
+ try {
3772
+ verifyResult = await client.verifyPlan(finalPlanId, diffStats, changedFiles, projectId, intentConstraintsForVerification, deterministicPolicyRules, 'api', compiledPolicyMetadata, {
3773
+ async: options.asyncMode === true,
3774
+ pollIntervalMs: Number.isFinite(options.verifyJobPollMs) ? options.verifyJobPollMs : undefined,
3775
+ timeoutMs: Number.isFinite(options.verifyJobTimeoutMs) ? options.verifyJobTimeoutMs : undefined,
3776
+ idempotencyKey: options.verifyIdempotencyKey,
3777
+ maxAttempts: Number.isFinite(options.verifyJobMaxAttempts) ? options.verifyJobMaxAttempts : undefined,
3778
+ });
3779
+ }
3780
+ catch (verifyApiError) {
3781
+ if (planFilesForVerification.length === 0) {
3782
+ throw verifyApiError;
3783
+ }
3784
+ verifySource = 'local_fallback';
3785
+ if (!options.json) {
3786
+ const fallbackReason = verifyApiError instanceof Error ? verifyApiError.message : String(verifyApiError);
3787
+ console.log(chalk.yellow('⚠️ Verify API unavailable, using local deterministic fallback.'));
3788
+ console.log(chalk.dim(` Reason: ${fallbackReason}`));
3789
+ }
3790
+ verifyResult = runLocalDeterministicVerification();
3791
+ }
3338
3792
  }
3339
3793
  const aiDebtEvaluation = (0, ai_debt_budget_1.evaluateAiDebtBudget)({
3340
3794
  diffFiles,
@@ -3530,7 +3984,7 @@ async function verifyCommand(options) {
3530
3984
  message: effectiveMessage,
3531
3985
  bloatFiles: displayBloatFiles,
3532
3986
  bloatCount: displayBloatFiles.length,
3533
- }, policyViolations);
3987
+ }, policyViolations, expediteModeEnabled);
3534
3988
  if (governanceResult) {
3535
3989
  displayGovernanceInsights(governanceResult, { explain: options.explain });
3536
3990
  }
@@ -3682,6 +4136,10 @@ async function verifyCommand(options) {
3682
4136
  });
3683
4137
  }
3684
4138
  else {
4139
+ console.error(chalk.red('\n❌ Verification failed before completion.'));
4140
+ if (diffFiles.length > 0) {
4141
+ console.log(chalk.dim(` Partial context captured: ${diffFiles.length} changed file(s) in diff.`));
4142
+ }
3685
4143
  if (error instanceof Error) {
3686
4144
  if (error.message.includes('404') || error.message.includes('not found')) {
3687
4145
  console.error(chalk.red(`❌ Error: Plan not found`));
@@ -3702,27 +4160,24 @@ async function verifyCommand(options) {
3702
4160
  catch (error) {
3703
4161
  if (options.json) {
3704
4162
  const errorMessage = error instanceof Error ? error.message : 'Unknown error';
3705
- console.log(JSON.stringify({
3706
- grade: 'F',
3707
- score: 0,
4163
+ emitCanonicalVerifyJson({
3708
4164
  verdict: 'FAIL',
3709
- violations: [],
3710
- adherenceScore: 0,
3711
- bloatCount: 0,
3712
- bloatFiles: [],
3713
- plannedFilesModified: 0,
3714
- totalPlannedFiles: 0,
3715
- message: `Unexpected error: ${errorMessage}`,
3716
- scopeGuardPassed: false,
3717
- scope: {
3718
- scanRoot: process.cwd(),
3719
- startDir: process.cwd(),
3720
- gitRoot: null,
3721
- linkedRepoOverrideUsed: false,
3722
- linkedRepos: [],
3723
- blockedOverride: null,
4165
+ summary: {
4166
+ totalFilesChanged: 0,
4167
+ totalViolations: 0,
4168
+ totalWarnings: 1,
4169
+ totalScopeIssues: 0,
3724
4170
  },
3725
- }, null, 2));
4171
+ violations: [],
4172
+ warnings: [
4173
+ {
4174
+ file: 'unknown',
4175
+ message: `Unexpected error: ${errorMessage}`,
4176
+ policy: 'verify_runtime',
4177
+ },
4178
+ ],
4179
+ scopeIssues: [],
4180
+ });
3726
4181
  }
3727
4182
  else {
3728
4183
  console.error(chalk.red('\n❌ Unexpected error:'));
@@ -4059,7 +4514,7 @@ function displayChangeContractDrift(summary, options = { advisory: false }) {
4059
4514
  /**
4060
4515
  * Display verification results in a formatted report card
4061
4516
  */
4062
- function displayVerifyResults(result, policyViolations) {
4517
+ function displayVerifyResults(result, policyViolations, expediteModeUsed = false) {
4063
4518
  const verdictLabel = result.verdict === 'PASS'
4064
4519
  ? chalk.green('PASS ✅')
4065
4520
  : result.verdict === 'WARN'
@@ -4068,42 +4523,110 @@ function displayVerifyResults(result, policyViolations) {
4068
4523
  const plannedText = `${result.plannedFilesModified}/${result.totalPlannedFiles}`;
4069
4524
  console.log(`\n${verdictLabel}`);
4070
4525
  console.log(chalk.dim(`Plan adherence: ${plannedText} files (${result.adherenceScore}%)`));
4071
- const maxItems = 20;
4072
- if (result.bloatCount > 0) {
4073
- console.log(chalk.red('\nOut-of-scope changes:'));
4074
- result.bloatFiles.slice(0, maxItems).forEach((file) => {
4075
- console.log(` - ${file}`);
4526
+ const maxBlockingItems = 20;
4527
+ const maxAdvisoryItems = 8;
4528
+ const maxExpediteItems = 12;
4529
+ const policyItems = policyViolations || [];
4530
+ const isBlockingSeverity = (severityRaw) => {
4531
+ const normalized = String(severityRaw || '').toLowerCase();
4532
+ return normalized === 'block' || normalized === 'critical' || normalized === 'high';
4533
+ };
4534
+ const scopeItems = result.bloatFiles.map((file) => ({
4535
+ file,
4536
+ message: 'File modified outside intended scope',
4537
+ policy: 'scope_guard',
4538
+ }));
4539
+ const policyTriageItems = policyItems.map((item) => ({
4540
+ file: item.file,
4541
+ message: item.message || item.rule,
4542
+ policy: item.rule || 'policy_violation',
4543
+ severity: item.severity,
4544
+ }));
4545
+ let blockingItems = [
4546
+ ...scopeItems.map((item) => ({
4547
+ file: item.file,
4548
+ message: item.message,
4549
+ })),
4550
+ ...policyTriageItems
4551
+ .filter((item) => isBlockingSeverity(item.severity))
4552
+ .map((item) => ({
4553
+ file: item.file,
4554
+ message: item.message,
4555
+ })),
4556
+ ];
4557
+ let advisoryItems = policyTriageItems
4558
+ .filter((item) => !isBlockingSeverity(item.severity))
4559
+ .map((item) => ({
4560
+ file: item.file,
4561
+ message: item.message,
4562
+ }));
4563
+ let expediteItems = [];
4564
+ if (expediteModeUsed) {
4565
+ blockingItems = [
4566
+ ...scopeItems
4567
+ .filter((item) => isCriticalScopeBreach(item.file, item.message))
4568
+ .map((item) => ({ file: item.file, message: item.message })),
4569
+ ...policyTriageItems
4570
+ .filter((item) => isSecurityOrAuthViolation(item.file, item.policy, item.message))
4571
+ .map((item) => ({ file: item.file, message: item.message })),
4572
+ ];
4573
+ expediteItems = [
4574
+ ...scopeItems
4575
+ .filter((item) => !isCriticalScopeBreach(item.file, item.message))
4576
+ .map((item) => ({ file: item.file, message: item.message })),
4577
+ ...policyTriageItems
4578
+ .filter((item) => !isSecurityOrAuthViolation(item.file, item.policy, item.message))
4579
+ .map((item) => ({ file: item.file, message: item.message })),
4580
+ ];
4581
+ advisoryItems = [];
4582
+ }
4583
+ if (blockingItems.length > 0) {
4584
+ console.log(chalk.red(`\nBLOCKING (${blockingItems.length})`));
4585
+ blockingItems.slice(0, maxBlockingItems).forEach((item) => {
4586
+ console.log(` - ${item.file}: ${item.message}`);
4076
4587
  });
4077
- if (result.bloatFiles.length > maxItems) {
4078
- console.log(chalk.dim(` - ... ${result.bloatFiles.length - maxItems} more`));
4588
+ if (blockingItems.length > maxBlockingItems) {
4589
+ console.log(chalk.dim(` - ... ${blockingItems.length - maxBlockingItems} more`));
4079
4590
  }
4080
4591
  }
4081
- if (policyViolations && policyViolations.length > 0) {
4082
- const blocking = policyViolations.filter((item) => item.severity === 'block');
4083
- const warnings = policyViolations.filter((item) => item.severity !== 'block');
4084
- if (blocking.length > 0) {
4085
- console.log(chalk.red('\nBlocking policy violations:'));
4086
- blocking.slice(0, maxItems).forEach((item) => {
4087
- console.log(` - ${item.file}: ${item.message || item.rule}`);
4088
- });
4089
- if (blocking.length > maxItems) {
4090
- console.log(chalk.dim(` - ... ${blocking.length - maxItems} more`));
4091
- }
4592
+ if (advisoryItems.length > 0) {
4593
+ console.log(chalk.yellow(`\nADVISORY (${advisoryItems.length})`));
4594
+ advisoryItems.slice(0, maxAdvisoryItems).forEach((item) => {
4595
+ console.log(` - ${item.file}: ${item.message}`);
4596
+ });
4597
+ if (advisoryItems.length > maxAdvisoryItems) {
4598
+ console.log(chalk.dim(` - ... ${advisoryItems.length - maxAdvisoryItems} more (summarized)`));
4092
4599
  }
4093
- if (warnings.length > 0) {
4094
- console.log(chalk.yellow('\nPolicy warnings:'));
4095
- warnings.slice(0, maxItems).forEach((item) => {
4096
- console.log(` - ${item.file}: ${item.message || item.rule}`);
4097
- });
4098
- if (warnings.length > maxItems) {
4099
- console.log(chalk.dim(` - ... ${warnings.length - maxItems} more`));
4100
- }
4600
+ }
4601
+ if (expediteModeUsed && expediteItems.length > 0) {
4602
+ console.log(chalk.yellow(`\nEXPEDITE (requires follow-up) (${expediteItems.length})`));
4603
+ expediteItems.slice(0, maxExpediteItems).forEach((item) => {
4604
+ console.log(` - ${item.file}: ${item.message}`);
4605
+ });
4606
+ if (expediteItems.length > maxExpediteItems) {
4607
+ console.log(chalk.dim(` - ... ${expediteItems.length - maxExpediteItems} more (summarized)`));
4101
4608
  }
4609
+ console.log(chalk.dim(' Follow-up checklist:'));
4610
+ EXPEDITE_FOLLOW_UP_CHECKLIST.forEach((checkItem) => {
4611
+ console.log(chalk.dim(` - ${checkItem}`));
4612
+ });
4613
+ console.log(chalk.dim(' Note: Expedite Mode used'));
4102
4614
  }
4103
- if (result.bloatCount === 0 && (!policyViolations || policyViolations.length === 0)) {
4615
+ if (blockingItems.length === 0 && advisoryItems.length === 0 && expediteItems.length === 0) {
4104
4616
  console.log(chalk.green('\nNo drift detected.'));
4105
4617
  }
4106
- console.log(chalk.dim(`\nSummary: ${result.message}\n`));
4618
+ const filesTouched = new Set([
4619
+ ...blockingItems.map((item) => item.file),
4620
+ ...advisoryItems.map((item) => item.file),
4621
+ ...expediteItems.map((item) => item.file),
4622
+ ]).size;
4623
+ if (expediteModeUsed) {
4624
+ console.log(chalk.dim(`\nSummary: ${blockingItems.length} blocking issues, ${expediteItems.length} expedite issues across ${filesTouched} files`));
4625
+ }
4626
+ else {
4627
+ console.log(chalk.dim(`\nSummary: ${blockingItems.length} blocking issues, ${advisoryItems.length} advisory issues across ${filesTouched} files`));
4628
+ }
4629
+ console.log(chalk.dim(`Details: ${result.message}\n`));
4107
4630
  }
4108
4631
  function printFirstRunAdvisoryMessage(demoMode) {
4109
4632
  console.log(chalk.cyan('\nNeurcode first-run advisory mode'));