@neurcode-ai/cli 0.9.44 → 0.9.45

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 (177) hide show
  1. package/dist/commands/contract.js +47 -0
  2. package/dist/commands/plan.js +40 -0
  3. package/dist/commands/verify.d.ts +2 -0
  4. package/dist/commands/verify.js +240 -114
  5. package/dist/index.js +41 -5
  6. package/dist/utils/advisory-signals.d.ts +20 -0
  7. package/dist/utils/advisory-signals.js +177 -0
  8. package/dist/utils/change-contract.d.ts +105 -1
  9. package/dist/utils/change-contract.js +685 -12
  10. package/dist/utils/diff-symbols.d.ts +10 -0
  11. package/dist/utils/diff-symbols.js +218 -0
  12. package/dist/utils/plan-symbols.d.ts +17 -0
  13. package/dist/utils/plan-symbols.js +209 -0
  14. package/package.json +6 -14
  15. package/LICENSE +0 -201
  16. package/dist/api-client.d.ts.map +0 -1
  17. package/dist/api-client.js.map +0 -1
  18. package/dist/commands/allow.d.ts.map +0 -1
  19. package/dist/commands/allow.js.map +0 -1
  20. package/dist/commands/apply.d.ts.map +0 -1
  21. package/dist/commands/apply.js.map +0 -1
  22. package/dist/commands/approve.d.ts.map +0 -1
  23. package/dist/commands/approve.js.map +0 -1
  24. package/dist/commands/ask.d.ts.map +0 -1
  25. package/dist/commands/ask.js.map +0 -1
  26. package/dist/commands/audit.d.ts.map +0 -1
  27. package/dist/commands/audit.js.map +0 -1
  28. package/dist/commands/bootstrap.d.ts.map +0 -1
  29. package/dist/commands/bootstrap.js.map +0 -1
  30. package/dist/commands/brain.d.ts.map +0 -1
  31. package/dist/commands/brain.js.map +0 -1
  32. package/dist/commands/check.d.ts.map +0 -1
  33. package/dist/commands/check.js.map +0 -1
  34. package/dist/commands/config.d.ts.map +0 -1
  35. package/dist/commands/config.js.map +0 -1
  36. package/dist/commands/contract.d.ts.map +0 -1
  37. package/dist/commands/contract.js.map +0 -1
  38. package/dist/commands/doctor.d.ts.map +0 -1
  39. package/dist/commands/doctor.js.map +0 -1
  40. package/dist/commands/feedback.d.ts.map +0 -1
  41. package/dist/commands/feedback.js.map +0 -1
  42. package/dist/commands/guard.d.ts.map +0 -1
  43. package/dist/commands/guard.js.map +0 -1
  44. package/dist/commands/init.d.ts.map +0 -1
  45. package/dist/commands/init.js.map +0 -1
  46. package/dist/commands/login.d.ts.map +0 -1
  47. package/dist/commands/login.js.map +0 -1
  48. package/dist/commands/logout.d.ts.map +0 -1
  49. package/dist/commands/logout.js.map +0 -1
  50. package/dist/commands/map.d.ts.map +0 -1
  51. package/dist/commands/map.js.map +0 -1
  52. package/dist/commands/plan-slo.d.ts.map +0 -1
  53. package/dist/commands/plan-slo.js.map +0 -1
  54. package/dist/commands/plan.d.ts.map +0 -1
  55. package/dist/commands/plan.js.map +0 -1
  56. package/dist/commands/policy.d.ts.map +0 -1
  57. package/dist/commands/policy.js.map +0 -1
  58. package/dist/commands/prompt.d.ts.map +0 -1
  59. package/dist/commands/prompt.js.map +0 -1
  60. package/dist/commands/refactor.d.ts.map +0 -1
  61. package/dist/commands/refactor.js.map +0 -1
  62. package/dist/commands/remediate.d.ts.map +0 -1
  63. package/dist/commands/remediate.js.map +0 -1
  64. package/dist/commands/repo.d.ts.map +0 -1
  65. package/dist/commands/repo.js.map +0 -1
  66. package/dist/commands/revert.d.ts.map +0 -1
  67. package/dist/commands/revert.js.map +0 -1
  68. package/dist/commands/security.d.ts.map +0 -1
  69. package/dist/commands/security.js.map +0 -1
  70. package/dist/commands/session.d.ts.map +0 -1
  71. package/dist/commands/session.js.map +0 -1
  72. package/dist/commands/ship.d.ts.map +0 -1
  73. package/dist/commands/ship.js.map +0 -1
  74. package/dist/commands/simulate.d.ts.map +0 -1
  75. package/dist/commands/simulate.js.map +0 -1
  76. package/dist/commands/verify.d.ts.map +0 -1
  77. package/dist/commands/verify.js.map +0 -1
  78. package/dist/commands/watch.d.ts.map +0 -1
  79. package/dist/commands/watch.js.map +0 -1
  80. package/dist/commands/whoami.d.ts.map +0 -1
  81. package/dist/commands/whoami.js.map +0 -1
  82. package/dist/config.d.ts.map +0 -1
  83. package/dist/config.js.map +0 -1
  84. package/dist/index.d.ts.map +0 -1
  85. package/dist/index.js.map +0 -1
  86. package/dist/rules.d.ts.map +0 -1
  87. package/dist/rules.js.map +0 -1
  88. package/dist/services/integrations/TicketService.d.ts.map +0 -1
  89. package/dist/services/integrations/TicketService.js.map +0 -1
  90. package/dist/services/mapper/ProjectScanner.d.ts.map +0 -1
  91. package/dist/services/mapper/ProjectScanner.js.map +0 -1
  92. package/dist/services/project-knowledge-service.d.ts.map +0 -1
  93. package/dist/services/project-knowledge-service.js.map +0 -1
  94. package/dist/services/security/SecurityGuard.d.ts.map +0 -1
  95. package/dist/services/security/SecurityGuard.js.map +0 -1
  96. package/dist/services/toolbox-service.d.ts.map +0 -1
  97. package/dist/services/toolbox-service.js.map +0 -1
  98. package/dist/services/watch/BlobStore.d.ts.map +0 -1
  99. package/dist/services/watch/BlobStore.js.map +0 -1
  100. package/dist/services/watch/CommandPoller.d.ts.map +0 -1
  101. package/dist/services/watch/CommandPoller.js.map +0 -1
  102. package/dist/services/watch/Journal.d.ts.map +0 -1
  103. package/dist/services/watch/Journal.js.map +0 -1
  104. package/dist/services/watch/Sentinel.d.ts.map +0 -1
  105. package/dist/services/watch/Sentinel.js.map +0 -1
  106. package/dist/services/watch/Syncer.d.ts.map +0 -1
  107. package/dist/services/watch/Syncer.js.map +0 -1
  108. package/dist/utils/ROILogger.d.ts.map +0 -1
  109. package/dist/utils/ROILogger.js.map +0 -1
  110. package/dist/utils/RelevanceScorer.d.ts.map +0 -1
  111. package/dist/utils/RelevanceScorer.js.map +0 -1
  112. package/dist/utils/ai-debt-budget.d.ts.map +0 -1
  113. package/dist/utils/ai-debt-budget.js.map +0 -1
  114. package/dist/utils/artifact-signature.d.ts.map +0 -1
  115. package/dist/utils/artifact-signature.js.map +0 -1
  116. package/dist/utils/ask-cache.d.ts.map +0 -1
  117. package/dist/utils/ask-cache.js.map +0 -1
  118. package/dist/utils/box.d.ts.map +0 -1
  119. package/dist/utils/box.js.map +0 -1
  120. package/dist/utils/brain-context.d.ts.map +0 -1
  121. package/dist/utils/brain-context.js.map +0 -1
  122. package/dist/utils/breakage-simulator.d.ts.map +0 -1
  123. package/dist/utils/breakage-simulator.js.map +0 -1
  124. package/dist/utils/change-contract.d.ts.map +0 -1
  125. package/dist/utils/change-contract.js.map +0 -1
  126. package/dist/utils/cli-json.d.ts.map +0 -1
  127. package/dist/utils/cli-json.js.map +0 -1
  128. package/dist/utils/custom-policy-rules.d.ts.map +0 -1
  129. package/dist/utils/custom-policy-rules.js.map +0 -1
  130. package/dist/utils/git.d.ts.map +0 -1
  131. package/dist/utils/git.js.map +0 -1
  132. package/dist/utils/gitignore.d.ts.map +0 -1
  133. package/dist/utils/gitignore.js.map +0 -1
  134. package/dist/utils/governance.d.ts.map +0 -1
  135. package/dist/utils/governance.js.map +0 -1
  136. package/dist/utils/ignore.d.ts.map +0 -1
  137. package/dist/utils/ignore.js.map +0 -1
  138. package/dist/utils/manual-approvals.d.ts.map +0 -1
  139. package/dist/utils/manual-approvals.js.map +0 -1
  140. package/dist/utils/messages.d.ts.map +0 -1
  141. package/dist/utils/messages.js.map +0 -1
  142. package/dist/utils/neurcode-context.d.ts.map +0 -1
  143. package/dist/utils/neurcode-context.js.map +0 -1
  144. package/dist/utils/plan-cache.d.ts.map +0 -1
  145. package/dist/utils/plan-cache.js.map +0 -1
  146. package/dist/utils/plan-slo.d.ts.map +0 -1
  147. package/dist/utils/plan-slo.js.map +0 -1
  148. package/dist/utils/policy-audit.d.ts.map +0 -1
  149. package/dist/utils/policy-audit.js.map +0 -1
  150. package/dist/utils/policy-compiler.d.ts.map +0 -1
  151. package/dist/utils/policy-compiler.js.map +0 -1
  152. package/dist/utils/policy-exceptions.d.ts.map +0 -1
  153. package/dist/utils/policy-exceptions.js.map +0 -1
  154. package/dist/utils/policy-governance.d.ts.map +0 -1
  155. package/dist/utils/policy-governance.js.map +0 -1
  156. package/dist/utils/policy-packs.d.ts.map +0 -1
  157. package/dist/utils/policy-packs.js.map +0 -1
  158. package/dist/utils/project-detector.d.ts.map +0 -1
  159. package/dist/utils/project-detector.js.map +0 -1
  160. package/dist/utils/project-root.d.ts.map +0 -1
  161. package/dist/utils/project-root.js.map +0 -1
  162. package/dist/utils/repo-links.d.ts.map +0 -1
  163. package/dist/utils/repo-links.js.map +0 -1
  164. package/dist/utils/restore.d.ts.map +0 -1
  165. package/dist/utils/restore.js.map +0 -1
  166. package/dist/utils/runtime-guard.d.ts.map +0 -1
  167. package/dist/utils/runtime-guard.js.map +0 -1
  168. package/dist/utils/scope-telemetry.d.ts.map +0 -1
  169. package/dist/utils/scope-telemetry.js.map +0 -1
  170. package/dist/utils/secret-masking.d.ts.map +0 -1
  171. package/dist/utils/secret-masking.js.map +0 -1
  172. package/dist/utils/state.d.ts.map +0 -1
  173. package/dist/utils/state.js.map +0 -1
  174. package/dist/utils/tier.d.ts.map +0 -1
  175. package/dist/utils/tier.js.map +0 -1
  176. package/dist/utils/user-context.d.ts.map +0 -1
  177. package/dist/utils/user-context.js.map +0 -1
package/dist/index.js CHANGED
@@ -62,12 +62,15 @@ catch (error) {
62
62
  const program = new commander_1.Command();
63
63
  const CORE_WORKFLOW_STEPS = [
64
64
  '1) neurcode init',
65
- '2) neurcode plan "Describe the change"',
66
- '3) neurcode prompt',
67
- '4) neurcode verify --record',
68
- '5) neurcode ship "Goal" --max-fix-attempts 2',
65
+ '2) neurcode login',
66
+ '3) neurcode verify --plan-id <id> --enforce-change-contract',
67
+ '4) neurcode fix --plan-id <id> (optional)',
69
68
  ];
70
69
  const ADVANCED_WORKFLOW_HINTS = [
70
+ 'neurcode plan "Describe the change"',
71
+ 'neurcode contract import --auto-detect --write-change-contract',
72
+ 'neurcode prompt',
73
+ 'neurcode ship "Goal" --max-fix-attempts 2',
71
74
  'neurcode ask "<question>"',
72
75
  'neurcode simulate --base origin/main',
73
76
  'neurcode guard start && neurcode guard check --staged',
@@ -121,7 +124,7 @@ showWelcomeIfNeeded().catch(() => {
121
124
  });
122
125
  program
123
126
  .command('start')
124
- .description('Show guided Neurcode flow (init -> plan -> prompt -> verify -> ship)')
127
+ .description('Show guided Neurcode flow (init -> login -> verify -> fix)')
125
128
  .option('--run-init', 'Run `neurcode init` immediately after showing the guide')
126
129
  .option('--json', 'Output machine-readable onboarding metadata')
127
130
  .action(async (options) => {
@@ -643,6 +646,37 @@ program
643
646
  json: options.json === true,
644
647
  });
645
648
  });
649
+ program
650
+ .command('fix')
651
+ .description('Primary alias for remediation loop (verify -> fix -> verify)')
652
+ .option('--goal <text>', 'Goal text for remediation loop')
653
+ .option('--plan-id <id>', 'Plan ID for verify scope checks')
654
+ .option('--project-id <id>', 'Project ID override')
655
+ .option('--max-fix-attempts <n>', 'Maximum remediation attempts (default: 2)', (val) => parseInt(val, 10))
656
+ .option('--policy-only', 'Run in policy-only verification mode')
657
+ .option('--require-plan', 'Fail verify if plan context is missing')
658
+ .option('--strict-artifacts', 'Require deterministic compiled-policy/change-contract artifacts')
659
+ .option('--no-strict-artifacts', 'Disable strict deterministic artifact enforcement')
660
+ .option('--enforce-change-contract', 'Require change contract enforcement')
661
+ .option('--no-enforce-change-contract', 'Disable change contract enforcement')
662
+ .option('--require-runtime-guard', 'Require runtime guard checks before each remediation attempt')
663
+ .option('--no-record', 'Disable cloud recording during verify/ship runs')
664
+ .option('--json', 'Output machine-readable JSON')
665
+ .action((options) => {
666
+ (0, remediate_1.remediateCommand)({
667
+ goal: options.goal,
668
+ planId: options.planId,
669
+ projectId: options.projectId,
670
+ maxFixAttempts: Number.isFinite(options.maxFixAttempts) ? options.maxFixAttempts : undefined,
671
+ policyOnly: options.policyOnly === true,
672
+ requirePlan: options.requirePlan === true,
673
+ strictArtifacts: options.strictArtifacts !== false,
674
+ enforceChangeContract: options.enforceChangeContract !== false,
675
+ requireRuntimeGuard: options.requireRuntimeGuard === true,
676
+ noRecord: options.record === false,
677
+ json: options.json === true,
678
+ });
679
+ });
646
680
  program
647
681
  .command('verify')
648
682
  .description('Verify plan adherence - Compare current changes against an Architect Plan')
@@ -668,6 +702,7 @@ program
668
702
  .option('--head', 'Verify changes against HEAD')
669
703
  .option('--base <ref>', 'Verify changes against a specific base ref')
670
704
  .option('--explain', 'Include AI change justification details in human-readable output')
705
+ .option('--demo', 'Demo mode: print extra explanatory output')
671
706
  .option('--json', 'Output results as JSON')
672
707
  .option('--record', 'Report verification results to Neurcode Cloud')
673
708
  .option('--api-key <key>', 'Neurcode API Key (overrides config and env var)')
@@ -679,6 +714,7 @@ program
679
714
  staged: options.staged,
680
715
  head: options.head,
681
716
  base: options.base,
717
+ demo: options.demo === true,
682
718
  explain: options.explain === true,
683
719
  json: options.json,
684
720
  record: options.record,
@@ -0,0 +1,20 @@
1
+ import type { DiffFile } from '@neurcode-ai/diff-parser';
2
+ export type AdvisorySignalSeverity = 'info' | 'warn';
3
+ export interface AdvisorySignal {
4
+ code: 'SENSITIVE_DOMAIN_SPAN' | 'DIRECT_DB_IN_REQUEST_LAYER' | 'LARGE_CHANGE_SURFACE' | 'CODE_WITHOUT_TEST_UPDATES' | 'INFRA_AND_APP_MIXED' | 'POSSIBLE_SECRET_ADDITION';
5
+ severity: AdvisorySignalSeverity;
6
+ title: string;
7
+ detail: string;
8
+ files: string[];
9
+ }
10
+ interface AdvisoryInput {
11
+ diffFiles: DiffFile[];
12
+ summary?: {
13
+ totalFiles: number;
14
+ totalAdded: number;
15
+ totalRemoved: number;
16
+ };
17
+ }
18
+ export declare function evaluateAdvisorySignals(input: AdvisoryInput): AdvisorySignal[];
19
+ export {};
20
+ //# sourceMappingURL=advisory-signals.d.ts.map
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.evaluateAdvisorySignals = evaluateAdvisorySignals;
4
+ function toUnixPath(filePath) {
5
+ return String(filePath || '').replace(/\\/g, '/');
6
+ }
7
+ function unique(items) {
8
+ return [...new Set(items)];
9
+ }
10
+ function isRequestLayerPath(path) {
11
+ return /(\/|^)(route|routes|controller|controllers|handler|handlers|api)(\/|$)/i.test(path);
12
+ }
13
+ function isTestPath(path) {
14
+ return /(^|\/)(__tests__|tests?|test)(\/|$)|\.(test|spec)\.[A-Za-z0-9]+$/i.test(path);
15
+ }
16
+ function isCodePath(path) {
17
+ return /\.(ts|tsx|js|jsx|mjs|cjs|py|go|java|rb|cs|php|rs|kt|swift)$/i.test(path);
18
+ }
19
+ function isInfraPath(path) {
20
+ return /(\/|^)(infra|terraform|k8s|kubernetes|helm|ansible|iac|cloudformation|pulumi|\.github\/workflows)(\/|$)/i.test(path);
21
+ }
22
+ function isAppPath(path) {
23
+ return /(\/|^)(src|app|services|packages|web)(\/|$)/i.test(path);
24
+ }
25
+ function isGeneratedArtifactPath(path) {
26
+ const normalized = path.toLowerCase();
27
+ return (normalized.endsWith('.map')
28
+ || normalized.includes('/dist/')
29
+ || normalized.includes('/build/')
30
+ || normalized.includes('/coverage/')
31
+ || normalized.includes('/.next/')
32
+ || normalized.includes('/out/')
33
+ || /\.min\.(js|css)$/.test(normalized));
34
+ }
35
+ function classifySensitiveDomains(path) {
36
+ const normalized = path.toLowerCase();
37
+ const domains = [];
38
+ if (/\b(auth|rbac|permission|acl|identity|oauth|session|jwt)\b/.test(normalized)) {
39
+ domains.push('auth');
40
+ }
41
+ if (/\b(payment|billing|invoice|refund|wallet|checkout)\b/.test(normalized)) {
42
+ domains.push('payment');
43
+ }
44
+ if (/\b(db|database|prisma|migration|sql|repository|repositories)\b/.test(normalized)) {
45
+ domains.push('data');
46
+ }
47
+ if (isInfraPath(normalized)) {
48
+ domains.push('infra');
49
+ }
50
+ return domains;
51
+ }
52
+ function findAddedLines(diffFiles) {
53
+ const lines = [];
54
+ for (const file of diffFiles) {
55
+ const filePath = toUnixPath(file.path);
56
+ for (const hunk of file.hunks || []) {
57
+ for (const line of hunk.lines || []) {
58
+ if (line.type === 'added') {
59
+ lines.push({ file: filePath, content: line.content });
60
+ }
61
+ }
62
+ }
63
+ }
64
+ return lines;
65
+ }
66
+ function limitFiles(files, max = 6) {
67
+ const deduped = unique(files);
68
+ if (deduped.length <= max)
69
+ return deduped;
70
+ return deduped.slice(0, max);
71
+ }
72
+ function evaluateAdvisorySignals(input) {
73
+ const diffFiles = input.diffFiles || [];
74
+ const changedPaths = unique(diffFiles.map((file) => toUnixPath(file.path)).filter(Boolean));
75
+ const totalFiles = input.summary?.totalFiles ?? changedPaths.length;
76
+ const totalAdded = input.summary?.totalAdded ?? 0;
77
+ const totalRemoved = input.summary?.totalRemoved ?? 0;
78
+ const signals = [];
79
+ const sensitiveDomains = new Set();
80
+ const filesBySensitiveDomain = {};
81
+ for (const path of changedPaths) {
82
+ const domains = classifySensitiveDomains(path);
83
+ for (const domain of domains) {
84
+ sensitiveDomains.add(domain);
85
+ if (!filesBySensitiveDomain[domain]) {
86
+ filesBySensitiveDomain[domain] = [];
87
+ }
88
+ filesBySensitiveDomain[domain].push(path);
89
+ }
90
+ }
91
+ if (sensitiveDomains.size >= 2) {
92
+ const domains = [...sensitiveDomains].sort((a, b) => a.localeCompare(b));
93
+ const files = limitFiles(domains.flatMap((domain) => filesBySensitiveDomain[domain] || []));
94
+ signals.push({
95
+ code: 'SENSITIVE_DOMAIN_SPAN',
96
+ severity: 'warn',
97
+ title: 'Changes span multiple sensitive domains',
98
+ detail: `Changes touch ${domains.join(' + ')} modules in one diff. ` +
99
+ 'This pattern often indicates unintended side effects or architectural drift.',
100
+ files,
101
+ });
102
+ }
103
+ const dbCallPattern = /\b(prisma|db|sequelize)\.[A-Za-z_$][A-Za-z0-9_$]*\.[A-Za-z_$][A-Za-z0-9_$]*\s*\(|\bknex\s*\(/;
104
+ const directDbRequestLayerFiles = findAddedLines(diffFiles)
105
+ .filter((line) => isRequestLayerPath(line.file) && dbCallPattern.test(line.content))
106
+ .map((line) => line.file);
107
+ if (directDbRequestLayerFiles.length > 0) {
108
+ signals.push({
109
+ code: 'DIRECT_DB_IN_REQUEST_LAYER',
110
+ severity: 'warn',
111
+ title: 'Direct DB access detected in request layer',
112
+ detail: 'Route/controller code now calls the DB directly. This can bypass service-layer controls and increase drift risk.',
113
+ files: limitFiles(directDbRequestLayerFiles),
114
+ });
115
+ }
116
+ const totalDelta = totalAdded + totalRemoved;
117
+ if (totalFiles >= 15 || totalDelta >= 600) {
118
+ signals.push({
119
+ code: 'LARGE_CHANGE_SURFACE',
120
+ severity: 'warn',
121
+ title: 'Large change surface',
122
+ detail: `This diff touches ${totalFiles} file(s) and ${totalDelta} changed line(s). ` +
123
+ 'Large surfaces reduce review certainty and raise drift probability.',
124
+ files: limitFiles(changedPaths),
125
+ });
126
+ }
127
+ const codeFilesTouched = changedPaths.filter((path) => isCodePath(path) && !isTestPath(path) && !path.startsWith('docs/'));
128
+ const nonGeneratedCodeFilesTouched = codeFilesTouched.filter((path) => !isGeneratedArtifactPath(path));
129
+ const testFilesTouched = changedPaths.filter((path) => isTestPath(path));
130
+ if (nonGeneratedCodeFilesTouched.length >= 3 && testFilesTouched.length === 0) {
131
+ signals.push({
132
+ code: 'CODE_WITHOUT_TEST_UPDATES',
133
+ severity: nonGeneratedCodeFilesTouched.length >= 8 ? 'warn' : 'info',
134
+ title: 'Code changed without test updates',
135
+ detail: `Detected ${nonGeneratedCodeFilesTouched.length} code file change(s) with no test file updates. ` +
136
+ 'Behavior drift may go unnoticed without verification tests.',
137
+ files: limitFiles(nonGeneratedCodeFilesTouched),
138
+ });
139
+ }
140
+ const infraFiles = changedPaths.filter((path) => isInfraPath(path));
141
+ const appFiles = changedPaths.filter((path) => isAppPath(path) && !isInfraPath(path));
142
+ if (infraFiles.length > 0 && appFiles.length > 0) {
143
+ signals.push({
144
+ code: 'INFRA_AND_APP_MIXED',
145
+ severity: 'warn',
146
+ title: 'Infra and app code changed together',
147
+ detail: 'This diff mixes infrastructure and application edits. Consider splitting for clearer intent and rollback safety.',
148
+ files: limitFiles([...infraFiles, ...appFiles]),
149
+ });
150
+ }
151
+ const secretPattern = /(AKIA[0-9A-Z]{16}|-----BEGIN [A-Z ]*PRIVATE KEY-----|(?:api[_-]?key|secret|password)\s*[:=]\s*(?:"[^"]{8,}"|'[^']{8,}'|`[^`]{8,}`))/i;
152
+ const possibleSecretFiles = findAddedLines(diffFiles)
153
+ .filter((line) => !isGeneratedArtifactPath(line.file))
154
+ .filter((line) => line.content.trim().length <= 240)
155
+ .filter((line) => !/\[REDACTED\]|process\.env\./i.test(line.content))
156
+ .filter((line) => secretPattern.test(line.content))
157
+ .map((line) => line.file);
158
+ if (possibleSecretFiles.length > 0) {
159
+ signals.push({
160
+ code: 'POSSIBLE_SECRET_ADDITION',
161
+ severity: 'warn',
162
+ title: 'Possible secret-like value added',
163
+ detail: 'Detected new lines resembling secrets or credentials. Validate redaction and secret management before merge.',
164
+ files: limitFiles(possibleSecretFiles),
165
+ });
166
+ }
167
+ const precedence = { warn: 0, info: 1 };
168
+ return signals
169
+ .sort((left, right) => {
170
+ const severityDelta = precedence[left.severity] - precedence[right.severity];
171
+ if (severityDelta !== 0)
172
+ return severityDelta;
173
+ return left.title.localeCompare(right.title);
174
+ })
175
+ .slice(0, 4);
176
+ }
177
+ //# sourceMappingURL=advisory-signals.js.map
@@ -10,9 +10,69 @@ export interface ChangeContract {
10
10
  intentHash: string;
11
11
  expectedFiles: string[];
12
12
  expectedFilesFingerprint: string;
13
+ planFiles?: ChangeContractPlanFile[];
14
+ expectedSymbols?: ChangeContractExpectedSymbol[];
15
+ options?: ChangeContractOptions;
13
16
  policyLockFingerprint: string | null;
14
17
  compiledPolicyFingerprint: string | null;
15
18
  }
19
+ export type ChangeContractPlanAction = 'CREATE' | 'MODIFY' | 'BLOCK';
20
+ export type ChangeContractDiffAction = 'add' | 'delete' | 'modify' | 'rename';
21
+ export type ChangeContractSymbolAction = 'CREATE' | 'MODIFY' | 'BLOCK';
22
+ export type ChangeContractSymbolType = 'function' | 'class' | 'interface' | 'type' | 'method' | 'const' | 'unknown';
23
+ export type ChangeContractDiffSymbolAction = 'add' | 'delete' | 'modify';
24
+ export interface ChangeContractPlanFile {
25
+ path: string;
26
+ action: ChangeContractPlanAction;
27
+ reason?: string;
28
+ }
29
+ export interface ChangeContractExpectedSymbol {
30
+ name: string;
31
+ action: ChangeContractSymbolAction;
32
+ type?: ChangeContractSymbolType;
33
+ file?: string;
34
+ reason?: string;
35
+ }
36
+ export interface ChangeContractOptions {
37
+ /**
38
+ * When enabled, files expected by the contract must be changed in the current diff.
39
+ * This is opt-in to avoid breaking partial/iterative delivery workflows.
40
+ */
41
+ enforceExpectedFiles?: boolean;
42
+ /**
43
+ * When enabled, diff operations are validated against planned file actions.
44
+ */
45
+ enforceActionMatching?: boolean;
46
+ /**
47
+ * Allows rename operations to satisfy MODIFY expectations.
48
+ * Defaults to true when action matching is enabled.
49
+ */
50
+ allowRenameForModify?: boolean;
51
+ /**
52
+ * When enabled, symbols expected by the contract must be present in changed symbol declarations.
53
+ */
54
+ enforceExpectedSymbols?: boolean;
55
+ /**
56
+ * When enabled, symbol-level operation matching is validated against expected symbol actions.
57
+ */
58
+ enforceSymbolActionMatching?: boolean;
59
+ /**
60
+ * When enabled (default), function/method/const symbol kinds can match compatible declarations.
61
+ */
62
+ symbolTypeRelaxedMatching?: boolean;
63
+ /**
64
+ * Allows expected symbol file constraints to fallback to basename matching.
65
+ */
66
+ symbolFileBasenameFallback?: boolean;
67
+ /**
68
+ * Tolerate this many out-of-contract file touches before failing.
69
+ */
70
+ maxUnexpectedFiles?: number;
71
+ /**
72
+ * Tolerate this many missing expected symbols before failing.
73
+ */
74
+ maxMissingExpectedSymbols?: number;
75
+ }
16
76
  export interface ReadChangeContractResult {
17
77
  path: string;
18
78
  exists: boolean;
@@ -20,9 +80,11 @@ export interface ReadChangeContractResult {
20
80
  error?: string;
21
81
  }
22
82
  export interface ChangeContractViolation {
23
- code: 'CHANGE_CONTRACT_PLAN_MISMATCH' | 'CHANGE_CONTRACT_UNEXPECTED_FILE' | 'CHANGE_CONTRACT_POLICY_LOCK_MISMATCH' | 'CHANGE_CONTRACT_COMPILED_POLICY_MISMATCH';
83
+ code: 'CHANGE_CONTRACT_PLAN_MISMATCH' | 'CHANGE_CONTRACT_UNEXPECTED_FILE' | 'CHANGE_CONTRACT_MISSING_EXPECTED_FILE' | 'CHANGE_CONTRACT_BLOCKED_FILE_TOUCHED' | 'CHANGE_CONTRACT_ACTION_MISMATCH' | 'CHANGE_CONTRACT_MISSING_EXPECTED_SYMBOL' | 'CHANGE_CONTRACT_BLOCKED_SYMBOL_TOUCHED' | 'CHANGE_CONTRACT_SYMBOL_ACTION_MISMATCH' | 'CHANGE_CONTRACT_POLICY_LOCK_MISMATCH' | 'CHANGE_CONTRACT_COMPILED_POLICY_MISMATCH';
24
84
  message: string;
25
85
  file?: string;
86
+ symbol?: string;
87
+ symbolType?: string;
26
88
  expected?: string;
27
89
  actual?: string;
28
90
  }
@@ -33,6 +95,17 @@ export interface ChangeContractEvaluation {
33
95
  expectedFiles: number;
34
96
  changedFiles: number;
35
97
  outOfContractFiles: number;
98
+ missingExpectedFiles: number;
99
+ blockedFilesTouched: number;
100
+ actionMismatches: number;
101
+ expectedSymbols: number;
102
+ changedSymbols: number;
103
+ missingExpectedSymbols: number;
104
+ blockedSymbolsTouched: number;
105
+ symbolActionMismatches: number;
106
+ symbolRenameMatches: number;
107
+ toleratedUnexpectedFiles: number;
108
+ toleratedMissingExpectedSymbols: number;
36
109
  };
37
110
  }
38
111
  export declare function createChangeContract(input: {
@@ -42,6 +115,19 @@ export declare function createChangeContract(input: {
42
115
  projectId?: string | null;
43
116
  intent: string;
44
117
  expectedFiles: string[];
118
+ planFiles?: Array<{
119
+ path: string;
120
+ action: ChangeContractPlanAction;
121
+ reason?: string;
122
+ }>;
123
+ expectedSymbols?: Array<{
124
+ name: string;
125
+ action: ChangeContractSymbolAction;
126
+ type?: ChangeContractSymbolType;
127
+ file?: string;
128
+ reason?: string;
129
+ }>;
130
+ options?: ChangeContractOptions;
45
131
  policyLockFingerprint?: string | null;
46
132
  compiledPolicyFingerprint?: string | null;
47
133
  }): ChangeContract;
@@ -51,7 +137,25 @@ export declare function readChangeContract(projectRoot: string, inputPath?: stri
51
137
  export declare function evaluateChangeContract(contract: ChangeContract, input: {
52
138
  planId: string;
53
139
  changedFiles: string[];
140
+ changedFileEntries?: Array<{
141
+ path: string;
142
+ changeType: ChangeContractDiffAction;
143
+ }>;
144
+ changedSymbols?: Array<{
145
+ name: string;
146
+ action: ChangeContractDiffSymbolAction;
147
+ type?: ChangeContractSymbolType;
148
+ file?: string;
149
+ }>;
54
150
  policyLockFingerprint?: string | null;
55
151
  compiledPolicyFingerprint?: string | null;
56
152
  }): ChangeContractEvaluation;
153
+ export interface ChangeContractViolationGroup {
154
+ key: 'out_of_scope_changes' | 'missing_expected_files' | 'blocked_files_touched' | 'file_action_mismatches' | 'missing_expected_symbols' | 'blocked_symbols_touched' | 'symbol_action_mismatches' | 'contract_metadata_mismatches' | 'other';
155
+ title: string;
156
+ impact: string;
157
+ items: string[];
158
+ count: number;
159
+ }
160
+ export declare function groupChangeContractViolations(violations: ChangeContractViolation[]): ChangeContractViolationGroup[];
57
161
  //# sourceMappingURL=change-contract.d.ts.map