sigmap 6.10.9 → 6.10.10
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/AGENTS.md +75 -109
- package/CHANGELOG.md +19 -0
- package/gen-context.js +298 -85
- package/package.json +3 -3
- package/packages/cli/package.json +1 -1
- package/packages/core/package.json +1 -1
- package/src/discovery/r-manifest.js +176 -0
- package/src/extractors/deps.js +30 -1
- package/src/extractors/r.js +182 -45
- package/src/graph/builder.js +133 -24
- package/src/graph/impact.js +6 -1
- package/src/mcp/server.js +1 -1
- package/src/retrieval/ranker.js +21 -2
package/gen-context.js
CHANGED
|
@@ -2761,7 +2761,27 @@ __factories["./src/extractors/deps"] = function(module, exports) {
|
|
|
2761
2761
|
return reverse;
|
|
2762
2762
|
}
|
|
2763
2763
|
|
|
2764
|
-
|
|
2764
|
+
const R_BASE_PKGS = new Set([
|
|
2765
|
+
'base', 'stats', 'utils', 'graphics', 'grDevices', 'methods', 'datasets',
|
|
2766
|
+
'parallel', 'splines', 'stats4', 'tools', 'tcltk', 'grid', 'compiler',
|
|
2767
|
+
]);
|
|
2768
|
+
|
|
2769
|
+
function extractRDeps(src) {
|
|
2770
|
+
const deps = new Set();
|
|
2771
|
+
const stripped = (src || '').replace(/#.*$/gm, '');
|
|
2772
|
+
for (const m of stripped.matchAll(/\b(?:library|require)\s*\(\s*["']?([\w.]+)["']?\s*\)/g)) {
|
|
2773
|
+
if (m[1] && !R_BASE_PKGS.has(m[1])) deps.add(m[1]);
|
|
2774
|
+
}
|
|
2775
|
+
for (const m of stripped.matchAll(/\brequireNamespace\s*\(\s*["']([\w.]+)["']/g)) {
|
|
2776
|
+
if (m[1] && !R_BASE_PKGS.has(m[1])) deps.add(m[1]);
|
|
2777
|
+
}
|
|
2778
|
+
for (const m of stripped.matchAll(/\b([A-Za-z][\w.]*)::[A-Za-z]/g)) {
|
|
2779
|
+
if (m[1] && !R_BASE_PKGS.has(m[1])) deps.add(m[1]);
|
|
2780
|
+
}
|
|
2781
|
+
return [...deps].slice(0, 5);
|
|
2782
|
+
}
|
|
2783
|
+
|
|
2784
|
+
module.exports = { extractPythonDeps, extractTSDeps, extractRDeps, buildReverseDepMap };
|
|
2765
2785
|
|
|
2766
2786
|
};
|
|
2767
2787
|
|
|
@@ -3063,67 +3083,169 @@ __factories["./src/extractors/r"] = function(module, exports) {
|
|
|
3063
3083
|
|
|
3064
3084
|
'use strict';
|
|
3065
3085
|
|
|
3066
|
-
/**
|
|
3067
|
-
* Extract signatures from R source code.
|
|
3068
|
-
* @param {string} src - Raw file content
|
|
3069
|
-
* @returns {string[]} Array of signature strings
|
|
3070
|
-
*/
|
|
3071
3086
|
function extract(src) {
|
|
3072
3087
|
if (!src || typeof src !== 'string') return [];
|
|
3073
3088
|
const sigs = [];
|
|
3074
|
-
|
|
3075
|
-
// Strip line comments. R uses # comments. Roxygen2 (#') comments are
|
|
3076
|
-
// stripped along with regular ones; Phase 2 may parse them.
|
|
3089
|
+
const docHints = collectRoxygenHints(src);
|
|
3077
3090
|
const stripped = src.replace(/#.*$/gm, '');
|
|
3091
|
+
const consumedRanges = [];
|
|
3078
3092
|
|
|
3079
|
-
|
|
3080
|
-
// name <- function(args) { ... }
|
|
3081
|
-
// name = function(args) { ... }
|
|
3082
|
-
// name <<- function(args) { ... }
|
|
3083
|
-
// Args may span multiple lines and contain default values, so we need to
|
|
3084
|
-
// match a balanced parenthesis group rather than a single line.
|
|
3085
|
-
const funcRe = /^(?:[ \t]*)([\w.]+)\s*(?:<<-|<-|=)\s*function\s*\(/gm;
|
|
3093
|
+
const r6Re = /([\w.]+)\s*(?:<<-|<-|=)\s*(?:R6::)?R6Class\s*\(/g;
|
|
3086
3094
|
let m;
|
|
3087
|
-
while ((m =
|
|
3095
|
+
while ((m = r6Re.exec(stripped)) !== null && sigs.length < 30) {
|
|
3096
|
+
const name = m[1];
|
|
3097
|
+
if (name.startsWith('.')) continue;
|
|
3098
|
+
const openIdx = r6Re.lastIndex - 1;
|
|
3099
|
+
const body = readBalancedParens(stripped, openIdx);
|
|
3100
|
+
if (body === null) continue;
|
|
3101
|
+
const closeIdx = openIdx + body.length + 1;
|
|
3102
|
+
const classNameLit = readFirstStringArg(body) || name;
|
|
3103
|
+
sigs.push(`${name} <- R6Class("${classNameLit}")` + applyHint(docHints, name));
|
|
3104
|
+
for (const memberSig of extractListMethods(body, 8)) {
|
|
3105
|
+
sigs.push(' ' + memberSig);
|
|
3106
|
+
if (sigs.length >= 30) break;
|
|
3107
|
+
}
|
|
3108
|
+
consumedRanges.push([m.index, closeIdx]);
|
|
3109
|
+
r6Re.lastIndex = closeIdx;
|
|
3110
|
+
}
|
|
3111
|
+
|
|
3112
|
+
const s7Classes = new Set();
|
|
3113
|
+
const s7Re = /([\w.]+)\s*(?:<<-|<-|=)\s*(?:S7::)?new_class\s*\(/g;
|
|
3114
|
+
while ((m = s7Re.exec(stripped)) !== null && sigs.length < 30) {
|
|
3115
|
+
const name = m[1];
|
|
3116
|
+
if (name.startsWith('.')) continue;
|
|
3117
|
+
const openIdx = s7Re.lastIndex - 1;
|
|
3118
|
+
const body = readBalancedParens(stripped, openIdx);
|
|
3119
|
+
if (body === null) continue;
|
|
3120
|
+
const closeIdx = openIdx + body.length + 1;
|
|
3121
|
+
const classNameLit = readFirstStringArg(body) || name;
|
|
3122
|
+
s7Classes.add(classNameLit);
|
|
3123
|
+
s7Classes.add(name);
|
|
3124
|
+
sigs.push(`${name} <- new_class("${classNameLit}")` + applyHint(docHints, name));
|
|
3125
|
+
consumedRanges.push([m.index, closeIdx]);
|
|
3126
|
+
s7Re.lastIndex = closeIdx;
|
|
3127
|
+
}
|
|
3128
|
+
|
|
3129
|
+
const s7MethodRe = /^[ \t]*method\s*\(\s*([\w.]+)\s*,\s*([\w.]+)\s*\)\s*(?:<<-|<-|=)\s*function\s*\(/gm;
|
|
3130
|
+
while ((m = s7MethodRe.exec(stripped)) !== null && sigs.length < 30) {
|
|
3131
|
+
if (!s7Classes.has(m[2])) continue;
|
|
3132
|
+
const argsStart = s7MethodRe.lastIndex - 1;
|
|
3133
|
+
const args = readBalancedParens(stripped, argsStart);
|
|
3134
|
+
if (args === null) continue;
|
|
3135
|
+
sigs.push(` method(${m[1]}, ${m[2]}) <- function(${normalizeParams(args)})`);
|
|
3136
|
+
}
|
|
3137
|
+
|
|
3138
|
+
const funcRe = /^(?:[ \t]*)([\w.]+)\s*(?:<<-|<-|=)\s*function\s*\(/gm;
|
|
3139
|
+
while ((m = funcRe.exec(stripped)) !== null && sigs.length < 30) {
|
|
3088
3140
|
const name = m[1];
|
|
3089
|
-
if (name.startsWith('.')) continue;
|
|
3090
|
-
|
|
3091
|
-
const
|
|
3141
|
+
if (name.startsWith('.')) continue;
|
|
3142
|
+
if (inAnyRange(m.index, consumedRanges)) continue;
|
|
3143
|
+
const argsStart = funcRe.lastIndex - 1;
|
|
3144
|
+
const args = readBalancedParens(stripped, argsStart);
|
|
3092
3145
|
if (args === null) continue;
|
|
3093
|
-
sigs.push(`${name} <- function(${normalizeParams(args)})`);
|
|
3146
|
+
sigs.push(`${name} <- function(${normalizeParams(args)})` + applyHint(docHints, name));
|
|
3094
3147
|
}
|
|
3095
3148
|
|
|
3096
|
-
// S4 setMethod / setGeneric:
|
|
3097
|
-
// setGeneric("name", function(args) standardGeneric("name"))
|
|
3098
|
-
// setMethod("name", "ClassName", function(args) { ... })
|
|
3099
3149
|
for (const sm of stripped.matchAll(/^[ \t]*setGeneric\s*\(\s*["']([\w.]+)["']/gm)) {
|
|
3150
|
+
if (sigs.length >= 30) break;
|
|
3100
3151
|
sigs.push(`setGeneric("${sm[1]}")`);
|
|
3101
3152
|
}
|
|
3102
3153
|
for (const sm of stripped.matchAll(/^[ \t]*setMethod\s*\(\s*["']([\w.]+)["']\s*,\s*["']([\w.]+)["']/gm)) {
|
|
3154
|
+
if (sigs.length >= 30) break;
|
|
3103
3155
|
sigs.push(`setMethod("${sm[1]}", "${sm[2]}")`);
|
|
3104
3156
|
}
|
|
3105
|
-
|
|
3106
|
-
// S4 class definitions:
|
|
3107
|
-
// setClass("Name", representation(...), ...)
|
|
3108
3157
|
for (const sm of stripped.matchAll(/^[ \t]*setClass\s*\(\s*["']([\w.]+)["']/gm)) {
|
|
3158
|
+
if (sigs.length >= 30) break;
|
|
3109
3159
|
sigs.push(`setClass("${sm[1]}")`);
|
|
3110
3160
|
}
|
|
3111
3161
|
|
|
3112
3162
|
return sigs.slice(0, 30);
|
|
3113
3163
|
}
|
|
3114
3164
|
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3165
|
+
function collectRoxygenHints(src) {
|
|
3166
|
+
const hints = new Map();
|
|
3167
|
+
const lines = src.split('\n');
|
|
3168
|
+
let block = [];
|
|
3169
|
+
for (let i = 0; i < lines.length; i++) {
|
|
3170
|
+
const line = lines[i];
|
|
3171
|
+
if (/^\s*#'/.test(line)) {
|
|
3172
|
+
block.push(line.replace(/^\s*#'\s?/, ''));
|
|
3173
|
+
continue;
|
|
3174
|
+
}
|
|
3175
|
+
if (block.length > 0) {
|
|
3176
|
+
const mm = line.match(/^[ \t]*([\w.]+)\s*(?:<<-|<-|=)\s*(?:R6::)?R6Class\b/)
|
|
3177
|
+
|| line.match(/^[ \t]*([\w.]+)\s*(?:<<-|<-|=)\s*(?:S7::)?new_class\b/)
|
|
3178
|
+
|| line.match(/^[ \t]*([\w.]+)\s*(?:<<-|<-|=)\s*function\b/);
|
|
3179
|
+
if (mm) {
|
|
3180
|
+
const name = mm[1];
|
|
3181
|
+
let hint = pickRoxygenLine(block, '@title')
|
|
3182
|
+
|| pickRoxygenLine(block, '@description')
|
|
3183
|
+
|| pickRoxygenLine(block, null);
|
|
3184
|
+
if (hint) {
|
|
3185
|
+
hint = hint.replace(/\s+/g, ' ').trim().slice(0, 60).replace(/[.,;:!?]+$/, '').trim();
|
|
3186
|
+
if (hint) hints.set(name, hint);
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3189
|
+
block = [];
|
|
3190
|
+
}
|
|
3191
|
+
}
|
|
3192
|
+
return hints;
|
|
3193
|
+
}
|
|
3194
|
+
|
|
3195
|
+
function pickRoxygenLine(block, tag) {
|
|
3196
|
+
for (const raw of block) {
|
|
3197
|
+
const b = raw.trim();
|
|
3198
|
+
if (!b) continue;
|
|
3199
|
+
if (tag) {
|
|
3200
|
+
if (b.startsWith(tag)) {
|
|
3201
|
+
const rest = b.slice(tag.length).trim();
|
|
3202
|
+
if (rest) return rest;
|
|
3203
|
+
}
|
|
3204
|
+
} else if (!b.startsWith('@')) {
|
|
3205
|
+
return b;
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
3208
|
+
return null;
|
|
3209
|
+
}
|
|
3210
|
+
|
|
3211
|
+
function applyHint(hints, name) {
|
|
3212
|
+
const h = hints.get(name);
|
|
3213
|
+
return h ? ` # ${h}` : '';
|
|
3214
|
+
}
|
|
3215
|
+
|
|
3216
|
+
function extractListMethods(body, cap) {
|
|
3217
|
+
const out = [];
|
|
3218
|
+
const re = /(?:^|[\n,])\s*([\w.]+)\s*=\s*function\s*\(/g;
|
|
3219
|
+
let m;
|
|
3220
|
+
while ((m = re.exec(body)) !== null && out.length < cap) {
|
|
3221
|
+
const name = m[1];
|
|
3222
|
+
if (name.startsWith('.')) continue;
|
|
3223
|
+
const argsStart = re.lastIndex - 1;
|
|
3224
|
+
const args = readBalancedParens(body, argsStart);
|
|
3225
|
+
if (args === null) continue;
|
|
3226
|
+
out.push(`${name} <- function(${normalizeParams(args)})`);
|
|
3227
|
+
}
|
|
3228
|
+
return out;
|
|
3229
|
+
}
|
|
3230
|
+
|
|
3231
|
+
function inAnyRange(pos, ranges) {
|
|
3232
|
+
for (const [s, e] of ranges) {
|
|
3233
|
+
if (pos >= s && pos < e) return true;
|
|
3234
|
+
}
|
|
3235
|
+
return false;
|
|
3236
|
+
}
|
|
3237
|
+
|
|
3238
|
+
function readFirstStringArg(body) {
|
|
3239
|
+
const m = body.match(/^\s*["']([\w.]+)["']/);
|
|
3240
|
+
return m ? m[1] : null;
|
|
3241
|
+
}
|
|
3242
|
+
|
|
3243
|
+
function readBalancedParens(src, openIdx, cap = 16384) {
|
|
3122
3244
|
if (src[openIdx] !== '(') return null;
|
|
3123
3245
|
let depth = 1;
|
|
3124
3246
|
let i = openIdx + 1;
|
|
3125
3247
|
const end = Math.min(src.length, openIdx + cap);
|
|
3126
|
-
let inString = null;
|
|
3248
|
+
let inString = null;
|
|
3127
3249
|
while (i < end) {
|
|
3128
3250
|
const ch = src[i];
|
|
3129
3251
|
if (inString) {
|
|
@@ -3143,38 +3265,14 @@ __factories["./src/extractors/r"] = function(module, exports) {
|
|
|
3143
3265
|
return null;
|
|
3144
3266
|
}
|
|
3145
3267
|
|
|
3146
|
-
/**
|
|
3147
|
-
* Compress whitespace inside a parameter list, collapse multi-line default
|
|
3148
|
-
* expressions onto a single line, and trim. The goal is one-line readable
|
|
3149
|
-
* signatures, not a faithful AST.
|
|
3150
|
-
*
|
|
3151
|
-
* String literals are protected so that commas/equals inside default values
|
|
3152
|
-
* like sep = "," don't get respaced.
|
|
3153
|
-
*/
|
|
3154
3268
|
function normalizeParams(raw) {
|
|
3155
|
-
|
|
3156
|
-
let buf = '';
|
|
3269
|
+
let out = '';
|
|
3157
3270
|
let inString = null;
|
|
3158
3271
|
for (let i = 0; i < raw.length; i++) {
|
|
3159
3272
|
const ch = raw[i];
|
|
3160
|
-
if (inString) {
|
|
3161
|
-
buf += ch;
|
|
3162
|
-
if (ch === '\\' && i + 1 < raw.length) { buf += raw[i + 1]; i++; continue; }
|
|
3163
|
-
if (ch === inString) inString = null;
|
|
3164
|
-
continue;
|
|
3165
|
-
}
|
|
3166
|
-
if (ch === '"' || ch === "'") { inString = ch; buf += ch; continue; }
|
|
3167
|
-
buf += ch;
|
|
3168
|
-
}
|
|
3169
|
-
// Now buf === raw with strings preserved character-for-character.
|
|
3170
|
-
// Walk again: collapse non-string runs of whitespace, normalize ', ' and ' = '.
|
|
3171
|
-
let out = '';
|
|
3172
|
-
inString = null;
|
|
3173
|
-
for (let i = 0; i < buf.length; i++) {
|
|
3174
|
-
const ch = buf[i];
|
|
3175
3273
|
if (inString) {
|
|
3176
3274
|
out += ch;
|
|
3177
|
-
if (ch === '\\' && i + 1 <
|
|
3275
|
+
if (ch === '\\' && i + 1 < raw.length) { out += raw[i + 1]; i++; continue; }
|
|
3178
3276
|
if (ch === inString) inString = null;
|
|
3179
3277
|
continue;
|
|
3180
3278
|
}
|
|
@@ -4916,13 +5014,33 @@ __factories["./src/graph/builder"] = function(module, exports) {
|
|
|
4916
5014
|
const RS_EXTS = new Set(['.rs']);
|
|
4917
5015
|
const JVM_EXTS = new Set(['.java', '.kt', '.kts', '.scala', '.sc']);
|
|
4918
5016
|
const RB_EXTS = new Set(['.rb', '.rake']);
|
|
5017
|
+
const R_EXTS = new Set(['.r', '.R']);
|
|
4919
5018
|
function resolveJsPath(dir, importStr, fileSet) {
|
|
4920
5019
|
const base = path.resolve(dir, importStr);
|
|
4921
5020
|
const candidates = [base, base+'.ts', base+'.tsx', base+'.js', base+'.jsx', base+'.mjs', base+'.cjs', path.join(base,'index.ts'), path.join(base,'index.js')];
|
|
4922
5021
|
for (const c of candidates) { if (fileSet.has(c)) return c; }
|
|
4923
5022
|
return null;
|
|
4924
5023
|
}
|
|
4925
|
-
function
|
|
5024
|
+
function normalizePath(p) {
|
|
5025
|
+
return path.normalize(p).toLowerCase();
|
|
5026
|
+
}
|
|
5027
|
+
function resolveRPath(dir, importStr, fileSet, cwd) {
|
|
5028
|
+
const tried = new Set();
|
|
5029
|
+
const bases = [path.resolve(dir, importStr)];
|
|
5030
|
+
if (cwd) bases.push(path.resolve(cwd, importStr));
|
|
5031
|
+
for (const base of bases) {
|
|
5032
|
+
for (const c of [base, base + '.R', base + '.r']) {
|
|
5033
|
+
const normC = normalizePath(c);
|
|
5034
|
+
if (tried.has(normC)) continue;
|
|
5035
|
+
tried.add(normC);
|
|
5036
|
+
if (fileSet.has(c)) return c;
|
|
5037
|
+
if (fileSet.has(normC)) return normC;
|
|
5038
|
+
}
|
|
5039
|
+
}
|
|
5040
|
+
return null;
|
|
5041
|
+
}
|
|
5042
|
+
function escapeRegex(s) { return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); }
|
|
5043
|
+
function extractFileDeps(filePath, content, fileSet, cwd, ctx) {
|
|
4926
5044
|
const ext = path.extname(filePath).toLowerCase();
|
|
4927
5045
|
const dir = path.dirname(filePath);
|
|
4928
5046
|
const found = [];
|
|
@@ -4941,7 +5059,13 @@ __factories["./src/graph/builder"] = function(module, exports) {
|
|
|
4941
5059
|
const modPart = m[1].slice(dotCount).replace(/\./g,'/');
|
|
4942
5060
|
let base = dir; for (let i=1;i<dotCount;i++) base=path.dirname(base);
|
|
4943
5061
|
const candidate = modPart ? path.join(base,modPart+'.py') : null;
|
|
4944
|
-
if (candidate
|
|
5062
|
+
if (candidate) { const normC = normalizePath(candidate); if (fileSet.has(normC)) found.push(normC); }
|
|
5063
|
+
}
|
|
5064
|
+
const reAbs = /^[ \t]*from\s+([\w.]+)\s+import/gm;
|
|
5065
|
+
while ((m = reAbs.exec(content)) !== null) {
|
|
5066
|
+
const modulePath = m[1].replace(/\./g,'/');
|
|
5067
|
+
const candidates = [path.join(dir,modulePath+'.py'),path.join(dir,modulePath,'__init__.py'),path.resolve(dir,'..',modulePath+'.py'),path.resolve(dir,'..',modulePath,'__init__.py')];
|
|
5068
|
+
for (const c of candidates) { const normC = normalizePath(c); if (fileSet.has(normC)) { found.push(normC); break; } }
|
|
4945
5069
|
}
|
|
4946
5070
|
}
|
|
4947
5071
|
if (GO_EXTS.has(ext)) {
|
|
@@ -4952,14 +5076,14 @@ __factories["./src/graph/builder"] = function(module, exports) {
|
|
|
4952
5076
|
while ((m = reInline.exec(content)) !== null) imports.push(m[1]);
|
|
4953
5077
|
for (const imp of imports) {
|
|
4954
5078
|
const suffix = imp.split('/').pop();
|
|
4955
|
-
for (const f of fileSet) { if (
|
|
5079
|
+
for (const f of fileSet) { const normF = normalizePath(f); if (normF.endsWith(path.sep+suffix+'.go')||normF.includes(path.sep+suffix+path.sep)) { found.push(normF); break; } }
|
|
4956
5080
|
}
|
|
4957
5081
|
}
|
|
4958
5082
|
if (RS_EXTS.has(ext)) {
|
|
4959
5083
|
const reMod = /^\s*(?:pub\s+)?mod\s+(\w+)\s*;/gm; let m;
|
|
4960
5084
|
while ((m = reMod.exec(content)) !== null) {
|
|
4961
|
-
const c1 = path.join(dir,m[1]+'.rs'); if (fileSet.has(
|
|
4962
|
-
const c2 = path.join(dir,m[1],'mod.rs'); if (fileSet.has(
|
|
5085
|
+
const c1 = path.join(dir,m[1]+'.rs'); const normC1 = normalizePath(c1); if (fileSet.has(normC1)) found.push(normC1);
|
|
5086
|
+
const c2 = path.join(dir,m[1],'mod.rs'); const normC2 = normalizePath(c2); if (fileSet.has(normC2)) found.push(normC2);
|
|
4963
5087
|
}
|
|
4964
5088
|
}
|
|
4965
5089
|
if (JVM_EXTS.has(ext)) {
|
|
@@ -4967,7 +5091,7 @@ __factories["./src/graph/builder"] = function(module, exports) {
|
|
|
4967
5091
|
while ((m = re.exec(content)) !== null) {
|
|
4968
5092
|
const asPath = m[1].replace(/\./g,path.sep);
|
|
4969
5093
|
for (const jvmExt of ['.java','.kt','.kts','.scala','.sc']) {
|
|
4970
|
-
for (const f of fileSet) { if (
|
|
5094
|
+
for (const f of fileSet) { const normF = normalizePath(f); if (normF.endsWith(normalizePath(asPath+jvmExt))) { found.push(normF); break; } }
|
|
4971
5095
|
}
|
|
4972
5096
|
}
|
|
4973
5097
|
}
|
|
@@ -4976,27 +5100,90 @@ __factories["./src/graph/builder"] = function(module, exports) {
|
|
|
4976
5100
|
while ((m = re.exec(content)) !== null) {
|
|
4977
5101
|
const base = path.resolve(dir,m[1]);
|
|
4978
5102
|
const candidate = base.endsWith('.rb') ? base : base+'.rb';
|
|
4979
|
-
|
|
5103
|
+
const normC = normalizePath(candidate);
|
|
5104
|
+
if (fileSet.has(normC)) found.push(normC);
|
|
5105
|
+
}
|
|
5106
|
+
}
|
|
5107
|
+
if (R_EXTS.has(ext)) {
|
|
5108
|
+
const stripped = content.replace(/#.*$/gm, '');
|
|
5109
|
+
const reSrc = /(?:^|[^\w.])source\s*\(\s*["']([^"']+)["']/g; let m;
|
|
5110
|
+
while ((m = reSrc.exec(stripped)) !== null) {
|
|
5111
|
+
const r = resolveRPath(dir, m[1], fileSet, cwd); if (r) found.push(r);
|
|
5112
|
+
}
|
|
5113
|
+
if (ctx && ctx.rPackage && ctx.rLocalDefs && ctx.rLocalDefs.size > 0) {
|
|
5114
|
+
const reNs = new RegExp('\\b' + escapeRegex(ctx.rPackage) + ':::?([A-Za-z][\\w.]*)', 'g');
|
|
5115
|
+
while ((m = reNs.exec(stripped)) !== null) {
|
|
5116
|
+
const target = ctx.rLocalDefs.get(m[1]);
|
|
5117
|
+
if (!target) continue;
|
|
5118
|
+
const normTarget = normalizePath(target);
|
|
5119
|
+
const normFilePath = normalizePath(filePath);
|
|
5120
|
+
if (normTarget === normFilePath) continue;
|
|
5121
|
+
if (fileSet.has(target)) {
|
|
5122
|
+
found.push(target);
|
|
5123
|
+
} else if (fileSet.has(normTarget)) {
|
|
5124
|
+
found.push(normTarget);
|
|
5125
|
+
}
|
|
5126
|
+
}
|
|
4980
5127
|
}
|
|
4981
5128
|
}
|
|
4982
5129
|
return [...new Set(found)];
|
|
4983
5130
|
}
|
|
4984
|
-
function build(files, cwd) {
|
|
5131
|
+
function build(files, cwd, ctx) {
|
|
4985
5132
|
const fileSet = new Set(files.map((f) => path.resolve(f)));
|
|
4986
|
-
const
|
|
4987
|
-
|
|
5133
|
+
const fileSetNormalized = new Set([...fileSet].map(normalizePath));
|
|
5134
|
+
const forward = new Map();
|
|
5135
|
+
const reverse = new Map();
|
|
5136
|
+
for (const f of fileSet) {
|
|
5137
|
+
const normF = normalizePath(f);
|
|
5138
|
+
if (!forward.has(normF)) forward.set(normF, []);
|
|
5139
|
+
if (!reverse.has(normF)) reverse.set(normF, []);
|
|
5140
|
+
}
|
|
4988
5141
|
for (const filePath of fileSet) {
|
|
4989
|
-
let content;
|
|
4990
|
-
|
|
5142
|
+
let content;
|
|
5143
|
+
try { content = fs.readFileSync(filePath,'utf8'); } catch(_) { continue; }
|
|
5144
|
+
const normFilePath = normalizePath(filePath);
|
|
5145
|
+
const deps = extractFileDeps(filePath, content, fileSetNormalized, cwd, ctx);
|
|
4991
5146
|
if (deps.length > 0) {
|
|
4992
|
-
forward.set(
|
|
4993
|
-
for (const dep of deps) {
|
|
5147
|
+
forward.set(normFilePath, deps);
|
|
5148
|
+
for (const dep of deps) {
|
|
5149
|
+
if (!reverse.has(dep)) reverse.set(dep, []);
|
|
5150
|
+
reverse.get(dep).push(normFilePath);
|
|
5151
|
+
}
|
|
4994
5152
|
}
|
|
4995
5153
|
}
|
|
4996
5154
|
return { forward, reverse };
|
|
4997
5155
|
}
|
|
5156
|
+
function _readDescriptionPackage(cwd) {
|
|
5157
|
+
try {
|
|
5158
|
+
const raw = fs.readFileSync(path.join(cwd, 'DESCRIPTION'), 'utf8');
|
|
5159
|
+
const m = raw.match(/^Package\s*:\s*(\S+)/m);
|
|
5160
|
+
return m ? m[1] : null;
|
|
5161
|
+
} catch (_) { return null; }
|
|
5162
|
+
}
|
|
5163
|
+
function _collectRLocalDefs(rFiles) {
|
|
5164
|
+
const defs = new Map();
|
|
5165
|
+
const reAssign = /^(?:[ \t]*)([\w.]+)\s*(?:<<-|<-|=)\s*(?:(?:R6::)?R6Class|(?:S7::)?new_class|function)\b/gm;
|
|
5166
|
+
const reS4Generic = /^[ \t]*setGeneric\s*\(\s*["']([\w.]+)["']/gm;
|
|
5167
|
+
const reS4Class = /^[ \t]*setClass\s*\(\s*["']([\w.]+)["']/gm;
|
|
5168
|
+
for (const filePath of rFiles) {
|
|
5169
|
+
let content; try { content = fs.readFileSync(filePath, 'utf8'); } catch (_) { continue; }
|
|
5170
|
+
const stripped = content.replace(/#.*$/gm, '');
|
|
5171
|
+
let m;
|
|
5172
|
+
while ((m = reAssign.exec(stripped)) !== null) {
|
|
5173
|
+
if (m[1].startsWith('.')) continue;
|
|
5174
|
+
if (!defs.has(m[1])) defs.set(m[1], filePath);
|
|
5175
|
+
}
|
|
5176
|
+
while ((m = reS4Generic.exec(stripped)) !== null) {
|
|
5177
|
+
if (!defs.has(m[1])) defs.set(m[1], filePath);
|
|
5178
|
+
}
|
|
5179
|
+
while ((m = reS4Class.exec(stripped)) !== null) {
|
|
5180
|
+
if (!defs.has(m[1])) defs.set(m[1], filePath);
|
|
5181
|
+
}
|
|
5182
|
+
}
|
|
5183
|
+
return defs;
|
|
5184
|
+
}
|
|
4998
5185
|
function buildFromCwd(cwd, opts) {
|
|
4999
|
-
const { srcDirs=['src','app','lib'], exclude=['node_modules','.git','dist','build'] } = opts||{};
|
|
5186
|
+
const { srcDirs=['src','app','lib','R','inst'], exclude=['node_modules','.git','dist','build'] } = opts||{};
|
|
5000
5187
|
const excludeSet = new Set(exclude);
|
|
5001
5188
|
function walkDir(dir,depth) {
|
|
5002
5189
|
if (depth>8) return [];
|
|
@@ -5008,19 +5195,25 @@ __factories["./src/graph/builder"] = function(module, exports) {
|
|
|
5008
5195
|
if (e.isDirectory()) out.push(...walkDir(full,depth+1));
|
|
5009
5196
|
else if (e.isFile()) {
|
|
5010
5197
|
const ext = path.extname(e.name).toLowerCase();
|
|
5011
|
-
if (JS_EXTS.has(ext)||PY_EXTS.has(ext)||GO_EXTS.has(ext)||RS_EXTS.has(ext)||JVM_EXTS.has(ext)||RB_EXTS.has(ext)) out.push(full);
|
|
5198
|
+
if (JS_EXTS.has(ext)||PY_EXTS.has(ext)||GO_EXTS.has(ext)||RS_EXTS.has(ext)||JVM_EXTS.has(ext)||RB_EXTS.has(ext)||R_EXTS.has(ext)) out.push(full);
|
|
5012
5199
|
}
|
|
5013
5200
|
}
|
|
5014
5201
|
return out;
|
|
5015
5202
|
}
|
|
5016
5203
|
const files=[];
|
|
5017
5204
|
for (const sd of srcDirs) { const absDir=path.resolve(cwd,sd); if (fs.existsSync(absDir)) files.push(...walkDir(absDir,0)); }
|
|
5018
|
-
for (const rootFile of ['gen-context.js','index.js','main.js','app.js']) {
|
|
5205
|
+
for (const rootFile of ['gen-context.js','index.js','main.js','app.js','app.R','server.R','ui.R','global.R']) {
|
|
5019
5206
|
const abs=path.resolve(cwd,rootFile); if (fs.existsSync(abs)) files.push(abs);
|
|
5020
5207
|
}
|
|
5021
|
-
|
|
5208
|
+
let ctx;
|
|
5209
|
+
const rPackage = _readDescriptionPackage(cwd);
|
|
5210
|
+
if (rPackage) {
|
|
5211
|
+
const rFiles = files.filter((f) => R_EXTS.has(path.extname(f).toLowerCase()));
|
|
5212
|
+
if (rFiles.length > 0) ctx = { rPackage, rLocalDefs: _collectRLocalDefs(rFiles) };
|
|
5213
|
+
}
|
|
5214
|
+
return build(files, cwd, ctx);
|
|
5022
5215
|
}
|
|
5023
|
-
module.exports = { build, buildFromCwd, extractFileDeps };
|
|
5216
|
+
module.exports = { build, buildFromCwd, extractFileDeps, normalizePath };
|
|
5024
5217
|
};
|
|
5025
5218
|
|
|
5026
5219
|
// ── ./src/graph/impact ──
|
|
@@ -5032,6 +5225,7 @@ __factories["./src/graph/impact"] = function(module, exports) {
|
|
|
5032
5225
|
const ROUTE_PATTERNS = [/router?\.[jt]sx?$/i,/routes?\.[jt]sx?$/i,/controller\.[jt]sx?$/i,/views?\.[jt]sx?$/i,/handlers?\.[jt]sx?$/i];
|
|
5033
5226
|
function isTestFile(f) { return TEST_PATTERNS.some((re) => re.test(f.replace(/\\/g,'/'))); }
|
|
5034
5227
|
function isRouteFile(f) { return ROUTE_PATTERNS.some((re) => re.test(f.replace(/\\/g,'/'))); }
|
|
5228
|
+
function normalizePath(p) { return path.normalize(p).toLowerCase(); }
|
|
5035
5229
|
function bfs(startFile, reverseGraph, maxDepth) {
|
|
5036
5230
|
const direct=new Set(); const transitive=new Set(); const visited=new Set([startFile]);
|
|
5037
5231
|
const firstLevel = reverseGraph.get(startFile)||[];
|
|
@@ -5050,7 +5244,7 @@ __factories["./src/graph/impact"] = function(module, exports) {
|
|
|
5050
5244
|
}
|
|
5051
5245
|
function getImpact(changedFile, graph, opts) {
|
|
5052
5246
|
const {depth=0,cwd=process.cwd()} = opts||{};
|
|
5053
|
-
const absChanged = path.resolve(cwd,changedFile);
|
|
5247
|
+
const absChanged = normalizePath(path.resolve(cwd,changedFile));
|
|
5054
5248
|
if (!graph||!graph.reverse) return {changed:changedFile,direct:[],transitive:[],tests:[],routes:[],totalImpact:0};
|
|
5055
5249
|
const {direct,transitive} = bfs(absChanged,graph.reverse,depth);
|
|
5056
5250
|
const allImpacted=[...direct,...transitive];
|
|
@@ -5707,7 +5901,7 @@ __factories["./src/mcp/server"] = function(module, exports) {
|
|
|
5707
5901
|
|
|
5708
5902
|
const SERVER_INFO = {
|
|
5709
5903
|
name: 'sigmap',
|
|
5710
|
-
version: '6.10.
|
|
5904
|
+
version: '6.10.10',
|
|
5711
5905
|
description: 'SigMap MCP server — code signatures on demand',
|
|
5712
5906
|
};
|
|
5713
5907
|
|
|
@@ -6727,6 +6921,21 @@ __factories["./src/retrieval/ranker"] = function(module, exports) {
|
|
|
6727
6921
|
if (learnedWeights && score > 0) score *= learnedWeights[file] || 1.0;
|
|
6728
6922
|
scored.push({ file, score, sigs, tokens: Math.ceil(sigs.join('\n').length / 4) });
|
|
6729
6923
|
}
|
|
6924
|
+
// Compute confidence levels based on score distribution
|
|
6925
|
+
if (scored.length > 0) {
|
|
6926
|
+
const scores = scored.map(s => s.score);
|
|
6927
|
+
const maxScore = Math.max(...scores);
|
|
6928
|
+
const minScore = Math.min(...scores);
|
|
6929
|
+
const scoreRange = maxScore - minScore || 1;
|
|
6930
|
+
for (const entry of scored) {
|
|
6931
|
+
if (entry.score <= 0) {
|
|
6932
|
+
entry.confidence = 'low';
|
|
6933
|
+
} else {
|
|
6934
|
+
const normalized = (entry.score - minScore) / scoreRange;
|
|
6935
|
+
entry.confidence = normalized > 0.66 ? 'high' : normalized > 0.33 ? 'medium' : 'low';
|
|
6936
|
+
}
|
|
6937
|
+
}
|
|
6938
|
+
}
|
|
6730
6939
|
scored.sort((a, b) => b.score - a.score || a.file.localeCompare(b.file));
|
|
6731
6940
|
return scored.slice(0, topK);
|
|
6732
6941
|
}
|
|
@@ -6894,6 +7103,7 @@ __factories["./src/eval/analyzer"] = function(module, exports) {
|
|
|
6894
7103
|
'.swift': 'swift',
|
|
6895
7104
|
'.dart': 'dart',
|
|
6896
7105
|
'.scala': 'scala', '.sc': 'scala',
|
|
7106
|
+
'.r': 'r', '.R': 'r',
|
|
6897
7107
|
'.vue': 'vue_sfc',
|
|
6898
7108
|
'.svelte': 'svelte',
|
|
6899
7109
|
'.html': 'html', '.htm': 'html',
|
|
@@ -7934,7 +8144,7 @@ __factories["./src/discovery/language-detector"] = function(module, exports) {
|
|
|
7934
8144
|
if (e.isDirectory()) {
|
|
7935
8145
|
_walkDepth(path.join(dir, e.name), depth - 1, extCount);
|
|
7936
8146
|
} else if (e.isFile()) {
|
|
7937
|
-
const EXT_TO_LANG = {'.js': 'javascript', '.mjs': 'javascript', '.cjs': 'javascript', '.ts': 'typescript', '.tsx': 'typescript', '.jsx': 'javascript', '.py': 'python', '.rb': 'ruby', '.go': 'go', '.rs': 'rust', '.java': 'java', '.kt': 'kotlin', '.cs': 'csharp', '.cpp': 'cpp', '.c': 'cpp', '.h': 'cpp', '.hpp': 'cpp', '.swift': 'swift', '.dart': 'dart', '.scala': 'scala', '.php': 'php'};
|
|
8147
|
+
const EXT_TO_LANG = {'.js': 'javascript', '.mjs': 'javascript', '.cjs': 'javascript', '.ts': 'typescript', '.tsx': 'typescript', '.jsx': 'javascript', '.py': 'python', '.rb': 'ruby', '.go': 'go', '.rs': 'rust', '.java': 'java', '.kt': 'kotlin', '.cs': 'csharp', '.cpp': 'cpp', '.c': 'cpp', '.h': 'cpp', '.hpp': 'cpp', '.swift': 'swift', '.dart': 'dart', '.scala': 'scala', '.php': 'php', '.r': 'r', '.R': 'r'};
|
|
7938
8148
|
const ext = path.extname(e.name).toLowerCase();
|
|
7939
8149
|
if (EXT_TO_LANG[ext]) extCount[ext] = (extCount[ext] || 0) + 1;
|
|
7940
8150
|
}
|
|
@@ -8334,7 +8544,7 @@ const path = require('path');
|
|
|
8334
8544
|
const os = require('os');
|
|
8335
8545
|
const { execSync } = require('child_process');
|
|
8336
8546
|
|
|
8337
|
-
const VERSION = '6.10.
|
|
8547
|
+
const VERSION = '6.10.10';
|
|
8338
8548
|
const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
|
|
8339
8549
|
|
|
8340
8550
|
function requireSourceOrBundled(key) {
|
|
@@ -8370,6 +8580,7 @@ const EXT_MAP = {
|
|
|
8370
8580
|
'.swift': 'swift',
|
|
8371
8581
|
'.dart': 'dart',
|
|
8372
8582
|
'.scala': 'scala', '.sc': 'scala',
|
|
8583
|
+
'.r': 'r', '.R': 'r',
|
|
8373
8584
|
'.vue': 'vue_sfc',
|
|
8374
8585
|
'.svelte': 'svelte',
|
|
8375
8586
|
'.html': 'html', '.htm': 'html',
|
|
@@ -8509,10 +8720,11 @@ function detectAndExtract(filePath, content, maxSigsPerFile) {
|
|
|
8509
8720
|
function extractFileDeps(filePath, content, config) {
|
|
8510
8721
|
if (config && config.depMap === false) return [];
|
|
8511
8722
|
try {
|
|
8512
|
-
const { extractPythonDeps, extractTSDeps } = requireSourceOrBundled('./src/extractors/deps');
|
|
8723
|
+
const { extractPythonDeps, extractTSDeps, extractRDeps } = requireSourceOrBundled('./src/extractors/deps');
|
|
8513
8724
|
const ext = path.extname(filePath).toLowerCase();
|
|
8514
8725
|
if (ext === '.py' || ext === '.pyw') return extractPythonDeps(content);
|
|
8515
8726
|
if (['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'].includes(ext)) return extractTSDeps(content);
|
|
8727
|
+
if (ext === '.r') return extractRDeps ? extractRDeps(content) : [];
|
|
8516
8728
|
} catch (_) {}
|
|
8517
8729
|
return [];
|
|
8518
8730
|
}
|
|
@@ -11425,6 +11637,7 @@ function main() {
|
|
|
11425
11637
|
'.java': 'java', '.kt': 'kotlin', '.go': 'go', '.rs': 'rust',
|
|
11426
11638
|
'.cs': 'csharp', '.cpp': 'cpp', '.rb': 'ruby', '.php': 'php',
|
|
11427
11639
|
'.swift': 'swift', '.dart': 'dart', '.scala': 'scala',
|
|
11640
|
+
'.r': 'r', '.R': 'r',
|
|
11428
11641
|
'.vue': 'vue', '.svelte': 'svelte', '.html': 'html',
|
|
11429
11642
|
'.css': 'css', '.yml': 'yaml', '.sh': 'shell',
|
|
11430
11643
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sigmap",
|
|
3
|
-
"version": "6.10.
|
|
3
|
+
"version": "6.10.10",
|
|
4
4
|
"description": "Zero-dependency AI context engine — 97% token reduction. No npm install. Runs on Node 18+.",
|
|
5
5
|
"main": "gen-context.js",
|
|
6
6
|
"exports": {
|
|
@@ -14,10 +14,10 @@
|
|
|
14
14
|
"gen-project-map": "./gen-project-map.js"
|
|
15
15
|
},
|
|
16
16
|
"scripts": {
|
|
17
|
-
"test": "node test/run.js",
|
|
17
|
+
"test": "node test/run.js && node test/r-language.test.js",
|
|
18
18
|
"test:integration": "node test/integration/strategy.test.js && node test/integration/secret-scan.test.js && node test/integration/token-budget.test.js && node test/integration/auto-budget.test.js && node test/integration/mcp-server.test.js",
|
|
19
19
|
"test:integration:all": "node test/integration/all.js",
|
|
20
|
-
"test:all": "node test/run.js && node test/integration/strategy.test.js && node test/integration/secret-scan.test.js",
|
|
20
|
+
"test:all": "node test/run.js && node test/r-language.test.js && node test/integration/strategy.test.js && node test/integration/secret-scan.test.js",
|
|
21
21
|
"generate": "node gen-context.js",
|
|
22
22
|
"watch": "node gen-context.js --watch",
|
|
23
23
|
"setup": "node gen-context.js --setup",
|