@neurcode-ai/cli 0.9.49 → 0.9.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/fix.d.ts +1 -0
- package/dist/commands/fix.d.ts.map +1 -1
- package/dist/commands/fix.js +710 -29
- package/dist/commands/fix.js.map +1 -1
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +16 -1
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/patch-apply.d.ts +7 -0
- package/dist/commands/patch-apply.d.ts.map +1 -0
- package/dist/commands/patch-apply.js +85 -0
- package/dist/commands/patch-apply.js.map +1 -0
- package/dist/commands/start-intent.d.ts.map +1 -1
- package/dist/commands/start-intent.js +17 -2
- package/dist/commands/start-intent.js.map +1 -1
- package/dist/commands/verify.d.ts.map +1 -1
- package/dist/commands/verify.js +372 -71
- package/dist/commands/verify.js.map +1 -1
- package/dist/context-engine/graph.d.ts +6 -0
- package/dist/context-engine/graph.d.ts.map +1 -0
- package/dist/context-engine/graph.js +55 -0
- package/dist/context-engine/graph.js.map +1 -0
- package/dist/context-engine/index.d.ts +14 -0
- package/dist/context-engine/index.d.ts.map +1 -0
- package/dist/context-engine/index.js +26 -0
- package/dist/context-engine/index.js.map +1 -0
- package/dist/context-engine/scanner.d.ts +6 -0
- package/dist/context-engine/scanner.d.ts.map +1 -0
- package/dist/context-engine/scanner.js +62 -0
- package/dist/context-engine/scanner.js.map +1 -0
- package/dist/context-engine/scorer.d.ts +9 -0
- package/dist/context-engine/scorer.d.ts.map +1 -0
- package/dist/context-engine/scorer.js +112 -0
- package/dist/context-engine/scorer.js.map +1 -0
- package/dist/context-engine/suggestions.d.ts +12 -0
- package/dist/context-engine/suggestions.d.ts.map +1 -0
- package/dist/context-engine/suggestions.js +22 -0
- package/dist/context-engine/suggestions.js.map +1 -0
- package/dist/daemon/server.d.ts +23 -0
- package/dist/daemon/server.d.ts.map +1 -0
- package/dist/daemon/server.js +222 -0
- package/dist/daemon/server.js.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -1
- package/dist/intent-engine/coverage.d.ts +69 -0
- package/dist/intent-engine/coverage.d.ts.map +1 -0
- package/dist/intent-engine/coverage.js +140 -0
- package/dist/intent-engine/coverage.js.map +1 -0
- package/dist/intent-engine/flow-rules.d.ts +21 -0
- package/dist/intent-engine/flow-rules.d.ts.map +1 -0
- package/dist/intent-engine/flow-rules.js +83 -0
- package/dist/intent-engine/flow-rules.js.map +1 -0
- package/dist/intent-engine/flow-validator.d.ts +29 -0
- package/dist/intent-engine/flow-validator.d.ts.map +1 -0
- package/dist/intent-engine/flow-validator.js +202 -0
- package/dist/intent-engine/flow-validator.js.map +1 -0
- package/dist/intent-engine/graph.d.ts +33 -0
- package/dist/intent-engine/graph.d.ts.map +1 -0
- package/dist/intent-engine/graph.js +67 -0
- package/dist/intent-engine/graph.js.map +1 -0
- package/dist/intent-engine/index.d.ts +35 -0
- package/dist/intent-engine/index.d.ts.map +1 -0
- package/dist/intent-engine/index.js +94 -0
- package/dist/intent-engine/index.js.map +1 -0
- package/dist/intent-engine/indexer.d.ts +18 -0
- package/dist/intent-engine/indexer.d.ts.map +1 -0
- package/dist/intent-engine/indexer.js +100 -0
- package/dist/intent-engine/indexer.js.map +1 -0
- package/dist/intent-engine/matcher.d.ts +35 -0
- package/dist/intent-engine/matcher.d.ts.map +1 -0
- package/dist/intent-engine/matcher.js +522 -0
- package/dist/intent-engine/matcher.js.map +1 -0
- package/dist/intent-engine/parser.d.ts +12 -0
- package/dist/intent-engine/parser.d.ts.map +1 -0
- package/dist/intent-engine/parser.js +93 -0
- package/dist/intent-engine/parser.js.map +1 -0
- package/dist/intent-engine/regression.d.ts +32 -0
- package/dist/intent-engine/regression.d.ts.map +1 -0
- package/dist/intent-engine/regression.js +166 -0
- package/dist/intent-engine/regression.js.map +1 -0
- package/dist/intent-engine/requirements.d.ts +22 -0
- package/dist/intent-engine/requirements.d.ts.map +1 -0
- package/dist/intent-engine/requirements.js +147 -0
- package/dist/intent-engine/requirements.js.map +1 -0
- package/dist/intent-engine/state.d.ts +44 -0
- package/dist/intent-engine/state.d.ts.map +1 -0
- package/dist/intent-engine/state.js +83 -0
- package/dist/intent-engine/state.js.map +1 -0
- package/dist/patch-engine/diff.d.ts +12 -0
- package/dist/patch-engine/diff.d.ts.map +1 -0
- package/dist/patch-engine/diff.js +74 -0
- package/dist/patch-engine/diff.js.map +1 -0
- package/dist/patch-engine/generator.d.ts +13 -0
- package/dist/patch-engine/generator.d.ts.map +1 -0
- package/dist/patch-engine/generator.js +51 -0
- package/dist/patch-engine/generator.js.map +1 -0
- package/dist/patch-engine/index.d.ts +47 -0
- package/dist/patch-engine/index.d.ts.map +1 -0
- package/dist/patch-engine/index.js +182 -0
- package/dist/patch-engine/index.js.map +1 -0
- package/dist/patch-engine/patterns.d.ts +4 -0
- package/dist/patch-engine/patterns.d.ts.map +1 -0
- package/dist/patch-engine/patterns.js +99 -0
- package/dist/patch-engine/patterns.js.map +1 -0
- package/dist/utils/ai-debt-budget.d.ts +3 -2
- package/dist/utils/ai-debt-budget.d.ts.map +1 -1
- package/dist/utils/ai-debt-budget.js +83 -2
- package/dist/utils/ai-debt-budget.js.map +1 -1
- package/package.json +8 -7
- package/LICENSE +0 -201
package/dist/commands/verify.js
CHANGED
|
@@ -56,6 +56,7 @@ 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
58
|
const plan_sync_1 = require("../utils/plan-sync");
|
|
59
|
+
const intent_engine_1 = require("../intent-engine");
|
|
59
60
|
const policy_packs_1 = require("../utils/policy-packs");
|
|
60
61
|
const custom_policy_rules_1 = require("../utils/custom-policy-rules");
|
|
61
62
|
const policy_exceptions_1 = require("../utils/policy-exceptions");
|
|
@@ -847,6 +848,16 @@ function toCanonicalVerifyOutput(payload) {
|
|
|
847
848
|
addScopeIssue(file, message);
|
|
848
849
|
continue;
|
|
849
850
|
}
|
|
851
|
+
// Artifact presence/signature checks are advisory — they must never block a PR.
|
|
852
|
+
// Real governance signal (policy violations, scope drift) should not be obscured
|
|
853
|
+
// by infrastructure setup state.
|
|
854
|
+
const policyStr = String(policy || '').toLowerCase();
|
|
855
|
+
const isArtifactCheck = policyStr === 'deterministic_artifacts_required'
|
|
856
|
+
|| policyStr === 'signed_artifacts_required';
|
|
857
|
+
if (isArtifactCheck) {
|
|
858
|
+
addWarning(file, message, policy);
|
|
859
|
+
continue;
|
|
860
|
+
}
|
|
850
861
|
if (severity === 'warning' || severity === 'info') {
|
|
851
862
|
addWarning(file, message, policy);
|
|
852
863
|
continue;
|
|
@@ -942,9 +953,79 @@ function toCanonicalVerifyOutput(payload) {
|
|
|
942
953
|
source: 'expedite',
|
|
943
954
|
})),
|
|
944
955
|
]);
|
|
945
|
-
|
|
946
|
-
const
|
|
956
|
+
// ── Intent issues and summary from engine ───────────────────────────────
|
|
957
|
+
const rawIntentIssues = Array.isArray(payload.intentIssues) ? payload.intentIssues : [];
|
|
958
|
+
const intentDomains = Array.isArray(payload.intentDomains) ? payload.intentDomains : [];
|
|
959
|
+
const intentSummary = (payload.intentSummary ?? null);
|
|
960
|
+
const rawFlowIssues = Array.isArray(payload.flowIssues) ? payload.flowIssues : [];
|
|
961
|
+
const rawRegressions = Array.isArray(payload.regressions) ? payload.regressions : [];
|
|
962
|
+
// High-severity intent issues become blocking; medium become advisory.
|
|
963
|
+
const intentBlockingTriageItems = rawIntentIssues
|
|
964
|
+
.filter((i) => i.severity === 'high')
|
|
965
|
+
.map((i) => ({
|
|
966
|
+
file: (i.files?.[0]) ?? 'intent-analysis',
|
|
967
|
+
message: i.message,
|
|
968
|
+
policy: i.rule,
|
|
969
|
+
severity: 'high',
|
|
970
|
+
source: 'violation',
|
|
971
|
+
}));
|
|
972
|
+
const intentAdvisoryTriageItems = rawIntentIssues
|
|
973
|
+
.filter((i) => i.severity === 'medium')
|
|
974
|
+
.map((i) => ({
|
|
975
|
+
file: (i.files?.[0]) ?? 'intent-analysis',
|
|
976
|
+
message: i.message,
|
|
977
|
+
policy: i.rule,
|
|
978
|
+
severity: 'warning',
|
|
979
|
+
source: 'warning',
|
|
980
|
+
}));
|
|
981
|
+
// V5: flow issues — high → blocking, medium → advisory
|
|
982
|
+
const flowBlockingTriageItems = rawFlowIssues
|
|
983
|
+
.filter((i) => i.severity === 'high')
|
|
984
|
+
.map((i) => ({
|
|
985
|
+
file: (i.files?.[0]) ?? 'flow-analysis',
|
|
986
|
+
message: i.message,
|
|
987
|
+
policy: i.rule,
|
|
988
|
+
severity: 'high',
|
|
989
|
+
source: 'violation',
|
|
990
|
+
}));
|
|
991
|
+
const flowAdvisoryTriageItems = rawFlowIssues
|
|
992
|
+
.filter((i) => i.severity === 'medium')
|
|
993
|
+
.map((i) => ({
|
|
994
|
+
file: (i.files?.[0]) ?? 'flow-analysis',
|
|
995
|
+
message: i.message,
|
|
996
|
+
policy: i.rule,
|
|
997
|
+
severity: 'warning',
|
|
998
|
+
source: 'warning',
|
|
999
|
+
}));
|
|
1000
|
+
let blockingItems = expediteModeUsed ? expediteBlockingItems : defaultBlockingItems;
|
|
1001
|
+
let advisoryItems = expediteModeUsed ? expediteItems : defaultAdvisoryItems;
|
|
1002
|
+
if (intentBlockingTriageItems.length > 0) {
|
|
1003
|
+
blockingItems = dedupeTriageItems([...blockingItems, ...intentBlockingTriageItems]);
|
|
1004
|
+
}
|
|
1005
|
+
if (intentAdvisoryTriageItems.length > 0) {
|
|
1006
|
+
advisoryItems = dedupeTriageItems([...advisoryItems, ...intentAdvisoryTriageItems]);
|
|
1007
|
+
}
|
|
1008
|
+
if (flowBlockingTriageItems.length > 0) {
|
|
1009
|
+
blockingItems = dedupeTriageItems([...blockingItems, ...flowBlockingTriageItems]);
|
|
1010
|
+
}
|
|
1011
|
+
if (flowAdvisoryTriageItems.length > 0) {
|
|
1012
|
+
advisoryItems = dedupeTriageItems([...advisoryItems, ...flowAdvisoryTriageItems]);
|
|
1013
|
+
}
|
|
1014
|
+
// V6: regressions — always blocking
|
|
1015
|
+
const regressionBlockingTriageItems = rawRegressions.map((r) => ({
|
|
1016
|
+
file: 'regression-analysis',
|
|
1017
|
+
message: r.message,
|
|
1018
|
+
policy: r.rule,
|
|
1019
|
+
severity: 'high',
|
|
1020
|
+
source: 'violation',
|
|
1021
|
+
}));
|
|
1022
|
+
if (regressionBlockingTriageItems.length > 0) {
|
|
1023
|
+
blockingItems = dedupeTriageItems([...regressionBlockingTriageItems, ...blockingItems]);
|
|
1024
|
+
}
|
|
1025
|
+
const grade = verdict === 'PASS' ? 'A' : verdict === 'WARN' ? 'C' : 'F';
|
|
947
1026
|
return {
|
|
1027
|
+
grade,
|
|
1028
|
+
score: violations.length === 0 && warnings.length === 0 && scopeIssues.length === 0 ? 100 : 0,
|
|
948
1029
|
verdict,
|
|
949
1030
|
summary: {
|
|
950
1031
|
totalFilesChanged,
|
|
@@ -959,6 +1040,11 @@ function toCanonicalVerifyOutput(payload) {
|
|
|
959
1040
|
advisoryCount: advisoryItems.length,
|
|
960
1041
|
blockingItems,
|
|
961
1042
|
advisoryItems,
|
|
1043
|
+
intentIssues: rawIntentIssues,
|
|
1044
|
+
intentDomains,
|
|
1045
|
+
intentSummary,
|
|
1046
|
+
flowIssues: rawFlowIssues,
|
|
1047
|
+
regressions: rawRegressions,
|
|
962
1048
|
expediteModeUsed,
|
|
963
1049
|
expediteCount: expediteModeUsed ? expediteItems.length : 0,
|
|
964
1050
|
expediteItems: expediteModeUsed ? expediteItems : [],
|
|
@@ -1080,20 +1166,32 @@ function toAiDebtSummary(evaluation) {
|
|
|
1080
1166
|
observed: item.observed,
|
|
1081
1167
|
budget: item.budget,
|
|
1082
1168
|
message: item.message,
|
|
1169
|
+
...(item.files && item.files.length > 0 ? { files: item.files } : {}),
|
|
1083
1170
|
})),
|
|
1084
1171
|
};
|
|
1085
1172
|
}
|
|
1173
|
+
const ARCH_VIOLATION_CODES = new Set(['db_in_ui', 'missing_validation']);
|
|
1086
1174
|
function toAiDebtReportViolations(summary) {
|
|
1087
1175
|
if (summary.mode === 'off' || summary.violations.length === 0) {
|
|
1088
1176
|
return [];
|
|
1089
1177
|
}
|
|
1090
|
-
const
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
severity
|
|
1095
|
-
|
|
1096
|
-
|
|
1178
|
+
const defaultSeverity = summary.mode === 'enforce' ? 'block' : 'warn';
|
|
1179
|
+
const result = [];
|
|
1180
|
+
for (const item of summary.violations) {
|
|
1181
|
+
// Architectural violations are always advisory (warn) — heuristic detection, not a hard block
|
|
1182
|
+
const severity = ARCH_VIOLATION_CODES.has(item.code) ? 'warn' : defaultSeverity;
|
|
1183
|
+
const rule = ARCH_VIOLATION_CODES.has(item.code) ? item.code : `ai_debt:${item.code}`;
|
|
1184
|
+
const files = item.files && item.files.length > 0 ? item.files : null;
|
|
1185
|
+
if (files) {
|
|
1186
|
+
for (const file of files) {
|
|
1187
|
+
result.push({ rule, file, severity, message: item.message });
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
else {
|
|
1191
|
+
result.push({ rule, file: '.neurcode/ai-debt-budget.json', severity, message: item.message });
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
return result;
|
|
1097
1195
|
}
|
|
1098
1196
|
function parseSigningKeyRing(raw) {
|
|
1099
1197
|
if (!raw || !raw.trim()) {
|
|
@@ -1649,6 +1747,9 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
|
|
|
1649
1747
|
if (!options.json && effectiveRules.policyPack && effectiveRules.policyPackRules.length > 0) {
|
|
1650
1748
|
console.log(chalk.dim(` Evaluating policy pack: ${effectiveRules.policyPack.packName} (${effectiveRules.policyPack.packId}@${effectiveRules.policyPack.version}, ${effectiveRules.policyPackRules.length} rule(s))`));
|
|
1651
1749
|
}
|
|
1750
|
+
else if (!options.json && !effectiveRules.policyPack) {
|
|
1751
|
+
console.log(chalk.dim(' No policy pack installed — run `neurcode policy install <pack>` to add governance rules'));
|
|
1752
|
+
}
|
|
1652
1753
|
const policyResult = (0, policy_engine_1.evaluateRules)(diffFilesForPolicy, effectiveRules.allRules);
|
|
1653
1754
|
policyViolations = (policyResult.violations || []);
|
|
1654
1755
|
policyViolations = policyViolations.filter((v) => !ignoreFilter(v.file));
|
|
@@ -1889,6 +1990,14 @@ async function verifyCommand(options) {
|
|
|
1889
1990
|
emitCanonicalVerifyJson({
|
|
1890
1991
|
...payload,
|
|
1891
1992
|
expediteMode: expediteModeEnabled,
|
|
1993
|
+
// Intent engine results injected so every code-path gets them.
|
|
1994
|
+
intentIssues: payload.intentIssues ?? intentEngineIssues,
|
|
1995
|
+
intentDomains: payload.intentDomains ?? intentEngineDomains,
|
|
1996
|
+
intentSummary: payload.intentSummary ?? intentEngineSummary,
|
|
1997
|
+
// V5: flow issues injected alongside intent issues
|
|
1998
|
+
flowIssues: payload.flowIssues ?? intentEngineFlowIssues,
|
|
1999
|
+
// V6: regressions always injected
|
|
2000
|
+
regressions: payload.regressions ?? intentEngineRegressions,
|
|
1892
2001
|
});
|
|
1893
2002
|
};
|
|
1894
2003
|
if (!isGitRepository(projectRoot)) {
|
|
@@ -1930,10 +2039,10 @@ async function verifyCommand(options) {
|
|
|
1930
2039
|
const explicitStrictArtifactMode = options.strictArtifacts === true ||
|
|
1931
2040
|
isEnabledFlag(process.env.NEURCODE_VERIFY_STRICT_ARTIFACTS) ||
|
|
1932
2041
|
isEnabledFlag(process.env.NEURCODE_ENTERPRISE_MODE);
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
const strictArtifactMode = explicitStrictArtifactMode
|
|
2042
|
+
// Strict artifact mode is only engaged when explicitly requested.
|
|
2043
|
+
// Auto-enabling it in CI based on API key presence masked real violations
|
|
2044
|
+
// by blocking early on missing artifacts before policy checks could run.
|
|
2045
|
+
const strictArtifactMode = explicitStrictArtifactMode;
|
|
1937
2046
|
const runtimeGuardArtifactPath = (0, runtime_guard_1.resolveRuntimeGuardPath)(projectRoot, options.runtimeGuard);
|
|
1938
2047
|
const autoRuntimeGuardInStrict = strictArtifactMode
|
|
1939
2048
|
&& (0, fs_1.existsSync)(runtimeGuardArtifactPath)
|
|
@@ -1999,38 +2108,57 @@ async function verifyCommand(options) {
|
|
|
1999
2108
|
]
|
|
2000
2109
|
: [],
|
|
2001
2110
|
};
|
|
2111
|
+
// Artifact presence warnings (advisory — missing artifacts fall back to runtime compilation).
|
|
2112
|
+
// These must never cause an early exit; real governance signal should always be evaluated.
|
|
2113
|
+
const artifactPresenceWarnings = [];
|
|
2002
2114
|
if (strictArtifactMode) {
|
|
2003
|
-
const strictErrors = [];
|
|
2004
2115
|
if (!compiledPolicyRead.artifact) {
|
|
2005
|
-
|
|
2116
|
+
artifactPresenceWarnings.push(compiledPolicyRead.error
|
|
2006
2117
|
? `Compiled policy artifact invalid (${compiledPolicyRead.error})`
|
|
2007
2118
|
: `Compiled policy artifact missing (${compiledPolicyRead.path})`);
|
|
2008
2119
|
}
|
|
2009
2120
|
if (!changeContractRead.contract) {
|
|
2010
|
-
|
|
2121
|
+
artifactPresenceWarnings.push(changeContractRead.error
|
|
2011
2122
|
? `Change contract artifact invalid (${changeContractRead.error})`
|
|
2012
2123
|
: `Change contract artifact missing (${changeContractRead.path})`);
|
|
2013
2124
|
}
|
|
2014
|
-
if (
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2125
|
+
if (!options.json && artifactPresenceWarnings.length > 0) {
|
|
2126
|
+
console.log(chalk.yellow('\n⚠️ Deterministic artifact(s) unavailable — falling back to runtime compilation'));
|
|
2127
|
+
artifactPresenceWarnings.forEach((entry) => {
|
|
2128
|
+
console.log(chalk.yellow(` • ${entry}`));
|
|
2129
|
+
});
|
|
2130
|
+
console.log(chalk.dim(' Governance will continue using runtime compilation. Artifact checks are advisory.\n'));
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
// Signature blocking distinguishes two cases:
|
|
2134
|
+
// - Artifact has a signature that is INVALID (present=true, valid=false): this is a tamper
|
|
2135
|
+
// indicator and blocks when requireSignedArtifacts is set.
|
|
2136
|
+
// - Artifact has NO signature (present=false): this is an unsigned artifact; advisory only,
|
|
2137
|
+
// never blocks — an unsigned artifact cannot be "tampered", only "not signed yet".
|
|
2138
|
+
if (strictArtifactMode) {
|
|
2139
|
+
const signatureBlockErrors = [];
|
|
2140
|
+
if (requireSignedArtifacts
|
|
2141
|
+
&& compiledPolicySignatureStatus
|
|
2142
|
+
&& compiledPolicySignatureStatus.present
|
|
2143
|
+
&& !compiledPolicySignatureStatus.valid) {
|
|
2144
|
+
signatureBlockErrors.push(`Compiled policy artifact signature validation failed (${compiledPolicySignatureStatus.issues.join('; ') || 'unknown issue'})`);
|
|
2018
2145
|
}
|
|
2019
|
-
if (
|
|
2020
|
-
&&
|
|
2021
|
-
&&
|
|
2022
|
-
|
|
2146
|
+
if (requireSignedArtifacts
|
|
2147
|
+
&& changeContractSignatureStatus
|
|
2148
|
+
&& changeContractSignatureStatus.present
|
|
2149
|
+
&& !changeContractSignatureStatus.valid) {
|
|
2150
|
+
signatureBlockErrors.push(`Change contract artifact signature validation failed (${changeContractSignatureStatus.issues.join('; ') || 'unknown issue'})`);
|
|
2023
2151
|
}
|
|
2024
|
-
if (
|
|
2025
|
-
const message = `
|
|
2152
|
+
if (signatureBlockErrors.length > 0) {
|
|
2153
|
+
const message = `Signed artifact enforcement failed — tampered or invalid signatures detected.\n- ${signatureBlockErrors.join('\n- ')}`;
|
|
2026
2154
|
if (options.json) {
|
|
2027
2155
|
emitVerifyJson({
|
|
2028
2156
|
grade: 'F',
|
|
2029
2157
|
score: 0,
|
|
2030
2158
|
verdict: 'FAIL',
|
|
2031
|
-
violations:
|
|
2159
|
+
violations: signatureBlockErrors.map((entry) => ({
|
|
2032
2160
|
file: entry.toLowerCase().includes('compiled policy') ? compiledPolicyRead.path : changeContractRead.path,
|
|
2033
|
-
rule: '
|
|
2161
|
+
rule: 'signed_artifacts_required',
|
|
2034
2162
|
severity: 'block',
|
|
2035
2163
|
message: entry,
|
|
2036
2164
|
})),
|
|
@@ -2041,32 +2169,34 @@ async function verifyCommand(options) {
|
|
|
2041
2169
|
totalPlannedFiles: 0,
|
|
2042
2170
|
message,
|
|
2043
2171
|
scopeGuardPassed: false,
|
|
2044
|
-
mode: '
|
|
2172
|
+
mode: 'signed_artifacts_required',
|
|
2045
2173
|
policyOnly: options.policyOnly === true,
|
|
2046
2174
|
changeContract: changeContractSummary,
|
|
2047
2175
|
...(compiledPolicyMetadata ? { policyCompilation: compiledPolicyMetadata } : {}),
|
|
2048
2176
|
});
|
|
2049
2177
|
}
|
|
2050
2178
|
else {
|
|
2051
|
-
(0, scope_telemetry_1.printScopeTelemetry)(chalk, scopeTelemetry, {
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
console.log(chalk.red('\n⛔ Deterministic Artifact Requirements Failed'));
|
|
2055
|
-
strictErrors.forEach((entry) => {
|
|
2179
|
+
(0, scope_telemetry_1.printScopeTelemetry)(chalk, scopeTelemetry, { includeBlockedWarning: true });
|
|
2180
|
+
console.log(chalk.red('\n⛔ Signed Artifact Validation Failed'));
|
|
2181
|
+
signatureBlockErrors.forEach((entry) => {
|
|
2056
2182
|
console.log(chalk.red(` • ${entry}`));
|
|
2057
2183
|
});
|
|
2058
|
-
console.log(chalk.dim('\
|
|
2059
|
-
if (requireSignedArtifacts) {
|
|
2060
|
-
console.log(chalk.dim('Enable signing keys via NEURCODE_GOVERNANCE_SIGNING_KEY or NEURCODE_GOVERNANCE_SIGNING_KEYS to generate signed artifacts.\n'));
|
|
2061
|
-
}
|
|
2062
|
-
else {
|
|
2063
|
-
console.log('');
|
|
2064
|
-
}
|
|
2184
|
+
console.log(chalk.dim('\nRegenerate artifacts with valid signing keys: NEURCODE_GOVERNANCE_SIGNING_KEY or NEURCODE_GOVERNANCE_SIGNING_KEYS.\n'));
|
|
2065
2185
|
}
|
|
2066
2186
|
process.exit(2);
|
|
2067
2187
|
}
|
|
2188
|
+
// Advisory notice when artifact has a signature but signing is not required in this context.
|
|
2189
|
+
if (!options.json) {
|
|
2190
|
+
if (compiledPolicySignatureStatus && !compiledPolicySignatureStatus.valid && !requireSignedArtifacts) {
|
|
2191
|
+
console.log(chalk.yellow(` ⚠️ Compiled policy signature could not be verified (${compiledPolicySignatureStatus.issues.join('; ') || 'key unavailable'}) — advisory only`));
|
|
2192
|
+
}
|
|
2193
|
+
if (changeContractSignatureStatus && !changeContractSignatureStatus.valid && !requireSignedArtifacts) {
|
|
2194
|
+
console.log(chalk.yellow(` ⚠️ Change contract signature could not be verified (${changeContractSignatureStatus.issues.join('; ') || 'key unavailable'}) — advisory only`));
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2068
2197
|
}
|
|
2069
2198
|
if (!strictArtifactMode && requireSignedArtifacts) {
|
|
2199
|
+
// Non-strict mode with signing required: same signature gate applies.
|
|
2070
2200
|
const signatureErrors = [];
|
|
2071
2201
|
if (compiledPolicyRead.artifact && compiledPolicySignatureStatus && !compiledPolicySignatureStatus.valid) {
|
|
2072
2202
|
signatureErrors.push(`Compiled policy artifact signature validation failed (${compiledPolicySignatureStatus.issues.join('; ') || 'unknown issue'})`);
|
|
@@ -2101,13 +2231,9 @@ async function verifyCommand(options) {
|
|
|
2101
2231
|
});
|
|
2102
2232
|
}
|
|
2103
2233
|
else {
|
|
2104
|
-
(0, scope_telemetry_1.printScopeTelemetry)(chalk, scopeTelemetry, {
|
|
2105
|
-
includeBlockedWarning: true,
|
|
2106
|
-
});
|
|
2234
|
+
(0, scope_telemetry_1.printScopeTelemetry)(chalk, scopeTelemetry, { includeBlockedWarning: true });
|
|
2107
2235
|
console.log(chalk.red('\n⛔ Signed Artifact Requirements Failed'));
|
|
2108
|
-
signatureErrors.forEach((entry) => {
|
|
2109
|
-
console.log(chalk.red(` • ${entry}`));
|
|
2110
|
-
});
|
|
2236
|
+
signatureErrors.forEach((entry) => console.log(chalk.red(` • ${entry}`)));
|
|
2111
2237
|
console.log(chalk.dim('\nEnable signing keys via NEURCODE_GOVERNANCE_SIGNING_KEY or NEURCODE_GOVERNANCE_SIGNING_KEYS and regenerate artifacts.\n'));
|
|
2112
2238
|
}
|
|
2113
2239
|
process.exit(2);
|
|
@@ -2145,9 +2271,6 @@ async function verifyCommand(options) {
|
|
|
2145
2271
|
console.log(chalk.yellow(` Change contract signature: invalid (${changeContractSignatureStatus.issues.join('; ') || 'unknown issue'})`));
|
|
2146
2272
|
}
|
|
2147
2273
|
}
|
|
2148
|
-
if (ciEnterpriseDefaultStrict && !explicitStrictArtifactMode) {
|
|
2149
|
-
console.log(chalk.dim(' CI enterprise mode detected: strict deterministic artifact enforcement is auto-enabled (set NEURCODE_VERIFY_ALLOW_NON_STRICT_CI=1 to opt out).'));
|
|
2150
|
-
}
|
|
2151
2274
|
if (autoRuntimeGuardInStrict && !options.requireRuntimeGuard && !isEnabledFlag(process.env.NEURCODE_VERIFY_REQUIRE_RUNTIME_GUARD)) {
|
|
2152
2275
|
console.log(chalk.dim(` Strict mode detected runtime guard artifact: auto-enforcing runtime guard (${runtimeGuardArtifactPath}).`));
|
|
2153
2276
|
}
|
|
@@ -2912,6 +3035,11 @@ async function verifyCommand(options) {
|
|
|
2912
3035
|
let governanceResult = null;
|
|
2913
3036
|
let planFilesForVerification = [];
|
|
2914
3037
|
let intentConstraintsForVerification;
|
|
3038
|
+
let intentEngineIssues = [];
|
|
3039
|
+
let intentEngineDomains = [];
|
|
3040
|
+
let intentEngineSummary = null;
|
|
3041
|
+
let intentEngineFlowIssues = [];
|
|
3042
|
+
let intentEngineRegressions = [];
|
|
2915
3043
|
try {
|
|
2916
3044
|
// Step A: Get Modified Files (already have from diffFiles)
|
|
2917
3045
|
const modifiedFiles = diffFiles.map(f => f.path);
|
|
@@ -2955,6 +3083,23 @@ async function verifyCommand(options) {
|
|
|
2955
3083
|
}
|
|
2956
3084
|
planFilesForVerification = [...new Set([...planFiles, ...localPlanExpectedFiles])];
|
|
2957
3085
|
intentConstraintsForVerification = originalIntent || undefined;
|
|
3086
|
+
// ── Intent-Aware Engine ─────────────────────────────────────────────
|
|
3087
|
+
// Run once we have both diffFiles and the resolved intent text.
|
|
3088
|
+
// Stored in outer scope so all emitCanonicalVerifyJson call sites can
|
|
3089
|
+
// include the result in their payloads without repeating the computation.
|
|
3090
|
+
if (intentConstraintsForVerification && diffFiles.length > 0) {
|
|
3091
|
+
try {
|
|
3092
|
+
const engineResult = (0, intent_engine_1.runIntentEngine)(intentConstraintsForVerification, diffFiles, projectRoot);
|
|
3093
|
+
intentEngineIssues = engineResult.intentIssues;
|
|
3094
|
+
intentEngineDomains = engineResult.checkedDomains;
|
|
3095
|
+
intentEngineSummary = engineResult.intentSummary;
|
|
3096
|
+
intentEngineFlowIssues = engineResult.flowIssues;
|
|
3097
|
+
intentEngineRegressions = engineResult.regressions;
|
|
3098
|
+
}
|
|
3099
|
+
catch {
|
|
3100
|
+
// Non-fatal: intent engine errors must never break verification
|
|
3101
|
+
}
|
|
3102
|
+
}
|
|
2958
3103
|
governanceResult = (0, governance_1.evaluateGovernance)({
|
|
2959
3104
|
projectRoot,
|
|
2960
3105
|
task: governanceTask,
|
|
@@ -3058,6 +3203,9 @@ async function verifyCommand(options) {
|
|
|
3058
3203
|
mode: 'plan_enforced',
|
|
3059
3204
|
policyOnly: false,
|
|
3060
3205
|
aiDebt: aiDebtSummaryForScope,
|
|
3206
|
+
intentIssues: intentEngineIssues,
|
|
3207
|
+
intentDomains: intentEngineDomains,
|
|
3208
|
+
intentSummary: intentEngineSummary,
|
|
3061
3209
|
...(expediteModeEnabled ? { expediteMode: true } : {}),
|
|
3062
3210
|
...(governanceResult
|
|
3063
3211
|
? buildGovernancePayload(governanceResult, orgGovernanceSettings, {
|
|
@@ -3133,6 +3281,42 @@ async function verifyCommand(options) {
|
|
|
3133
3281
|
if (governanceResult) {
|
|
3134
3282
|
displayGovernanceInsights(governanceResult, { explain: options.explain });
|
|
3135
3283
|
}
|
|
3284
|
+
// ── Intent Status in scope-violation path ──────────────────────
|
|
3285
|
+
if (intentEngineSummary) {
|
|
3286
|
+
const s = intentEngineSummary;
|
|
3287
|
+
const domainLabel = s.domain.charAt(0).toUpperCase() + s.domain.slice(1);
|
|
3288
|
+
const confColor = s.confidence === 'HIGH' ? chalk.green : s.confidence === 'MEDIUM' ? chalk.yellow : chalk.red;
|
|
3289
|
+
const wCovPct = s.weightedCoverage != null ? Math.round(s.weightedCoverage * 100) : s.coveragePct;
|
|
3290
|
+
const filled = Math.round((wCovPct / 100) * 20);
|
|
3291
|
+
const bar = chalk.cyan('█'.repeat(filled)) + chalk.dim('░'.repeat(20 - filled));
|
|
3292
|
+
const sysStatus = s.status;
|
|
3293
|
+
const statusLabel = sysStatus === 'CRITICAL' ? chalk.bold.red('[CRITICAL]') : sysStatus === 'AT RISK' ? chalk.bold.yellow('[AT RISK]') : chalk.bold.green('[SECURE]');
|
|
3294
|
+
console.log(chalk.bold('\n━━━ INTENT STATUS ━━━━━━━━━━━━━━━━━━━━━━'));
|
|
3295
|
+
console.log(` ${statusLabel} ${chalk.bold(`${domainLabel} Implementation:`)} ${bar} ${chalk.bold(`${wCovPct}%`)} (weighted)`);
|
|
3296
|
+
console.log(` Confidence: ${confColor(s.confidence)}`);
|
|
3297
|
+
const critMissing = s.criticalMissing ?? [];
|
|
3298
|
+
const otherMissing = s.missing.filter((k) => !critMissing.includes(k));
|
|
3299
|
+
if (critMissing.length > 0) {
|
|
3300
|
+
console.log(` ${chalk.bold.red('Critical missing:')}`);
|
|
3301
|
+
critMissing.forEach((k) => { const label = k.split('-').map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(' '); console.log(chalk.red(` ✗ ${label}`)); });
|
|
3302
|
+
}
|
|
3303
|
+
if (otherMissing.length > 0) {
|
|
3304
|
+
console.log(` ${chalk.bold.yellow('Missing:')}`);
|
|
3305
|
+
otherMissing.forEach((k) => { const label = k.split('-').map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(' '); console.log(chalk.yellow(` • ${label}`)); });
|
|
3306
|
+
}
|
|
3307
|
+
if (critMissing.length === 0 && otherMissing.length === 0) {
|
|
3308
|
+
console.log(` Missing: ${chalk.green('none — all components detected')}`);
|
|
3309
|
+
}
|
|
3310
|
+
console.log(chalk.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
3311
|
+
}
|
|
3312
|
+
if (intentEngineRegressions.length > 0) {
|
|
3313
|
+
console.log(chalk.bold.red('\n━━━ REGRESSION ANALYSIS ━━━━━━━━━━━━━━━━━'));
|
|
3314
|
+
intentEngineRegressions.forEach((reg) => {
|
|
3315
|
+
const icon = reg.type === 'coverage-regression' ? '📉' : reg.type === 'critical-regression' ? '🔴' : reg.type === 'flow-regression' ? '⛓' : '⚠';
|
|
3316
|
+
console.log(` ${chalk.red('[REGRESSION]')} ${icon} ${reg.message}`);
|
|
3317
|
+
});
|
|
3318
|
+
console.log(chalk.bold.red('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
3319
|
+
}
|
|
3136
3320
|
console.log('');
|
|
3137
3321
|
await recordVerificationIfRequested(options, config, {
|
|
3138
3322
|
grade: 'F',
|
|
@@ -3523,6 +3707,9 @@ async function verifyCommand(options) {
|
|
|
3523
3707
|
if (!options.json && effectiveRules.policyPack && effectiveRules.policyPackRules.length > 0) {
|
|
3524
3708
|
console.log(chalk.dim(` Evaluating policy pack: ${effectiveRules.policyPack.packName} (${effectiveRules.policyPack.packId}@${effectiveRules.policyPack.version}, ${effectiveRules.policyPackRules.length} rule(s))`));
|
|
3525
3709
|
}
|
|
3710
|
+
else if (!options.json && !effectiveRules.policyPack) {
|
|
3711
|
+
console.log(chalk.dim(' No policy pack installed — run `neurcode policy install <pack>` to add governance rules'));
|
|
3712
|
+
}
|
|
3526
3713
|
// Prepare diff stats and changed files for API
|
|
3527
3714
|
const diffStats = {
|
|
3528
3715
|
totalAdded: summary.totalAdded,
|
|
@@ -3984,7 +4171,7 @@ async function verifyCommand(options) {
|
|
|
3984
4171
|
message: effectiveMessage,
|
|
3985
4172
|
bloatFiles: displayBloatFiles,
|
|
3986
4173
|
bloatCount: displayBloatFiles.length,
|
|
3987
|
-
}, policyViolations, expediteModeEnabled);
|
|
4174
|
+
}, policyViolations, expediteModeEnabled, intentEngineIssues, intentEngineSummary, intentEngineFlowIssues, intentEngineRegressions);
|
|
3988
4175
|
if (governanceResult) {
|
|
3989
4176
|
displayGovernanceInsights(governanceResult, { explain: options.explain });
|
|
3990
4177
|
}
|
|
@@ -4514,15 +4701,69 @@ function displayChangeContractDrift(summary, options = { advisory: false }) {
|
|
|
4514
4701
|
/**
|
|
4515
4702
|
* Display verification results in a formatted report card
|
|
4516
4703
|
*/
|
|
4517
|
-
function displayVerifyResults(result, policyViolations, expediteModeUsed = false) {
|
|
4518
|
-
|
|
4519
|
-
|
|
4704
|
+
function displayVerifyResults(result, policyViolations, expediteModeUsed = false, intentIssuesForDisplay = [], intentSummaryForDisplay = null, flowIssuesForDisplay = [], regressionsForDisplay = []) {
|
|
4705
|
+
// ── Header ────────────────────────────────────────────────────────────────
|
|
4706
|
+
const headerLabel = result.verdict === 'PASS'
|
|
4707
|
+
? chalk.bold.green('\n✅ VERIFICATION PASSED')
|
|
4520
4708
|
: result.verdict === 'WARN'
|
|
4521
|
-
? chalk.yellow('
|
|
4522
|
-
: chalk.red('
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4709
|
+
? chalk.bold.yellow('\n⚠️ VERIFICATION PASSED WITH WARNINGS')
|
|
4710
|
+
: chalk.bold.red('\n❌ VERIFICATION FAILED');
|
|
4711
|
+
console.log(headerLabel);
|
|
4712
|
+
// ── Intent Status block ──────────────────────────────────────────────────
|
|
4713
|
+
if (intentSummaryForDisplay) {
|
|
4714
|
+
const s = intentSummaryForDisplay;
|
|
4715
|
+
const domainLabel = s.domain.charAt(0).toUpperCase() + s.domain.slice(1);
|
|
4716
|
+
const confColor = s.confidence === 'HIGH'
|
|
4717
|
+
? chalk.green
|
|
4718
|
+
: s.confidence === 'MEDIUM'
|
|
4719
|
+
? chalk.yellow
|
|
4720
|
+
: chalk.red;
|
|
4721
|
+
// V4: weighted coverage bar
|
|
4722
|
+
const wCovPct = s.weightedCoverage != null
|
|
4723
|
+
? Math.round(s.weightedCoverage * 100)
|
|
4724
|
+
: s.coveragePct;
|
|
4725
|
+
const barWidth = 20;
|
|
4726
|
+
const filled = Math.round((wCovPct / 100) * barWidth);
|
|
4727
|
+
const bar = chalk.cyan('█'.repeat(filled)) + chalk.dim('░'.repeat(barWidth - filled));
|
|
4728
|
+
// V4: system status label
|
|
4729
|
+
const sysStatus = s.status;
|
|
4730
|
+
const statusLabel = sysStatus === 'CRITICAL'
|
|
4731
|
+
? chalk.bold.red('[CRITICAL]')
|
|
4732
|
+
: sysStatus === 'AT RISK'
|
|
4733
|
+
? chalk.bold.yellow('[AT RISK]')
|
|
4734
|
+
: chalk.bold.green('[SECURE]');
|
|
4735
|
+
console.log(chalk.bold('\n━━━ INTENT STATUS ━━━━━━━━━━━━━━━━━━━━━━'));
|
|
4736
|
+
console.log(` ${statusLabel} ${chalk.bold(`${domainLabel} Implementation:`)} ${bar} ${chalk.bold(`${wCovPct}%`)} (weighted)`);
|
|
4737
|
+
console.log(` Confidence: ${confColor(s.confidence)}`);
|
|
4738
|
+
if (s.foundList.length > 0) {
|
|
4739
|
+
const foundLabels = s.foundList
|
|
4740
|
+
.map((k) => k.split('-').map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(' '))
|
|
4741
|
+
.slice(0, 4);
|
|
4742
|
+
console.log(` Found: ${chalk.green(foundLabels.join(', '))}${s.foundList.length > 4 ? chalk.dim(` +${s.foundList.length - 4} more`) : ''}`);
|
|
4743
|
+
}
|
|
4744
|
+
// V4: show critical missing and non-critical missing separately
|
|
4745
|
+
const critMissing = s.criticalMissing ?? [];
|
|
4746
|
+
const otherMissing = s.missing.filter((k) => !critMissing.includes(k));
|
|
4747
|
+
if (critMissing.length > 0) {
|
|
4748
|
+
console.log(` ${chalk.bold.red('Critical missing:')}`);
|
|
4749
|
+
critMissing.forEach((k) => {
|
|
4750
|
+
const label = k.split('-').map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
|
|
4751
|
+
console.log(chalk.red(` ✗ ${label}`));
|
|
4752
|
+
});
|
|
4753
|
+
}
|
|
4754
|
+
if (otherMissing.length > 0) {
|
|
4755
|
+
console.log(` ${chalk.bold.yellow('Missing:')}`);
|
|
4756
|
+
otherMissing.forEach((k) => {
|
|
4757
|
+
const label = k.split('-').map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
|
|
4758
|
+
console.log(chalk.yellow(` • ${label}`));
|
|
4759
|
+
});
|
|
4760
|
+
}
|
|
4761
|
+
if (critMissing.length === 0 && otherMissing.length === 0) {
|
|
4762
|
+
console.log(` Missing: ${chalk.green('none — all components detected')}`);
|
|
4763
|
+
}
|
|
4764
|
+
console.log(chalk.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
4765
|
+
}
|
|
4766
|
+
// ── Triage items ──────────────────────────────────────────────────────────
|
|
4526
4767
|
const maxBlockingItems = 20;
|
|
4527
4768
|
const maxAdvisoryItems = 8;
|
|
4528
4769
|
const maxExpediteItems = 12;
|
|
@@ -4580,6 +4821,31 @@ function displayVerifyResults(result, policyViolations, expediteModeUsed = false
|
|
|
4580
4821
|
];
|
|
4581
4822
|
advisoryItems = [];
|
|
4582
4823
|
}
|
|
4824
|
+
// ── Counts ────────────────────────────────────────────────────────────────
|
|
4825
|
+
console.log(blockingItems.length > 0
|
|
4826
|
+
? chalk.red(`Blocking Issues: ${blockingItems.length}`)
|
|
4827
|
+
: chalk.dim('Blocking Issues: 0'));
|
|
4828
|
+
if (expediteModeUsed) {
|
|
4829
|
+
console.log(chalk.yellow(`Expedite Issues: ${expediteItems.length}`));
|
|
4830
|
+
}
|
|
4831
|
+
else {
|
|
4832
|
+
console.log(advisoryItems.length > 0
|
|
4833
|
+
? chalk.yellow(`Advisory Issues: ${advisoryItems.length}`)
|
|
4834
|
+
: chalk.dim('Advisory Issues: 0'));
|
|
4835
|
+
}
|
|
4836
|
+
console.log(chalk.dim(`Plan adherence: ${result.plannedFilesModified}/${result.totalPlannedFiles} files (${result.adherenceScore}%)`));
|
|
4837
|
+
// ── Top issues ────────────────────────────────────────────────────────────
|
|
4838
|
+
const topIssues = [
|
|
4839
|
+
...blockingItems,
|
|
4840
|
+
...(expediteModeUsed ? expediteItems : advisoryItems),
|
|
4841
|
+
].slice(0, 2);
|
|
4842
|
+
if (topIssues.length > 0) {
|
|
4843
|
+
console.log(chalk.bold('\nTop Issues:'));
|
|
4844
|
+
topIssues.forEach((item, i) => {
|
|
4845
|
+
console.log(` ${i + 1}. ${item.message} → ${chalk.cyan(item.file)}`);
|
|
4846
|
+
});
|
|
4847
|
+
}
|
|
4848
|
+
// ── Detailed lists ────────────────────────────────────────────────────────
|
|
4583
4849
|
if (blockingItems.length > 0) {
|
|
4584
4850
|
console.log(chalk.red(`\nBLOCKING (${blockingItems.length})`));
|
|
4585
4851
|
blockingItems.slice(0, maxBlockingItems).forEach((item) => {
|
|
@@ -4612,21 +4878,56 @@ function displayVerifyResults(result, policyViolations, expediteModeUsed = false
|
|
|
4612
4878
|
});
|
|
4613
4879
|
console.log(chalk.dim(' Note: Expedite Mode used'));
|
|
4614
4880
|
}
|
|
4615
|
-
|
|
4616
|
-
|
|
4881
|
+
// ── Intent issues ─────────────────────────────────────────────────────────
|
|
4882
|
+
if (intentIssuesForDisplay.length > 0) {
|
|
4883
|
+
console.log(chalk.magenta(`\nINTENT ISSUES (${intentIssuesForDisplay.length})`));
|
|
4884
|
+
intentIssuesForDisplay.forEach((issue) => {
|
|
4885
|
+
const label = issue.severity === 'high' ? chalk.red('[HIGH]') : chalk.yellow('[MEDIUM]');
|
|
4886
|
+
const typeLabel = issue.type === 'missing' ? 'Missing' : issue.type === 'misplaced' ? 'Misplaced' : 'Partial';
|
|
4887
|
+
console.log(` ${label} ${typeLabel}: ${issue.message}`);
|
|
4888
|
+
});
|
|
4617
4889
|
}
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4890
|
+
// ── Flow Validation ───────────────────────────────────────────────────────
|
|
4891
|
+
if (flowIssuesForDisplay.length > 0) {
|
|
4892
|
+
console.log(chalk.bold('\n━━━ FLOW VALIDATION ━━━━━━━━━━━━━━━━━━━━━'));
|
|
4893
|
+
flowIssuesForDisplay.forEach((issue) => {
|
|
4894
|
+
const label = issue.severity === 'high' ? chalk.red('[HIGH]') : chalk.yellow('[MEDIUM]');
|
|
4895
|
+
const typeIcon = issue.type === 'missing-flow' ? '⛓' : issue.type === 'misplaced-flow' ? '⚠' : '⊘';
|
|
4896
|
+
console.log(` ${label} ${typeIcon} ${issue.message}`);
|
|
4897
|
+
if (issue.files && issue.files.length > 0) {
|
|
4898
|
+
const display = issue.files.slice(0, 3);
|
|
4899
|
+
console.log(chalk.dim(` → ${display.join(', ')}${issue.files.length > 3 ? ` +${issue.files.length - 3} more` : ''}`));
|
|
4900
|
+
}
|
|
4901
|
+
});
|
|
4902
|
+
console.log(chalk.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
4625
4903
|
}
|
|
4626
|
-
|
|
4627
|
-
|
|
4904
|
+
// ── Regression Analysis ───────────────────────────────────────────────────
|
|
4905
|
+
if (regressionsForDisplay.length > 0) {
|
|
4906
|
+
console.log(chalk.bold.red('\n━━━ REGRESSION ANALYSIS ━━━━━━━━━━━━━━━━━'));
|
|
4907
|
+
regressionsForDisplay.forEach((reg) => {
|
|
4908
|
+
const icon = reg.type === 'coverage-regression' ? '📉' :
|
|
4909
|
+
reg.type === 'critical-regression' ? '🔴' :
|
|
4910
|
+
reg.type === 'flow-regression' ? '⛓' : '⚠';
|
|
4911
|
+
console.log(` ${chalk.red('[REGRESSION]')} ${icon} ${reg.message}`);
|
|
4912
|
+
});
|
|
4913
|
+
console.log(chalk.bold.red('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
4914
|
+
}
|
|
4915
|
+
const hasAnyIssue = blockingItems.length > 0 ||
|
|
4916
|
+
advisoryItems.length > 0 ||
|
|
4917
|
+
expediteItems.length > 0 ||
|
|
4918
|
+
intentIssuesForDisplay.length > 0 ||
|
|
4919
|
+
flowIssuesForDisplay.length > 0 ||
|
|
4920
|
+
regressionsForDisplay.length > 0;
|
|
4921
|
+
if (!hasAnyIssue) {
|
|
4922
|
+
console.log(chalk.green('\nNo issues detected.'));
|
|
4923
|
+
}
|
|
4924
|
+
// ── Next step ─────────────────────────────────────────────────────────────
|
|
4925
|
+
if (hasAnyIssue) {
|
|
4926
|
+
console.log(chalk.bold('\nNext step:'));
|
|
4927
|
+
console.log(` ${chalk.cyan('neurcode fix')}`);
|
|
4928
|
+
console.log(chalk.dim(' or: neurcode fix --apply-safe (auto-apply high-confidence patches)'));
|
|
4628
4929
|
}
|
|
4629
|
-
console.log(chalk.dim(
|
|
4930
|
+
console.log(chalk.dim(`\nDetails: ${result.message}\n`));
|
|
4630
4931
|
}
|
|
4631
4932
|
function printFirstRunAdvisoryMessage(demoMode) {
|
|
4632
4933
|
console.log(chalk.cyan('\nNeurcode first-run advisory mode'));
|