@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.
- package/dist/commands/contract.js +47 -0
- package/dist/commands/plan.js +40 -0
- package/dist/commands/verify.d.ts +2 -0
- package/dist/commands/verify.js +240 -114
- package/dist/index.js +41 -5
- package/dist/utils/advisory-signals.d.ts +20 -0
- package/dist/utils/advisory-signals.js +177 -0
- package/dist/utils/change-contract.d.ts +105 -1
- package/dist/utils/change-contract.js +685 -12
- package/dist/utils/diff-symbols.d.ts +10 -0
- package/dist/utils/diff-symbols.js +218 -0
- package/dist/utils/plan-symbols.d.ts +17 -0
- package/dist/utils/plan-symbols.js +209 -0
- package/package.json +6 -14
- package/LICENSE +0 -201
- package/dist/api-client.d.ts.map +0 -1
- package/dist/api-client.js.map +0 -1
- package/dist/commands/allow.d.ts.map +0 -1
- package/dist/commands/allow.js.map +0 -1
- package/dist/commands/apply.d.ts.map +0 -1
- package/dist/commands/apply.js.map +0 -1
- package/dist/commands/approve.d.ts.map +0 -1
- package/dist/commands/approve.js.map +0 -1
- package/dist/commands/ask.d.ts.map +0 -1
- package/dist/commands/ask.js.map +0 -1
- package/dist/commands/audit.d.ts.map +0 -1
- package/dist/commands/audit.js.map +0 -1
- package/dist/commands/bootstrap.d.ts.map +0 -1
- package/dist/commands/bootstrap.js.map +0 -1
- package/dist/commands/brain.d.ts.map +0 -1
- package/dist/commands/brain.js.map +0 -1
- package/dist/commands/check.d.ts.map +0 -1
- package/dist/commands/check.js.map +0 -1
- package/dist/commands/config.d.ts.map +0 -1
- package/dist/commands/config.js.map +0 -1
- package/dist/commands/contract.d.ts.map +0 -1
- package/dist/commands/contract.js.map +0 -1
- package/dist/commands/doctor.d.ts.map +0 -1
- package/dist/commands/doctor.js.map +0 -1
- package/dist/commands/feedback.d.ts.map +0 -1
- package/dist/commands/feedback.js.map +0 -1
- package/dist/commands/guard.d.ts.map +0 -1
- package/dist/commands/guard.js.map +0 -1
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/login.d.ts.map +0 -1
- package/dist/commands/login.js.map +0 -1
- package/dist/commands/logout.d.ts.map +0 -1
- package/dist/commands/logout.js.map +0 -1
- package/dist/commands/map.d.ts.map +0 -1
- package/dist/commands/map.js.map +0 -1
- package/dist/commands/plan-slo.d.ts.map +0 -1
- package/dist/commands/plan-slo.js.map +0 -1
- package/dist/commands/plan.d.ts.map +0 -1
- package/dist/commands/plan.js.map +0 -1
- package/dist/commands/policy.d.ts.map +0 -1
- package/dist/commands/policy.js.map +0 -1
- package/dist/commands/prompt.d.ts.map +0 -1
- package/dist/commands/prompt.js.map +0 -1
- package/dist/commands/refactor.d.ts.map +0 -1
- package/dist/commands/refactor.js.map +0 -1
- package/dist/commands/remediate.d.ts.map +0 -1
- package/dist/commands/remediate.js.map +0 -1
- package/dist/commands/repo.d.ts.map +0 -1
- package/dist/commands/repo.js.map +0 -1
- package/dist/commands/revert.d.ts.map +0 -1
- package/dist/commands/revert.js.map +0 -1
- package/dist/commands/security.d.ts.map +0 -1
- package/dist/commands/security.js.map +0 -1
- package/dist/commands/session.d.ts.map +0 -1
- package/dist/commands/session.js.map +0 -1
- package/dist/commands/ship.d.ts.map +0 -1
- package/dist/commands/ship.js.map +0 -1
- package/dist/commands/simulate.d.ts.map +0 -1
- package/dist/commands/simulate.js.map +0 -1
- package/dist/commands/verify.d.ts.map +0 -1
- package/dist/commands/verify.js.map +0 -1
- package/dist/commands/watch.d.ts.map +0 -1
- package/dist/commands/watch.js.map +0 -1
- package/dist/commands/whoami.d.ts.map +0 -1
- package/dist/commands/whoami.js.map +0 -1
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/rules.d.ts.map +0 -1
- package/dist/rules.js.map +0 -1
- package/dist/services/integrations/TicketService.d.ts.map +0 -1
- package/dist/services/integrations/TicketService.js.map +0 -1
- package/dist/services/mapper/ProjectScanner.d.ts.map +0 -1
- package/dist/services/mapper/ProjectScanner.js.map +0 -1
- package/dist/services/project-knowledge-service.d.ts.map +0 -1
- package/dist/services/project-knowledge-service.js.map +0 -1
- package/dist/services/security/SecurityGuard.d.ts.map +0 -1
- package/dist/services/security/SecurityGuard.js.map +0 -1
- package/dist/services/toolbox-service.d.ts.map +0 -1
- package/dist/services/toolbox-service.js.map +0 -1
- package/dist/services/watch/BlobStore.d.ts.map +0 -1
- package/dist/services/watch/BlobStore.js.map +0 -1
- package/dist/services/watch/CommandPoller.d.ts.map +0 -1
- package/dist/services/watch/CommandPoller.js.map +0 -1
- package/dist/services/watch/Journal.d.ts.map +0 -1
- package/dist/services/watch/Journal.js.map +0 -1
- package/dist/services/watch/Sentinel.d.ts.map +0 -1
- package/dist/services/watch/Sentinel.js.map +0 -1
- package/dist/services/watch/Syncer.d.ts.map +0 -1
- package/dist/services/watch/Syncer.js.map +0 -1
- package/dist/utils/ROILogger.d.ts.map +0 -1
- package/dist/utils/ROILogger.js.map +0 -1
- package/dist/utils/RelevanceScorer.d.ts.map +0 -1
- package/dist/utils/RelevanceScorer.js.map +0 -1
- package/dist/utils/ai-debt-budget.d.ts.map +0 -1
- package/dist/utils/ai-debt-budget.js.map +0 -1
- package/dist/utils/artifact-signature.d.ts.map +0 -1
- package/dist/utils/artifact-signature.js.map +0 -1
- package/dist/utils/ask-cache.d.ts.map +0 -1
- package/dist/utils/ask-cache.js.map +0 -1
- package/dist/utils/box.d.ts.map +0 -1
- package/dist/utils/box.js.map +0 -1
- package/dist/utils/brain-context.d.ts.map +0 -1
- package/dist/utils/brain-context.js.map +0 -1
- package/dist/utils/breakage-simulator.d.ts.map +0 -1
- package/dist/utils/breakage-simulator.js.map +0 -1
- package/dist/utils/change-contract.d.ts.map +0 -1
- package/dist/utils/change-contract.js.map +0 -1
- package/dist/utils/cli-json.d.ts.map +0 -1
- package/dist/utils/cli-json.js.map +0 -1
- package/dist/utils/custom-policy-rules.d.ts.map +0 -1
- package/dist/utils/custom-policy-rules.js.map +0 -1
- package/dist/utils/git.d.ts.map +0 -1
- package/dist/utils/git.js.map +0 -1
- package/dist/utils/gitignore.d.ts.map +0 -1
- package/dist/utils/gitignore.js.map +0 -1
- package/dist/utils/governance.d.ts.map +0 -1
- package/dist/utils/governance.js.map +0 -1
- package/dist/utils/ignore.d.ts.map +0 -1
- package/dist/utils/ignore.js.map +0 -1
- package/dist/utils/manual-approvals.d.ts.map +0 -1
- package/dist/utils/manual-approvals.js.map +0 -1
- package/dist/utils/messages.d.ts.map +0 -1
- package/dist/utils/messages.js.map +0 -1
- package/dist/utils/neurcode-context.d.ts.map +0 -1
- package/dist/utils/neurcode-context.js.map +0 -1
- package/dist/utils/plan-cache.d.ts.map +0 -1
- package/dist/utils/plan-cache.js.map +0 -1
- package/dist/utils/plan-slo.d.ts.map +0 -1
- package/dist/utils/plan-slo.js.map +0 -1
- package/dist/utils/policy-audit.d.ts.map +0 -1
- package/dist/utils/policy-audit.js.map +0 -1
- package/dist/utils/policy-compiler.d.ts.map +0 -1
- package/dist/utils/policy-compiler.js.map +0 -1
- package/dist/utils/policy-exceptions.d.ts.map +0 -1
- package/dist/utils/policy-exceptions.js.map +0 -1
- package/dist/utils/policy-governance.d.ts.map +0 -1
- package/dist/utils/policy-governance.js.map +0 -1
- package/dist/utils/policy-packs.d.ts.map +0 -1
- package/dist/utils/policy-packs.js.map +0 -1
- package/dist/utils/project-detector.d.ts.map +0 -1
- package/dist/utils/project-detector.js.map +0 -1
- package/dist/utils/project-root.d.ts.map +0 -1
- package/dist/utils/project-root.js.map +0 -1
- package/dist/utils/repo-links.d.ts.map +0 -1
- package/dist/utils/repo-links.js.map +0 -1
- package/dist/utils/restore.d.ts.map +0 -1
- package/dist/utils/restore.js.map +0 -1
- package/dist/utils/runtime-guard.d.ts.map +0 -1
- package/dist/utils/runtime-guard.js.map +0 -1
- package/dist/utils/scope-telemetry.d.ts.map +0 -1
- package/dist/utils/scope-telemetry.js.map +0 -1
- package/dist/utils/secret-masking.d.ts.map +0 -1
- package/dist/utils/secret-masking.js.map +0 -1
- package/dist/utils/state.d.ts.map +0 -1
- package/dist/utils/state.js.map +0 -1
- package/dist/utils/tier.d.ts.map +0 -1
- package/dist/utils/tier.js.map +0 -1
- package/dist/utils/user-context.d.ts.map +0 -1
- package/dist/utils/user-context.js.map +0 -1
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { DiffFile } from '@neurcode-ai/diff-parser';
|
|
2
|
+
import type { ChangeContractDiffSymbolAction, ChangeContractSymbolType } from './change-contract';
|
|
3
|
+
export interface DiffSymbolChange {
|
|
4
|
+
name: string;
|
|
5
|
+
type: ChangeContractSymbolType;
|
|
6
|
+
action: ChangeContractDiffSymbolAction;
|
|
7
|
+
file: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function extractDeclaredSymbolsFromDiff(diffFiles: DiffFile[]): DiffSymbolChange[];
|
|
10
|
+
//# sourceMappingURL=diff-symbols.d.ts.map
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractDeclaredSymbolsFromDiff = extractDeclaredSymbolsFromDiff;
|
|
4
|
+
const SYMBOL_EXTENSIONS = new Set([
|
|
5
|
+
'.ts',
|
|
6
|
+
'.tsx',
|
|
7
|
+
'.mts',
|
|
8
|
+
'.cts',
|
|
9
|
+
'.js',
|
|
10
|
+
'.jsx',
|
|
11
|
+
'.mjs',
|
|
12
|
+
'.cjs',
|
|
13
|
+
'.py',
|
|
14
|
+
'.go',
|
|
15
|
+
]);
|
|
16
|
+
function normalizeRepoPath(value) {
|
|
17
|
+
return value.replace(/\\/g, '/').replace(/^\.\//, '').trim();
|
|
18
|
+
}
|
|
19
|
+
function extname(filePath) {
|
|
20
|
+
const normalized = normalizeRepoPath(filePath);
|
|
21
|
+
const lastSlash = normalized.lastIndexOf('/');
|
|
22
|
+
const lastDot = normalized.lastIndexOf('.');
|
|
23
|
+
if (lastDot <= lastSlash)
|
|
24
|
+
return '';
|
|
25
|
+
return normalized.slice(lastDot).toLowerCase();
|
|
26
|
+
}
|
|
27
|
+
function isSymbolCandidateFile(filePath) {
|
|
28
|
+
const extension = extname(filePath);
|
|
29
|
+
return SYMBOL_EXTENSIONS.has(extension);
|
|
30
|
+
}
|
|
31
|
+
function normalizeSymbolName(value) {
|
|
32
|
+
return String(value || '')
|
|
33
|
+
.trim()
|
|
34
|
+
.replace(/^['"`]+|['"`]+$/g, '')
|
|
35
|
+
.replace(/\(\)\s*$/, '');
|
|
36
|
+
}
|
|
37
|
+
function isValidIdentifier(value) {
|
|
38
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(value);
|
|
39
|
+
}
|
|
40
|
+
function stripInlineComments(line) {
|
|
41
|
+
const hashIndex = line.indexOf('#');
|
|
42
|
+
if (hashIndex >= 0 && !/["'`]/.test(line.slice(0, hashIndex))) {
|
|
43
|
+
return line.slice(0, hashIndex);
|
|
44
|
+
}
|
|
45
|
+
const slashIndex = line.indexOf('//');
|
|
46
|
+
if (slashIndex >= 0) {
|
|
47
|
+
return line.slice(0, slashIndex);
|
|
48
|
+
}
|
|
49
|
+
return line;
|
|
50
|
+
}
|
|
51
|
+
const METHOD_RESERVED_NAMES = new Set([
|
|
52
|
+
'if',
|
|
53
|
+
'for',
|
|
54
|
+
'while',
|
|
55
|
+
'switch',
|
|
56
|
+
'catch',
|
|
57
|
+
'return',
|
|
58
|
+
'else',
|
|
59
|
+
'do',
|
|
60
|
+
'try',
|
|
61
|
+
'finally',
|
|
62
|
+
'throw',
|
|
63
|
+
'constructor',
|
|
64
|
+
]);
|
|
65
|
+
function extractDeclaredSymbolsFromLine(line) {
|
|
66
|
+
const sanitizedLine = stripInlineComments(line);
|
|
67
|
+
const matches = [];
|
|
68
|
+
const seen = new Set();
|
|
69
|
+
const push = (name, type) => {
|
|
70
|
+
const normalized = normalizeSymbolName(name);
|
|
71
|
+
if (!isValidIdentifier(normalized))
|
|
72
|
+
return;
|
|
73
|
+
const key = `${type}::${normalized}`;
|
|
74
|
+
if (seen.has(key))
|
|
75
|
+
return;
|
|
76
|
+
seen.add(key);
|
|
77
|
+
matches.push({ name: normalized, type });
|
|
78
|
+
};
|
|
79
|
+
const patternSpecs = [
|
|
80
|
+
{
|
|
81
|
+
type: 'class',
|
|
82
|
+
regex: /^\s*(?:export\s+)?(?:default\s+)?class\s+([A-Za-z_$][A-Za-z0-9_$]*)\b/,
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
type: 'class',
|
|
86
|
+
regex: /^\s*(?:export\s+)?default\s+class(?:\s+([A-Za-z_$][A-Za-z0-9_$]*))?\b/,
|
|
87
|
+
resolver: (match) => (match[1] ? match[1] : 'default'),
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
type: 'interface',
|
|
91
|
+
regex: /^\s*(?:export\s+)?interface\s+([A-Za-z_$][A-Za-z0-9_$]*)\b/,
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
type: 'type',
|
|
95
|
+
regex: /^\s*(?:export\s+)?type\s+([A-Za-z_$][A-Za-z0-9_$]*)\b/,
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
type: 'function',
|
|
99
|
+
regex: /^\s*(?:export\s+)?(?:default\s+)?(?:async\s+)?function\s+([A-Za-z_$][A-Za-z0-9_$]*)\s*\(/,
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
type: 'function',
|
|
103
|
+
regex: /^\s*(?:export\s+)?default\s+(?:async\s+)?function\s*\(/,
|
|
104
|
+
resolver: () => 'default',
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
type: 'function',
|
|
108
|
+
regex: /^\s*(?:export\s+)?(?:const|let|var)\s+([A-Za-z_$][A-Za-z0-9_$]*)\s*=\s*(?:async\s*)?(?:\([^)]*\)|[A-Za-z_$][A-Za-z0-9_$]*)\s*=>/,
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
type: 'function',
|
|
112
|
+
regex: /^\s*(?:export\s+)?(?:const|let|var)\s+([A-Za-z_$][A-Za-z0-9_$]*)\s*=\s*(?:async\s*)?function\b/,
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
type: 'method',
|
|
116
|
+
regex: /^\s*(?:public|private|protected|readonly|static|async|get|set|\s)*([A-Za-z_$][A-Za-z0-9_$]*)\s*\([^;{}]*\)\s*\{/,
|
|
117
|
+
resolver: (match) => {
|
|
118
|
+
const candidate = match[1] || null;
|
|
119
|
+
if (!candidate)
|
|
120
|
+
return null;
|
|
121
|
+
if (METHOD_RESERVED_NAMES.has(candidate))
|
|
122
|
+
return null;
|
|
123
|
+
return candidate;
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
type: 'method',
|
|
128
|
+
regex: /^\s*(?:public|private|protected|readonly|static)\s+([A-Za-z_$][A-Za-z0-9_$]*)\s*=\s*(?:async\s*)?(?:\([^)]*\)|[A-Za-z_$][A-Za-z0-9_$]*)\s*=>/,
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
type: 'class',
|
|
132
|
+
regex: /^\s*class\s+([A-Za-z_][A-Za-z0-9_]*)\s*[\(:]/,
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
type: 'function',
|
|
136
|
+
regex: /^\s*def\s+([A-Za-z_][A-Za-z0-9_]*)\s*\(/,
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
type: 'function',
|
|
140
|
+
regex: /^\s*func\s+(?:\([^)]+\)\s*)?([A-Za-z_][A-Za-z0-9_]*)\s*\(/,
|
|
141
|
+
},
|
|
142
|
+
];
|
|
143
|
+
for (const spec of patternSpecs) {
|
|
144
|
+
const match = sanitizedLine.match(spec.regex);
|
|
145
|
+
if (!match)
|
|
146
|
+
continue;
|
|
147
|
+
const resolved = spec.resolver ? spec.resolver(match) : match[1] || null;
|
|
148
|
+
if (resolved) {
|
|
149
|
+
push(resolved, spec.type);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return matches;
|
|
153
|
+
}
|
|
154
|
+
function extractDeclaredSymbolsFromDiff(diffFiles) {
|
|
155
|
+
const counters = new Map();
|
|
156
|
+
for (const file of diffFiles) {
|
|
157
|
+
const normalizedPath = normalizeRepoPath(file.path);
|
|
158
|
+
if (!normalizedPath || !isSymbolCandidateFile(normalizedPath))
|
|
159
|
+
continue;
|
|
160
|
+
for (const hunk of file.hunks || []) {
|
|
161
|
+
const hasDelta = (hunk.lines || []).some((line) => line.type === 'added' || line.type === 'removed');
|
|
162
|
+
if (!hasDelta)
|
|
163
|
+
continue;
|
|
164
|
+
for (const line of hunk.lines || []) {
|
|
165
|
+
if (line.type !== 'added' && line.type !== 'removed' && line.type !== 'context')
|
|
166
|
+
continue;
|
|
167
|
+
const declared = extractDeclaredSymbolsFromLine(line.content);
|
|
168
|
+
if (declared.length === 0)
|
|
169
|
+
continue;
|
|
170
|
+
for (const symbol of declared) {
|
|
171
|
+
const key = `${normalizedPath}::${symbol.type}::${symbol.name}`;
|
|
172
|
+
const current = counters.get(key) || {
|
|
173
|
+
file: normalizedPath,
|
|
174
|
+
name: symbol.name,
|
|
175
|
+
type: symbol.type,
|
|
176
|
+
added: 0,
|
|
177
|
+
removed: 0,
|
|
178
|
+
context: 0,
|
|
179
|
+
};
|
|
180
|
+
if (line.type === 'added')
|
|
181
|
+
current.added += 1;
|
|
182
|
+
if (line.type === 'removed')
|
|
183
|
+
current.removed += 1;
|
|
184
|
+
if (line.type === 'context')
|
|
185
|
+
current.context += 1;
|
|
186
|
+
counters.set(key, current);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
const changes = [];
|
|
192
|
+
for (const item of counters.values()) {
|
|
193
|
+
let action = 'delete';
|
|
194
|
+
if (item.added > 0 && item.removed > 0) {
|
|
195
|
+
action = 'modify';
|
|
196
|
+
}
|
|
197
|
+
else if (item.added > 0) {
|
|
198
|
+
action = 'add';
|
|
199
|
+
}
|
|
200
|
+
else if (item.removed === 0 && item.context > 0) {
|
|
201
|
+
action = 'modify';
|
|
202
|
+
}
|
|
203
|
+
changes.push({
|
|
204
|
+
name: item.name,
|
|
205
|
+
type: item.type,
|
|
206
|
+
action,
|
|
207
|
+
file: item.file,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
return changes.sort((a, b) => {
|
|
211
|
+
if (a.file !== b.file)
|
|
212
|
+
return a.file.localeCompare(b.file);
|
|
213
|
+
if (a.name !== b.name)
|
|
214
|
+
return a.name.localeCompare(b.name);
|
|
215
|
+
return a.type.localeCompare(b.type);
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
//# sourceMappingURL=diff-symbols.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ChangeContractExpectedSymbol, ChangeContractSymbolAction } from './change-contract';
|
|
2
|
+
interface PlanFileLike {
|
|
3
|
+
path: string;
|
|
4
|
+
action: ChangeContractSymbolAction;
|
|
5
|
+
reason?: string;
|
|
6
|
+
suggestion?: string;
|
|
7
|
+
rationale?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface PlanLike {
|
|
10
|
+
summary?: string;
|
|
11
|
+
recommendations?: string[];
|
|
12
|
+
files?: PlanFileLike[];
|
|
13
|
+
symbols?: unknown;
|
|
14
|
+
}
|
|
15
|
+
export declare function mapPlanSymbolsForChangeContract(plan: PlanLike): ChangeContractExpectedSymbol[];
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=plan-symbols.d.ts.map
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mapPlanSymbolsForChangeContract = mapPlanSymbolsForChangeContract;
|
|
4
|
+
function normalizeRepoPath(value) {
|
|
5
|
+
return value.replace(/\\/g, '/').replace(/^\.\//, '').trim();
|
|
6
|
+
}
|
|
7
|
+
function normalizeSymbolName(value) {
|
|
8
|
+
return String(value || '')
|
|
9
|
+
.trim()
|
|
10
|
+
.replace(/^['"`]+|['"`]+$/g, '')
|
|
11
|
+
.replace(/\(\)\s*$/, '');
|
|
12
|
+
}
|
|
13
|
+
function normalizeSymbolType(value) {
|
|
14
|
+
if (!value)
|
|
15
|
+
return undefined;
|
|
16
|
+
const normalized = value.trim().toLowerCase();
|
|
17
|
+
if (normalized === 'function'
|
|
18
|
+
|| normalized === 'class'
|
|
19
|
+
|| normalized === 'interface'
|
|
20
|
+
|| normalized === 'type'
|
|
21
|
+
|| normalized === 'method'
|
|
22
|
+
|| normalized === 'const'
|
|
23
|
+
|| normalized === 'unknown') {
|
|
24
|
+
return normalized;
|
|
25
|
+
}
|
|
26
|
+
if (normalized === 'fn')
|
|
27
|
+
return 'function';
|
|
28
|
+
if (normalized === 'var' || normalized === 'variable')
|
|
29
|
+
return 'const';
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
function normalizeSymbolAction(value, fallback) {
|
|
33
|
+
const normalized = String(value || '').trim().toUpperCase();
|
|
34
|
+
if (normalized === 'CREATE' || normalized === 'MODIFY' || normalized === 'BLOCK') {
|
|
35
|
+
return normalized;
|
|
36
|
+
}
|
|
37
|
+
return fallback;
|
|
38
|
+
}
|
|
39
|
+
function extractSymbolMentionsFromText(text) {
|
|
40
|
+
if (!text || !text.trim())
|
|
41
|
+
return [];
|
|
42
|
+
const entries = [];
|
|
43
|
+
const seen = new Set();
|
|
44
|
+
const push = (name, type) => {
|
|
45
|
+
const normalizedName = normalizeSymbolName(name);
|
|
46
|
+
if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(normalizedName))
|
|
47
|
+
return;
|
|
48
|
+
if (normalizedName.length > 80)
|
|
49
|
+
return;
|
|
50
|
+
const key = `${type || 'unknown'}::${normalizedName}`;
|
|
51
|
+
if (seen.has(key))
|
|
52
|
+
return;
|
|
53
|
+
seen.add(key);
|
|
54
|
+
entries.push({
|
|
55
|
+
name: normalizedName,
|
|
56
|
+
...(type ? { type } : {}),
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
for (const match of text.matchAll(/`([A-Za-z_$][A-Za-z0-9_$]*)`/g)) {
|
|
60
|
+
if (match[1]) {
|
|
61
|
+
push(match[1]);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
for (const match of text.matchAll(/\b(function|class|interface|type|method|handler)\s+`?([A-Za-z_$][A-Za-z0-9_$]*)`?/gi)) {
|
|
65
|
+
const keyword = String(match[1] || '').toLowerCase();
|
|
66
|
+
const candidate = match[2];
|
|
67
|
+
if (!candidate)
|
|
68
|
+
continue;
|
|
69
|
+
const type = keyword === 'class'
|
|
70
|
+
? 'class'
|
|
71
|
+
: keyword === 'interface'
|
|
72
|
+
? 'interface'
|
|
73
|
+
: keyword === 'type'
|
|
74
|
+
? 'type'
|
|
75
|
+
: keyword === 'method' || keyword === 'handler'
|
|
76
|
+
? 'method'
|
|
77
|
+
: 'function';
|
|
78
|
+
push(candidate, type);
|
|
79
|
+
}
|
|
80
|
+
return entries;
|
|
81
|
+
}
|
|
82
|
+
function parseExplicitSymbols(rawSymbols) {
|
|
83
|
+
if (!Array.isArray(rawSymbols))
|
|
84
|
+
return [];
|
|
85
|
+
const parsed = [];
|
|
86
|
+
for (const entry of rawSymbols) {
|
|
87
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry))
|
|
88
|
+
continue;
|
|
89
|
+
const symbol = entry;
|
|
90
|
+
const name = normalizeSymbolName(String(symbol.name || ''));
|
|
91
|
+
if (!name)
|
|
92
|
+
continue;
|
|
93
|
+
const action = normalizeSymbolAction(typeof symbol.action === 'string' ? symbol.action : undefined, 'MODIFY');
|
|
94
|
+
const type = normalizeSymbolType(typeof symbol.type === 'string' ? symbol.type : undefined);
|
|
95
|
+
const file = typeof symbol.file === 'string' && symbol.file.trim()
|
|
96
|
+
? normalizeRepoPath(symbol.file)
|
|
97
|
+
: undefined;
|
|
98
|
+
const reason = typeof symbol.reason === 'string' && symbol.reason.trim()
|
|
99
|
+
? symbol.reason.trim().slice(0, 240)
|
|
100
|
+
: undefined;
|
|
101
|
+
parsed.push({
|
|
102
|
+
name,
|
|
103
|
+
action,
|
|
104
|
+
...(type ? { type } : {}),
|
|
105
|
+
...(file ? { file } : {}),
|
|
106
|
+
...(reason ? { reason } : {}),
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
return parsed;
|
|
110
|
+
}
|
|
111
|
+
function mergeExpectedSymbols(items) {
|
|
112
|
+
const precedence = {
|
|
113
|
+
MODIFY: 1,
|
|
114
|
+
CREATE: 2,
|
|
115
|
+
BLOCK: 3,
|
|
116
|
+
};
|
|
117
|
+
const merged = new Map();
|
|
118
|
+
for (const item of items) {
|
|
119
|
+
const name = normalizeSymbolName(item.name);
|
|
120
|
+
if (!name)
|
|
121
|
+
continue;
|
|
122
|
+
const type = normalizeSymbolType(item.type);
|
|
123
|
+
const file = item.file ? normalizeRepoPath(item.file) : undefined;
|
|
124
|
+
const key = `${file || '*'}::${type || 'unknown'}::${name}`;
|
|
125
|
+
const reason = item.reason ? item.reason.trim().slice(0, 240) : undefined;
|
|
126
|
+
const candidate = {
|
|
127
|
+
name,
|
|
128
|
+
action: item.action,
|
|
129
|
+
...(type ? { type } : {}),
|
|
130
|
+
...(file ? { file } : {}),
|
|
131
|
+
...(reason ? { reason } : {}),
|
|
132
|
+
};
|
|
133
|
+
const existing = merged.get(key);
|
|
134
|
+
if (!existing) {
|
|
135
|
+
merged.set(key, candidate);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (precedence[candidate.action] > precedence[existing.action]) {
|
|
139
|
+
merged.set(key, candidate);
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (!existing.reason && candidate.reason) {
|
|
143
|
+
merged.set(key, {
|
|
144
|
+
...existing,
|
|
145
|
+
reason: candidate.reason,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return [...merged.values()].sort((a, b) => {
|
|
150
|
+
const fileA = a.file || '';
|
|
151
|
+
const fileB = b.file || '';
|
|
152
|
+
if (fileA !== fileB)
|
|
153
|
+
return fileA.localeCompare(fileB);
|
|
154
|
+
if (a.name !== b.name)
|
|
155
|
+
return a.name.localeCompare(b.name);
|
|
156
|
+
return (a.type || '').localeCompare(b.type || '');
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
function mapPlanSymbolsForChangeContract(plan) {
|
|
160
|
+
const result = [];
|
|
161
|
+
const files = Array.isArray(plan.files) ? plan.files : [];
|
|
162
|
+
result.push(...parseExplicitSymbols(plan.symbols));
|
|
163
|
+
for (const file of files) {
|
|
164
|
+
const filePath = normalizeRepoPath(String(file.path || ''));
|
|
165
|
+
if (!filePath)
|
|
166
|
+
continue;
|
|
167
|
+
const action = normalizeSymbolAction(file.action, 'MODIFY');
|
|
168
|
+
const textSources = [file.reason, file.suggestion, file.rationale];
|
|
169
|
+
for (const source of textSources) {
|
|
170
|
+
if (!source || !source.trim())
|
|
171
|
+
continue;
|
|
172
|
+
for (const symbol of extractSymbolMentionsFromText(source)) {
|
|
173
|
+
result.push({
|
|
174
|
+
name: symbol.name,
|
|
175
|
+
action,
|
|
176
|
+
file: filePath,
|
|
177
|
+
...(symbol.type ? { type: symbol.type } : {}),
|
|
178
|
+
reason: `Derived from plan note for ${filePath}`,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (typeof plan.summary === 'string' && plan.summary.trim()) {
|
|
184
|
+
for (const symbol of extractSymbolMentionsFromText(plan.summary)) {
|
|
185
|
+
result.push({
|
|
186
|
+
name: symbol.name,
|
|
187
|
+
action: 'MODIFY',
|
|
188
|
+
...(symbol.type ? { type: symbol.type } : {}),
|
|
189
|
+
reason: 'Derived from plan summary',
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (Array.isArray(plan.recommendations)) {
|
|
194
|
+
for (const recommendation of plan.recommendations) {
|
|
195
|
+
if (!recommendation || !recommendation.trim())
|
|
196
|
+
continue;
|
|
197
|
+
for (const symbol of extractSymbolMentionsFromText(recommendation)) {
|
|
198
|
+
result.push({
|
|
199
|
+
name: symbol.name,
|
|
200
|
+
action: 'MODIFY',
|
|
201
|
+
...(symbol.type ? { type: symbol.type } : {}),
|
|
202
|
+
reason: 'Derived from plan recommendations',
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return mergeExpectedSymbols(result);
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=plan-symbols.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neurcode-ai/cli",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.45",
|
|
4
4
|
"description": "Neurcode CLI - AI code governance and diff analysis",
|
|
5
5
|
"bin": {
|
|
6
6
|
"neurcode": "dist/index.js"
|
|
@@ -11,6 +11,10 @@
|
|
|
11
11
|
"dist",
|
|
12
12
|
"README.md"
|
|
13
13
|
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"start": "node dist/index.js",
|
|
16
|
+
"help": "node dist/index.js --help"
|
|
17
|
+
},
|
|
14
18
|
"keywords": [
|
|
15
19
|
"cli",
|
|
16
20
|
"code-governance",
|
|
@@ -54,17 +58,5 @@
|
|
|
54
58
|
},
|
|
55
59
|
"publishConfig": {
|
|
56
60
|
"access": "public"
|
|
57
|
-
},
|
|
58
|
-
"devDependencies": {
|
|
59
|
-
"@types/fs-extra": "^11.0.4",
|
|
60
|
-
"@types/glob": "^8.1.0",
|
|
61
|
-
"@types/node": "^20.10.0",
|
|
62
|
-
"@types/uuid": "^9.0.8",
|
|
63
|
-
"typescript": "^5.3.0"
|
|
64
|
-
},
|
|
65
|
-
"scripts": {
|
|
66
|
-
"build": "rm -rf dist && tsc && chmod +x dist/index.js",
|
|
67
|
-
"dev": "tsc --watch",
|
|
68
|
-
"start": "node dist/index.js"
|
|
69
61
|
}
|
|
70
|
-
}
|
|
62
|
+
}
|