sigmap 8.32.1 → 8.34.0
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 +317 -293
- package/CHANGELOG.md +25 -0
- package/README.md +5 -5
- package/gen-context.js +414 -69
- package/llms-full.txt +5 -5
- package/llms.txt +3 -3
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/core/index.js +1 -0
- package/packages/core/package.json +1 -1
- package/src/config/loader.js +1 -1
- package/src/config/tune.js +1 -1
- package/src/discovery/language-detector.js +1 -0
- package/src/discovery/source-root-registry.js +10 -1
- package/src/discovery/source-root-scorer.js +1 -1
- package/src/eval/analyzer.js +5 -39
- package/src/extractors/deps.js +23 -1
- package/src/extractors/dispatch.js +18 -1
- package/src/extractors/lua.js +152 -0
- package/src/graph/call-graph.js +81 -1
- package/src/map/route-table.js +46 -9
- package/src/mcp/server.js +1 -1
package/gen-context.js
CHANGED
|
@@ -1644,7 +1644,7 @@ __factories["./src/config/loader"] = function(module, exports) {
|
|
|
1644
1644
|
'.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs',
|
|
1645
1645
|
'.py', '.pyw', '.java', '.kt', '.kts', '.go', '.rs', '.cs',
|
|
1646
1646
|
'.cpp', '.c', '.h', '.hpp', '.cc', '.rb', '.rake', '.php',
|
|
1647
|
-
'.swift', '.dart', '.scala', '.sc', '.vue', '.svelte',
|
|
1647
|
+
'.swift', '.dart', '.scala', '.sc', '.lua', '.vue', '.svelte',
|
|
1648
1648
|
'.html', '.htm', '.css', '.scss', '.sass', '.less',
|
|
1649
1649
|
'.yml', '.yaml', '.sh', '.bash', '.zsh', '.fish',
|
|
1650
1650
|
'.sql', '.graphql', '.gql', '.tf', '.tfvars', '.proto',
|
|
@@ -1968,7 +1968,7 @@ __factories["./src/config/tune"] = function(module, exports) {
|
|
|
1968
1968
|
const SOURCE_EXTS = new Set([
|
|
1969
1969
|
'.js', '.mjs', '.cjs', '.jsx', '.ts', '.tsx', '.py', '.rb', '.go', '.rs',
|
|
1970
1970
|
'.java', '.kt', '.cs', '.cpp', '.c', '.h', '.hpp', '.swift', '.dart',
|
|
1971
|
-
'.scala', '.php', '.gd', '.r', '.R',
|
|
1971
|
+
'.scala', '.php', '.lua', '.gd', '.r', '.R',
|
|
1972
1972
|
]);
|
|
1973
1973
|
|
|
1974
1974
|
/** Raw user config file content, or null when absent/unparsable. */
|
|
@@ -3128,6 +3128,7 @@ __factories["./src/discovery/language-detector"] = function(module, exports) {
|
|
|
3128
3128
|
'.java': 'java', '.kt': 'kotlin', '.cs': 'csharp', '.cpp': 'cpp',
|
|
3129
3129
|
'.c': 'cpp', '.h': 'cpp', '.hpp': 'cpp', '.swift': 'swift',
|
|
3130
3130
|
'.dart': 'dart', '.scala': 'scala', '.php': 'php',
|
|
3131
|
+
'.lua': 'lua',
|
|
3131
3132
|
'.gd': 'gdscript',
|
|
3132
3133
|
'.r': 'r', '.R': 'r',
|
|
3133
3134
|
};
|
|
@@ -3558,12 +3559,21 @@ __factories["./src/discovery/source-root-registry"] = function(module, exports)
|
|
|
3558
3559
|
akka: { detectionFiles: [], detectionDeps: ['akka'], srcDirs: ['src/main/scala','src'] },
|
|
3559
3560
|
play: { detectionFiles: [], detectionDeps: ['play'], srcDirs: ['app','conf'] },
|
|
3560
3561
|
spark: { detectionFiles: [], detectionDeps: ['spark'],srcDirs: ['src/main/scala'] },
|
|
3561
|
-
zio: { detectionFiles: [], detectionDeps: ['zio'],
|
|
3562
|
+
zio: { detectionFiles: [], detectionDeps: ['zio'], srcDirs: ['src/main/scala'] },
|
|
3562
3563
|
},
|
|
3563
3564
|
srcDirs: ['src/main/scala','src'],
|
|
3564
3565
|
penalties: ['target'],
|
|
3565
3566
|
},
|
|
3566
3567
|
|
|
3568
|
+
lua: {
|
|
3569
|
+
manifestFiles: ['.luarc.json', 'selene.toml', 'stylua.toml'],
|
|
3570
|
+
frameworks: {
|
|
3571
|
+
luarocks: { detectionFiles: ['*.rockspec'], srcDirs: ['src','lua','lib'] },
|
|
3572
|
+
},
|
|
3573
|
+
srcDirs: ['src','lua','lib'],
|
|
3574
|
+
penalties: ['.luarocks','luarocks_modules'],
|
|
3575
|
+
},
|
|
3576
|
+
|
|
3567
3577
|
r: {
|
|
3568
3578
|
manifestFiles: ['DESCRIPTION','renv.lock'],
|
|
3569
3579
|
frameworks: {
|
|
@@ -3797,7 +3807,7 @@ __factories["./src/discovery/source-root-scorer"] = function(module, exports) {
|
|
|
3797
3807
|
const CODE_EXTS = new Set([
|
|
3798
3808
|
'.js','.mjs','.cjs','.ts','.tsx','.jsx',
|
|
3799
3809
|
'.py','.rb','.go','.rs','.java','.kt',
|
|
3800
|
-
'.cs','.cpp','.c','.h','.swift','.dart','.scala','.php',
|
|
3810
|
+
'.cs','.cpp','.c','.h','.swift','.dart','.scala','.php','.lua',
|
|
3801
3811
|
]);
|
|
3802
3812
|
|
|
3803
3813
|
const AUTO_SKIP = new Set([
|
|
@@ -4151,47 +4161,13 @@ __factories["./src/eval/analyzer"] = function(module, exports) {
|
|
|
4151
4161
|
const path = require('path');
|
|
4152
4162
|
|
|
4153
4163
|
// Extension → extractor name (mirrors EXT_MAP in gen-context.js)
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
'.java': 'java',
|
|
4159
|
-
'.kt': 'kotlin', '.kts': 'kotlin',
|
|
4160
|
-
'.go': 'go',
|
|
4161
|
-
'.rs': 'rust',
|
|
4162
|
-
'.cs': 'csharp',
|
|
4163
|
-
'.cpp': 'cpp', '.c': 'cpp', '.h': 'cpp', '.hpp': 'cpp', '.cc': 'cpp',
|
|
4164
|
-
'.rb': 'ruby', '.rake': 'ruby',
|
|
4165
|
-
'.php': 'php',
|
|
4166
|
-
'.swift': 'swift',
|
|
4167
|
-
'.dart': 'dart',
|
|
4168
|
-
'.scala': 'scala', '.sc': 'scala',
|
|
4169
|
-
'.gd': 'gdscript',
|
|
4170
|
-
'.r': 'r', '.R': 'r',
|
|
4171
|
-
'.svelte': 'svelte',
|
|
4172
|
-
'.html': 'html', '.htm': 'html',
|
|
4173
|
-
'.css': 'css', '.scss': 'css', '.sass': 'css', '.less': 'css',
|
|
4174
|
-
'.yml': 'yaml', '.yaml': 'yaml',
|
|
4175
|
-
'.sh': 'shell', '.bash': 'shell', '.zsh': 'shell', '.fish': 'shell',
|
|
4176
|
-
'.toml': 'toml',
|
|
4177
|
-
'.properties': 'properties',
|
|
4178
|
-
'.xml': 'xml',
|
|
4179
|
-
'.md': 'markdown',
|
|
4180
|
-
// Phase C specialized extractors
|
|
4181
|
-
'.tsx': 'typescript_react',
|
|
4182
|
-
'.vue': 'vue_sfc',
|
|
4183
|
-
};
|
|
4184
|
-
|
|
4185
|
-
function isDockerfile(name) {
|
|
4186
|
-
return name === 'Dockerfile' || name.startsWith('Dockerfile.');
|
|
4187
|
-
}
|
|
4164
|
+
// Extractor resolution goes through the dispatcher — the single source of
|
|
4165
|
+
// truth (#591). This file previously kept its own copy, which had drifted to
|
|
4166
|
+
// a dead duplicate `.vue` key.
|
|
4167
|
+
const { langFor } = __require('./src/extractors/dispatch');
|
|
4188
4168
|
|
|
4189
4169
|
function getExtractorName(filePath) {
|
|
4190
|
-
|
|
4191
|
-
const ext = path.extname(base).toLowerCase();
|
|
4192
|
-
if (EXT_MAP[ext]) return EXT_MAP[ext];
|
|
4193
|
-
if (isDockerfile(base)) return 'dockerfile';
|
|
4194
|
-
return null;
|
|
4170
|
+
return langFor(filePath);
|
|
4195
4171
|
}
|
|
4196
4172
|
|
|
4197
4173
|
/** Rough token estimate: chars / 4 */
|
|
@@ -5916,6 +5892,28 @@ __factories["./src/extractors/deps"] = function(module, exports) {
|
|
|
5916
5892
|
return [...deps].slice(0, 5);
|
|
5917
5893
|
}
|
|
5918
5894
|
|
|
5895
|
+
/**
|
|
5896
|
+
* Extract Lua require() module dependencies.
|
|
5897
|
+
* Captures `require "mod"` and `require("mod")`, returning compact module
|
|
5898
|
+
* names as they appear in source.
|
|
5899
|
+
* @param {string} src
|
|
5900
|
+
* @returns {string[]}
|
|
5901
|
+
*/
|
|
5902
|
+
function extractLuaDeps(src) {
|
|
5903
|
+
const deps = new Set();
|
|
5904
|
+
const stripped = stripLuaComments(src || '');
|
|
5905
|
+
for (const m of stripped.matchAll(/\brequire\s*(?:\(\s*)?["']([A-Za-z0-9_.\/-]+)["']\s*\)?/g)) {
|
|
5906
|
+
if (m[1]) deps.add(m[1]);
|
|
5907
|
+
}
|
|
5908
|
+
return [...deps].slice(0, 5);
|
|
5909
|
+
}
|
|
5910
|
+
|
|
5911
|
+
function stripLuaComments(src) {
|
|
5912
|
+
return String(src || '')
|
|
5913
|
+
.replace(/--\[\[[\s\S]*?\]\]/g, '')
|
|
5914
|
+
.replace(/--.*$/gm, '');
|
|
5915
|
+
}
|
|
5916
|
+
|
|
5919
5917
|
/**
|
|
5920
5918
|
* Build reverse dependency map from forward map.
|
|
5921
5919
|
* @param {Map<string, string[]>} forwardMap
|
|
@@ -5934,7 +5932,7 @@ __factories["./src/extractors/deps"] = function(module, exports) {
|
|
|
5934
5932
|
return reverse;
|
|
5935
5933
|
}
|
|
5936
5934
|
|
|
5937
|
-
module.exports = { extractPythonDeps, extractTSDeps, extractRDeps, buildReverseDepMap };
|
|
5935
|
+
module.exports = { extractPythonDeps, extractTSDeps, extractRDeps, extractLuaDeps, buildReverseDepMap };
|
|
5938
5936
|
|
|
5939
5937
|
};
|
|
5940
5938
|
|
|
@@ -5969,6 +5967,7 @@ __factories["./src/extractors/dispatch"] = function(module, exports) {
|
|
|
5969
5967
|
swift: __require('./src/extractors/swift'),
|
|
5970
5968
|
dart: __require('./src/extractors/dart'),
|
|
5971
5969
|
scala: __require('./src/extractors/scala'),
|
|
5970
|
+
lua: __require('./src/extractors/lua'),
|
|
5972
5971
|
gdscript: __require('./src/extractors/gdscript'),
|
|
5973
5972
|
r: __require('./src/extractors/r'),
|
|
5974
5973
|
vue_sfc: __require('./src/extractors/vue_sfc'),
|
|
@@ -5989,6 +5988,21 @@ __factories["./src/extractors/dispatch"] = function(module, exports) {
|
|
|
5989
5988
|
generic: __require('./src/extractors/generic'),
|
|
5990
5989
|
};
|
|
5991
5990
|
|
|
5991
|
+
/**
|
|
5992
|
+
* Extension → extractor module name. **The single source of truth for
|
|
5993
|
+
* extractor resolution** (#591).
|
|
5994
|
+
*
|
|
5995
|
+
* Anything that decides *which extractor module to load* must go through
|
|
5996
|
+
* `langFor` rather than declaring its own copy. Three copies existed and two
|
|
5997
|
+
* had drifted: `src/eval/analyzer.js` carried a dead duplicate `.vue` key, and
|
|
5998
|
+
* the `--diagnose-extractors` map pointed at `vue.js` after that module was
|
|
5999
|
+
* deleted — which is how an unreachable extractor survived unnoticed (#582).
|
|
6000
|
+
*
|
|
6001
|
+
* Not every extension map in the codebase belongs here. `language-detector.js`
|
|
6002
|
+
* maps `.tsx → typescript` for language *statistics*, and `dashboard.js` keeps
|
|
6003
|
+
* short display *labels*. Both are correct for their purpose and deliberately
|
|
6004
|
+
* differ from resolution — folding them in would miscount languages.
|
|
6005
|
+
*/
|
|
5992
6006
|
const EXT_MAP = {
|
|
5993
6007
|
'.ts': 'typescript', '.tsx': 'typescript_react',
|
|
5994
6008
|
'.js': 'javascript', '.jsx': 'javascript', '.mjs': 'javascript', '.cjs': 'javascript',
|
|
@@ -6004,6 +6018,7 @@ __factories["./src/extractors/dispatch"] = function(module, exports) {
|
|
|
6004
6018
|
'.swift': 'swift',
|
|
6005
6019
|
'.dart': 'dart',
|
|
6006
6020
|
'.scala': 'scala', '.sc': 'scala',
|
|
6021
|
+
'.lua': 'lua',
|
|
6007
6022
|
'.gd': 'gdscript',
|
|
6008
6023
|
'.r': 'r', '.R': 'r',
|
|
6009
6024
|
'.vue': 'vue_sfc',
|
|
@@ -6049,7 +6064,7 @@ __factories["./src/extractors/dispatch"] = function(module, exports) {
|
|
|
6049
6064
|
}
|
|
6050
6065
|
}
|
|
6051
6066
|
|
|
6052
|
-
module.exports = { extractFile, langFor };
|
|
6067
|
+
module.exports = { extractFile, langFor, EXT_MAP };
|
|
6053
6068
|
|
|
6054
6069
|
};
|
|
6055
6070
|
|
|
@@ -7031,6 +7046,162 @@ __factories["./src/extractors/line-anchor"] = function(module, exports) {
|
|
|
7031
7046
|
|
|
7032
7047
|
};
|
|
7033
7048
|
|
|
7049
|
+
// ── ./src/extractors/lua ──
|
|
7050
|
+
__factories["./src/extractors/lua"] = function(module, exports) {
|
|
7051
|
+
|
|
7052
|
+
const { capWithNotice } = __require('./src/util/truncate');
|
|
7053
|
+
|
|
7054
|
+
// Ceiling discloses what it drops rather than truncating silently (#583).
|
|
7055
|
+
// Collection runs to completion so the marker reports the true overflow —
|
|
7056
|
+
// stopping early made r.js report "+1 more" where 50 were hidden (#584).
|
|
7057
|
+
const PER_FILE_LIMIT = 30;
|
|
7058
|
+
|
|
7059
|
+
/**
|
|
7060
|
+
* Extract signatures from Lua source code.
|
|
7061
|
+
*
|
|
7062
|
+
* Recognised constructs:
|
|
7063
|
+
* - Global functions: `function name(args)`
|
|
7064
|
+
* - Module-table functions: `function M.name(args)` / `function M:name(args)`
|
|
7065
|
+
* - Local functions: `local function name(args)`
|
|
7066
|
+
* - Assigned functions: `name = function(args)` / `M.name = function(args)`
|
|
7067
|
+
* - Module imports: `local mod = require("mod")` as compact hints
|
|
7068
|
+
* - LDoc-style doc comments (`---`) as first-sentence hints
|
|
7069
|
+
*
|
|
7070
|
+
* The extractor is regex-only and zero-dependency, matching SigMap's Tier-3
|
|
7071
|
+
* language extractor style.
|
|
7072
|
+
*
|
|
7073
|
+
* @param {string} src - Raw file content
|
|
7074
|
+
* @returns {string[]} Array of signature strings
|
|
7075
|
+
*/
|
|
7076
|
+
function extract(src) {
|
|
7077
|
+
if (!src || typeof src !== 'string') return [];
|
|
7078
|
+
const sigs = [];
|
|
7079
|
+
const hints = collectDocHints(src);
|
|
7080
|
+
const stripped = stripLuaComments(src);
|
|
7081
|
+
const seen = new Set();
|
|
7082
|
+
|
|
7083
|
+
// local foo = require('bar.baz') — useful module-surface hint, capped low.
|
|
7084
|
+
for (const m of stripped.matchAll(/^\s*(?:local\s+)?([A-Za-z_]\w*)\s*=\s*require\s*\(\s*['"]([A-Za-z0-9_.\/-]+)['"]\s*\)/gm)) {
|
|
7085
|
+
pushUnique(sigs, seen, `require ${m[2]} as ${m[1]}`);
|
|
7086
|
+
}
|
|
7087
|
+
|
|
7088
|
+
// local function name(args)
|
|
7089
|
+
for (const m of stripped.matchAll(/^\s*local\s+function\s+([A-Za-z_]\w*)\s*\(([^)]*)\)/gm)) {
|
|
7090
|
+
if (m[1].startsWith('_')) continue;
|
|
7091
|
+
pushUnique(sigs, seen, `local function ${m[1]}(${normalizeParams(m[2])})${applyHint(hints, m[1])}`);
|
|
7092
|
+
}
|
|
7093
|
+
|
|
7094
|
+
// function name(args), function M.name(args), function M:name(args)
|
|
7095
|
+
for (const m of stripped.matchAll(/^\s*function\s+([A-Za-z_]\w*(?:(?:\.|:)[A-Za-z_]\w*)*)\s*\(([^)]*)\)/gm)) {
|
|
7096
|
+
const name = m[1];
|
|
7097
|
+
if (name.startsWith('_')) continue;
|
|
7098
|
+
pushUnique(sigs, seen, `function ${name}(${normalizeParams(m[2])})${applyHint(hints, name)}`);
|
|
7099
|
+
}
|
|
7100
|
+
|
|
7101
|
+
// name = function(args), M.name = function(args), M:name = function(args)
|
|
7102
|
+
for (const m of stripped.matchAll(/^\s*(?:local\s+)?([A-Za-z_]\w*(?:(?:\.|:)[A-Za-z_]\w*)*)\s*=\s*function\s*\(([^)]*)\)/gm)) {
|
|
7103
|
+
const name = m[1];
|
|
7104
|
+
if (name.startsWith('_')) continue;
|
|
7105
|
+
pushUnique(sigs, seen, `${name} = function(${normalizeParams(m[2])})${applyHint(hints, name)}`);
|
|
7106
|
+
}
|
|
7107
|
+
|
|
7108
|
+
return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
|
|
7109
|
+
}
|
|
7110
|
+
|
|
7111
|
+
function pushUnique(out, seen, sig) {
|
|
7112
|
+
if (!sig || seen.has(sig)) return;
|
|
7113
|
+
seen.add(sig);
|
|
7114
|
+
out.push(sig);
|
|
7115
|
+
}
|
|
7116
|
+
|
|
7117
|
+
function normalizeParams(params) {
|
|
7118
|
+
return String(params || '')
|
|
7119
|
+
.replace(/--.*$/gm, '')
|
|
7120
|
+
.split(',')
|
|
7121
|
+
.map((p) => p.trim().replace(/\s+/g, ' '))
|
|
7122
|
+
.filter(Boolean)
|
|
7123
|
+
.join(', ');
|
|
7124
|
+
}
|
|
7125
|
+
|
|
7126
|
+
function applyHint(hints, name) {
|
|
7127
|
+
const h = hints.get(name);
|
|
7128
|
+
return h ? ` # ${h}` : '';
|
|
7129
|
+
}
|
|
7130
|
+
|
|
7131
|
+
/**
|
|
7132
|
+
* Attach each contiguous `---` doc block to the next function-like declaration
|
|
7133
|
+
* by its extracted symbol name.
|
|
7134
|
+
*/
|
|
7135
|
+
function collectDocHints(src) {
|
|
7136
|
+
const hints = new Map();
|
|
7137
|
+
const lines = src.split('\n');
|
|
7138
|
+
let block = [];
|
|
7139
|
+
for (const line of lines) {
|
|
7140
|
+
const doc = line.match(/^\s*---\s?(.*)$/);
|
|
7141
|
+
if (doc) {
|
|
7142
|
+
block.push(doc[1]);
|
|
7143
|
+
} else if (block.length > 0) {
|
|
7144
|
+
const decl = line.match(/^\s*local\s+function\s+([A-Za-z_]\w*)\s*\(/)
|
|
7145
|
+
|| line.match(/^\s*function\s+([A-Za-z_]\w*(?:(?:\.|:)[A-Za-z_]\w*)*)\s*\(/)
|
|
7146
|
+
|| line.match(/^\s*(?:local\s+)?([A-Za-z_]\w*(?:(?:\.|:)[A-Za-z_]\w*)*)\s*=\s*function\s*\(/);
|
|
7147
|
+
if (decl) {
|
|
7148
|
+
const hint = firstDocSentence(block);
|
|
7149
|
+
if (hint) hints.set(decl[1], hint);
|
|
7150
|
+
}
|
|
7151
|
+
block = [];
|
|
7152
|
+
}
|
|
7153
|
+
}
|
|
7154
|
+
return hints;
|
|
7155
|
+
}
|
|
7156
|
+
|
|
7157
|
+
function firstDocSentence(block) {
|
|
7158
|
+
for (const raw of block) {
|
|
7159
|
+
const line = String(raw || '').trim();
|
|
7160
|
+
if (!line || line.startsWith('@')) continue;
|
|
7161
|
+
return line.replace(/\s+/g, ' ').slice(0, 60).replace(/[.,;:!?]+$/, '').trim();
|
|
7162
|
+
}
|
|
7163
|
+
return '';
|
|
7164
|
+
}
|
|
7165
|
+
|
|
7166
|
+
/** Strip Lua line and long comments while preserving strings enough for regex scans. */
|
|
7167
|
+
function stripLuaComments(src) {
|
|
7168
|
+
const out = src.split('');
|
|
7169
|
+
const blank = (a, b) => { for (let i = a; i < b; i++) if (out[i] !== '\n') out[i] = ' '; };
|
|
7170
|
+
let i = 0;
|
|
7171
|
+
while (i < src.length) {
|
|
7172
|
+
const ch = src[i];
|
|
7173
|
+
if (ch === '"' || ch === "'") {
|
|
7174
|
+
const quote = ch;
|
|
7175
|
+
i++;
|
|
7176
|
+
while (i < src.length) {
|
|
7177
|
+
if (src[i] === '\\') { i += 2; continue; }
|
|
7178
|
+
if (src[i] === quote) { i++; break; }
|
|
7179
|
+
if (src[i] === '\n') break;
|
|
7180
|
+
i++;
|
|
7181
|
+
}
|
|
7182
|
+
continue;
|
|
7183
|
+
}
|
|
7184
|
+
if (src.startsWith('--[[', i)) {
|
|
7185
|
+
const end = src.indexOf(']]', i + 4);
|
|
7186
|
+
blank(i, end === -1 ? src.length : end + 2);
|
|
7187
|
+
i = end === -1 ? src.length : end + 2;
|
|
7188
|
+
continue;
|
|
7189
|
+
}
|
|
7190
|
+
if (src.startsWith('--', i)) {
|
|
7191
|
+
const end = src.indexOf('\n', i + 2);
|
|
7192
|
+
blank(i, end === -1 ? src.length : end);
|
|
7193
|
+
i = end === -1 ? src.length : end;
|
|
7194
|
+
continue;
|
|
7195
|
+
}
|
|
7196
|
+
i++;
|
|
7197
|
+
}
|
|
7198
|
+
return out.join('');
|
|
7199
|
+
}
|
|
7200
|
+
|
|
7201
|
+
module.exports = { extract };
|
|
7202
|
+
|
|
7203
|
+
};
|
|
7204
|
+
|
|
7034
7205
|
// ── ./src/extractors/markdown ──
|
|
7035
7206
|
__factories["./src/extractors/markdown"] = function(module, exports) {
|
|
7036
7207
|
|
|
@@ -11996,6 +12167,8 @@ __factories["./src/graph/call-graph"] = function(module, exports) {
|
|
|
11996
12167
|
const JAVA_EXTS = new Set(['.java']);
|
|
11997
12168
|
const GO_EXTS = new Set(['.go']);
|
|
11998
12169
|
const RS_EXTS = new Set(['.rs']);
|
|
12170
|
+
const KT_EXTS = new Set(['.kt', '.kts']);
|
|
12171
|
+
const SCALA_EXTS = new Set(['.scala', '.sc']);
|
|
11999
12172
|
|
|
12000
12173
|
// Tokens that look like `name(` calls or definition headers but are language
|
|
12001
12174
|
// keywords, not user symbols — never treated as a call or a definition.
|
|
@@ -12319,6 +12492,81 @@ __factories["./src/graph/call-graph"] = function(module, exports) {
|
|
|
12319
12492
|
|
|
12320
12493
|
// Pick the masker whose comment/string syntax matches the language.
|
|
12321
12494
|
// Java and Go share JS syntax (Go raw strings mask like template literals).
|
|
12495
|
+
/**
|
|
12496
|
+
* Body range for a JVM-family member that may use either a brace body or an
|
|
12497
|
+
* expression body (`fun f() = expr` / `def f = expr`), which Java has not.
|
|
12498
|
+
* An expression body runs to the end of its line — enough to capture the calls
|
|
12499
|
+
* it makes, which is all the graph needs.
|
|
12500
|
+
* @returns {{bodyStart:number, bodyEnd:number}|null} null when neither form follows
|
|
12501
|
+
*/
|
|
12502
|
+
function jvmBodyRange(masked, from) {
|
|
12503
|
+
let k = from;
|
|
12504
|
+
while (k < masked.length && masked[k] !== '{' && masked[k] !== '=' && masked[k] !== '\n' && masked[k] !== ';') k++;
|
|
12505
|
+
if (masked[k] === '{') return { bodyStart: k, bodyEnd: matchDelim(masked, k, '{', '}') };
|
|
12506
|
+
if (masked[k] === '=') {
|
|
12507
|
+
// `= {` is still a brace body, just written with an assignment.
|
|
12508
|
+
let j = k + 1;
|
|
12509
|
+
while (j < masked.length && /\s/.test(masked[j])) j++;
|
|
12510
|
+
if (masked[j] === '{') return { bodyStart: j, bodyEnd: matchDelim(masked, j, '{', '}') };
|
|
12511
|
+
const eol = masked.indexOf('\n', k);
|
|
12512
|
+
return { bodyStart: k, bodyEnd: eol === -1 ? masked.length : eol };
|
|
12513
|
+
}
|
|
12514
|
+
return null; // abstract member or interface declaration
|
|
12515
|
+
}
|
|
12516
|
+
|
|
12517
|
+
/**
|
|
12518
|
+
* Kotlin `fun` definitions, including extension functions (`fun Foo.bar()`,
|
|
12519
|
+
* recorded as `bar`) and expression bodies.
|
|
12520
|
+
*/
|
|
12521
|
+
function ktDefs(masked) {
|
|
12522
|
+
const defs = [];
|
|
12523
|
+
const seen = new Set();
|
|
12524
|
+
const re = /(?:^|\n)[ \t]*((?:(?:public|private|protected|internal|open|override|abstract|final|suspend|inline|operator|infix|tailrec|external|expect|actual|inner|companion)\s+)*)fun\s+(?:<[^>\n]{0,80}>\s*)?(?:[A-Za-z_][\w.<>]*\.)?([A-Za-z_][\w]*)\s*\(/g;
|
|
12525
|
+
let m;
|
|
12526
|
+
while ((m = re.exec(masked)) !== null) {
|
|
12527
|
+
const name = m[2];
|
|
12528
|
+
if (NON_CALL.has(name)) continue;
|
|
12529
|
+
const paren = masked.indexOf('(', m.index + m[0].length - 1);
|
|
12530
|
+
const close = matchDelim(masked, paren, '(', ')');
|
|
12531
|
+
if (close < 0) continue;
|
|
12532
|
+
const range = jvmBodyRange(masked, close + 1);
|
|
12533
|
+
const line = lineAt(masked, m.index + 1);
|
|
12534
|
+
const key = name + ':' + (range ? range.bodyStart : line);
|
|
12535
|
+
if (seen.has(key)) continue;
|
|
12536
|
+
seen.add(key);
|
|
12537
|
+
// An abstract/interface member owns no body: it can receive edges but
|
|
12538
|
+
// never produces them, the same treatment javaDefs gives declarations.
|
|
12539
|
+
defs.push(range ? { name, line, ...range } : { name, line, bodyStart: close, bodyEnd: close });
|
|
12540
|
+
}
|
|
12541
|
+
return defs;
|
|
12542
|
+
}
|
|
12543
|
+
|
|
12544
|
+
/**
|
|
12545
|
+
* Scala `def` definitions. Handles parameterless members (`def foo: Int = 1`),
|
|
12546
|
+
* type parameters, and expression bodies.
|
|
12547
|
+
*/
|
|
12548
|
+
function scalaDefs(masked) {
|
|
12549
|
+
const defs = [];
|
|
12550
|
+
const seen = new Set();
|
|
12551
|
+
const re = /(?:^|\n)[ \t]*((?:(?:private|protected|override|implicit|final|lazy|sealed|abstract|inline)(?:\s*\[[^\]\n]{0,60}\])?\s+)*)def\s+([A-Za-z_][\w]*)\s*(?=[\[\(:=])/g;
|
|
12552
|
+
let m;
|
|
12553
|
+
while ((m = re.exec(masked)) !== null) {
|
|
12554
|
+
const name = m[2];
|
|
12555
|
+
if (NON_CALL.has(name)) continue;
|
|
12556
|
+
// Step past an optional type-parameter list, then an optional value list.
|
|
12557
|
+
let k = m.index + m[0].length;
|
|
12558
|
+
if (masked[k] === '[') { const e = matchDelim(masked, k, '[', ']'); if (e < 0) continue; k = e + 1; }
|
|
12559
|
+
while (masked[k] === '(') { const e = matchDelim(masked, k, '(', ')'); if (e < 0) break; k = e + 1; }
|
|
12560
|
+
const range = jvmBodyRange(masked, k);
|
|
12561
|
+
const line = lineAt(masked, m.index + 1);
|
|
12562
|
+
const key = name + ':' + (range ? range.bodyStart : line);
|
|
12563
|
+
if (seen.has(key)) continue;
|
|
12564
|
+
seen.add(key);
|
|
12565
|
+
defs.push(range ? { name, line, ...range } : { name, line, bodyStart: k, bodyEnd: k });
|
|
12566
|
+
}
|
|
12567
|
+
return defs;
|
|
12568
|
+
}
|
|
12569
|
+
|
|
12322
12570
|
function maskFor(filePath, src) {
|
|
12323
12571
|
const ext = path.extname(filePath).toLowerCase();
|
|
12324
12572
|
if (PY_EXTS.has(ext)) return maskPy(src);
|
|
@@ -12333,6 +12581,8 @@ __factories["./src/graph/call-graph"] = function(module, exports) {
|
|
|
12333
12581
|
if (JAVA_EXTS.has(ext)) return javaDefs(maskJs(src));
|
|
12334
12582
|
if (GO_EXTS.has(ext)) return goDefs(maskJs(src));
|
|
12335
12583
|
if (RS_EXTS.has(ext)) return rustDefs(maskRust(src));
|
|
12584
|
+
if (KT_EXTS.has(ext)) return ktDefs(maskJs(src));
|
|
12585
|
+
if (SCALA_EXTS.has(ext)) return scalaDefs(maskJs(src));
|
|
12336
12586
|
return null; // unsupported language
|
|
12337
12587
|
}
|
|
12338
12588
|
|
|
@@ -12422,7 +12672,8 @@ __factories["./src/graph/call-graph"] = function(module, exports) {
|
|
|
12422
12672
|
if (e.isDirectory()) _walk(full, excludeSet, out, depth + 1, maxDepth);
|
|
12423
12673
|
else if (e.isFile()) {
|
|
12424
12674
|
const ext = path.extname(e.name).toLowerCase();
|
|
12425
|
-
if (JS_EXTS.has(ext) || PY_EXTS.has(ext) || JAVA_EXTS.has(ext) || GO_EXTS.has(ext)
|
|
12675
|
+
if (JS_EXTS.has(ext) || PY_EXTS.has(ext) || JAVA_EXTS.has(ext) || GO_EXTS.has(ext)
|
|
12676
|
+
|| RS_EXTS.has(ext) || KT_EXTS.has(ext) || SCALA_EXTS.has(ext)) out.push(full);
|
|
12426
12677
|
}
|
|
12427
12678
|
}
|
|
12428
12679
|
}
|
|
@@ -14453,6 +14704,45 @@ __factories["./src/map/route-table"] = function(module, exports) {
|
|
|
14453
14704
|
return /(^|\/)(gen-context|gen-project-map)\.js$/.test(normalized);
|
|
14454
14705
|
}
|
|
14455
14706
|
|
|
14707
|
+
/**
|
|
14708
|
+
* Byte offsets and prefixes of every `@Controller(...)` in a file.
|
|
14709
|
+
* A file may declare several controllers, so each route is attributed to the
|
|
14710
|
+
* nearest one above it rather than to a single file-wide prefix.
|
|
14711
|
+
* @param {string} content
|
|
14712
|
+
* @returns {Array<{index:number, prefix:string}>} ascending by index
|
|
14713
|
+
*/
|
|
14714
|
+
function nestControllerPrefixes(content) {
|
|
14715
|
+
const out = [];
|
|
14716
|
+
const re = /@Controller\s*\(\s*(?:['"`]([^'"`]*)['"`])?/g;
|
|
14717
|
+
let m;
|
|
14718
|
+
while ((m = re.exec(content)) !== null) {
|
|
14719
|
+
out.push({ index: m.index, prefix: m[1] || '' });
|
|
14720
|
+
}
|
|
14721
|
+
return out;
|
|
14722
|
+
}
|
|
14723
|
+
|
|
14724
|
+
/** Prefix of the nearest `@Controller` above `index`, or '' when there is none. */
|
|
14725
|
+
function prefixBefore(controllers, index) {
|
|
14726
|
+
let prefix = '';
|
|
14727
|
+
for (const c of controllers) {
|
|
14728
|
+
if (c.index > index) break;
|
|
14729
|
+
prefix = c.prefix;
|
|
14730
|
+
}
|
|
14731
|
+
return prefix;
|
|
14732
|
+
}
|
|
14733
|
+
|
|
14734
|
+
/**
|
|
14735
|
+
* Join a controller prefix and a method path into one route path.
|
|
14736
|
+
* Either side may be empty, absent, or carry its own slashes.
|
|
14737
|
+
* @returns {string} always slash-prefixed; never a trailing slash except '/'
|
|
14738
|
+
*/
|
|
14739
|
+
function joinRoute(prefix, methodPath) {
|
|
14740
|
+
const parts = [prefix, methodPath]
|
|
14741
|
+
.map((p) => String(p || '').trim().replace(/^\/+|\/+$/g, ''))
|
|
14742
|
+
.filter(Boolean);
|
|
14743
|
+
return parts.length ? '/' + parts.join('/') : '/';
|
|
14744
|
+
}
|
|
14745
|
+
|
|
14456
14746
|
/**
|
|
14457
14747
|
* Structured route rows across the supported frameworks — the data behind
|
|
14458
14748
|
* `analyze`, exposed for retrieval surface-enrichment (#488).
|
|
@@ -14481,16 +14771,14 @@ __factories["./src/map/route-table"] = function(module, exports) {
|
|
|
14481
14771
|
routes.push({ method: m[1].toUpperCase(), path: m[2], file: rel });
|
|
14482
14772
|
}
|
|
14483
14773
|
|
|
14484
|
-
// NestJS
|
|
14485
|
-
|
|
14774
|
+
// NestJS: @Get(':id') / @Post() — composed with the enclosing
|
|
14775
|
+
// @Controller('prefix'). Without the prefix the emitted path matches
|
|
14776
|
+
// nothing real, which defeats the point of route pseudo-signatures (#585).
|
|
14777
|
+
const controllers = nestControllerPrefixes(content);
|
|
14778
|
+
const re2 = /@(Get|Post|Put|Patch|Delete|Head|Options|All)\s*\(\s*(?:['"`]([^'"`]*)['"`])?\s*\)/g;
|
|
14486
14779
|
while ((m = re2.exec(content)) !== null) {
|
|
14487
|
-
|
|
14488
|
-
|
|
14489
|
-
|
|
14490
|
-
// NestJS: @Get() with no path
|
|
14491
|
-
const re3 = /@(Get|Post|Put|Patch|Delete)\s*\(\s*\)/g;
|
|
14492
|
-
while ((m = re3.exec(content)) !== null) {
|
|
14493
|
-
routes.push({ method: m[1].toUpperCase(), path: '/', file: rel });
|
|
14780
|
+
const prefix = prefixBefore(controllers, m.index);
|
|
14781
|
+
routes.push({ method: m[1].toUpperCase(), path: joinRoute(prefix, m[2]), file: rel });
|
|
14494
14782
|
}
|
|
14495
14783
|
}
|
|
14496
14784
|
|
|
@@ -15788,7 +16076,7 @@ __factories["./src/mcp/server"] = function(module, exports) {
|
|
|
15788
16076
|
|
|
15789
16077
|
const SERVER_INFO = {
|
|
15790
16078
|
name: 'sigmap',
|
|
15791
|
-
version: '8.
|
|
16079
|
+
version: '8.34.0',
|
|
15792
16080
|
description: 'SigMap MCP server — code signatures on demand',
|
|
15793
16081
|
};
|
|
15794
16082
|
|
|
@@ -22082,7 +22370,7 @@ function __tryGit(args, opts = {}) {
|
|
|
22082
22370
|
catch (_) { return ''; }
|
|
22083
22371
|
}
|
|
22084
22372
|
|
|
22085
|
-
const VERSION = '8.
|
|
22373
|
+
const VERSION = '8.34.0';
|
|
22086
22374
|
const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
|
|
22087
22375
|
|
|
22088
22376
|
function requireSourceOrBundled(key) {
|
|
@@ -22451,6 +22739,38 @@ function computeEffectiveMaxTokens(fileEntries, config) {
|
|
|
22451
22739
|
return effective;
|
|
22452
22740
|
}
|
|
22453
22741
|
|
|
22742
|
+
/**
|
|
22743
|
+
* Delete `.github/context-*.md` split files this run did not write (#555).
|
|
22744
|
+
*
|
|
22745
|
+
* Splits are discovered by filename pattern at read time, not by consulting
|
|
22746
|
+
* the config, so a file left behind by a previous `strategy` — or by a module
|
|
22747
|
+
* since dropped from `srcDirs` — keeps being merged into the retrieval index
|
|
22748
|
+
* and silently steers every query. Observed on a 524-file Java repo: a stale
|
|
22749
|
+
* 376 KB `context-mall-mbg.md` kept generated entities at ranks 1, 3 and 4
|
|
22750
|
+
* while both files implementing the feature fell outside the top 6, even
|
|
22751
|
+
* though `sig-index.json` correctly held zero entries for that module.
|
|
22752
|
+
*
|
|
22753
|
+
* @param {string} cwd
|
|
22754
|
+
* @param {string[]} keep basenames this run wrote, e.g. ['context-core.md']
|
|
22755
|
+
* @returns {string[]} basenames removed
|
|
22756
|
+
*/
|
|
22757
|
+
function pruneStaleContextSplits(cwd, keep) {
|
|
22758
|
+
const ghDir = path.join(cwd, '.github');
|
|
22759
|
+
const kept = new Set(keep);
|
|
22760
|
+
const removed = [];
|
|
22761
|
+
let entries;
|
|
22762
|
+
try { entries = fs.readdirSync(ghDir); } catch (_) { return removed; }
|
|
22763
|
+
for (const f of entries) {
|
|
22764
|
+
if (!/^context-[\w.-]+\.md$/.test(f) || kept.has(f)) continue;
|
|
22765
|
+
try { fs.unlinkSync(path.join(ghDir, f)); removed.push(f); } catch (_) { /* best effort */ }
|
|
22766
|
+
}
|
|
22767
|
+
if (removed.length) {
|
|
22768
|
+
console.warn(`[sigmap] pruned ${removed.length} stale context split(s) from a previous `
|
|
22769
|
+
+ `strategy/srcDirs: ${removed.join(', ')}`);
|
|
22770
|
+
}
|
|
22771
|
+
return removed;
|
|
22772
|
+
}
|
|
22773
|
+
|
|
22454
22774
|
function applyTokenBudget(fileEntries, maxTokens) {
|
|
22455
22775
|
// fileEntries: [{ filePath, sigs, mtime }]
|
|
22456
22776
|
// Per-file rendered overhead: "### <path>", code fences, trailing blank line.
|
|
@@ -22532,6 +22852,17 @@ function applyTokenBudget(fileEntries, maxTokens) {
|
|
|
22532
22852
|
// Restore the original file order for stable output.
|
|
22533
22853
|
const kept = withPriority.filter((e) => finalByPath.has(e.filePath)).map((e) => finalByPath.get(e.filePath));
|
|
22534
22854
|
|
|
22855
|
+
// Record what was omitted so the artifact itself can say so (#587). The
|
|
22856
|
+
// stderr warning below is invisible to an agent that only reads the file:
|
|
22857
|
+
// 25 sections with no notice is indistinguishable from a 25-file repo.
|
|
22858
|
+
// Non-enumerable so every existing consumer still sees a plain array.
|
|
22859
|
+
if (verboseDropped.length > 0 || collapsedCount > 0) {
|
|
22860
|
+
Object.defineProperty(kept, '__omissions', {
|
|
22861
|
+
value: { dropped: verboseDropped.length, collapsed: collapsedCount, maxTokens },
|
|
22862
|
+
enumerable: false, writable: false, configurable: true,
|
|
22863
|
+
});
|
|
22864
|
+
}
|
|
22865
|
+
|
|
22535
22866
|
if (verboseDropped.length > 0 || collapsedCount > 0) {
|
|
22536
22867
|
const parts = [];
|
|
22537
22868
|
if (verboseDropped.length) parts.push(`dropped ${verboseDropped.length} file(s)`);
|
|
@@ -22860,6 +23191,20 @@ function formatOutput(fileEntries, cwd, routingEnabled, config, extras) {
|
|
|
22860
23191
|
}
|
|
22861
23192
|
}
|
|
22862
23193
|
|
|
23194
|
+
// Say what the budget left out — an omission the reader cannot see is the
|
|
23195
|
+
// same failure as an undisclosed truncation cap (#587, cf. #576).
|
|
23196
|
+
const omitted = fileEntries && fileEntries.__omissions;
|
|
23197
|
+
if (omitted && (omitted.dropped > 0 || omitted.collapsed > 0)) {
|
|
23198
|
+
const bits = [];
|
|
23199
|
+
if (omitted.dropped) bits.push(`${omitted.dropped} file(s) omitted`);
|
|
23200
|
+
if (omitted.collapsed) bits.push(`${omitted.collapsed} collapsed to anchors`);
|
|
23201
|
+
lines.push('');
|
|
23202
|
+
lines.push(`> **Not everything is here.** ${bits.join(', ')} to stay under the `
|
|
23203
|
+
+ `${omitted.maxTokens}-token budget (tests and configs go first). `
|
|
23204
|
+
+ 'The retrieval index still has them all — run `sigmap ask "<question>"` '
|
|
23205
|
+
+ 'to pull in anything missing.');
|
|
23206
|
+
}
|
|
23207
|
+
|
|
22863
23208
|
return lines.join('\n');
|
|
22864
23209
|
}
|
|
22865
23210
|
|
|
@@ -23233,6 +23578,7 @@ function runPerModuleStrategy(cwd, config, fileEntries, inputTokenTotal) {
|
|
|
23233
23578
|
];
|
|
23234
23579
|
|
|
23235
23580
|
let totalOut = 0;
|
|
23581
|
+
const writtenSplits = [];
|
|
23236
23582
|
for (const mod of moduleNames) {
|
|
23237
23583
|
const outName = `context-${mod}.md`;
|
|
23238
23584
|
const outPath = path.join(cwd, '.github', outName);
|
|
@@ -23251,7 +23597,9 @@ function runPerModuleStrategy(cwd, config, fileEntries, inputTokenTotal) {
|
|
|
23251
23597
|
console.warn(`[sigmap] per-module: wrote .github/${outName} (~${modTokens} tokens, ${budgeted.length} files)`);
|
|
23252
23598
|
|
|
23253
23599
|
overviewLines.push(`| \`${mod}\` | \`.github/${outName}\` |`);
|
|
23600
|
+
writtenSplits.push(outName);
|
|
23254
23601
|
}
|
|
23602
|
+
pruneStaleContextSplits(cwd, writtenSplits);
|
|
23255
23603
|
|
|
23256
23604
|
overviewLines.push('');
|
|
23257
23605
|
overviewLines.push('> Inject the relevant module file into your IDE context window.');
|
|
@@ -23290,6 +23638,7 @@ function runHotColdStrategy(cwd, config, fileEntries, recentFiles, inputTokenTot
|
|
|
23290
23638
|
const coldContent = coldHeader + formatOutput(coldEntries, cwd, false, config, null);
|
|
23291
23639
|
ensureDir(coldPath);
|
|
23292
23640
|
fs.writeFileSync(coldPath, coldContent, 'utf8');
|
|
23641
|
+
pruneStaleContextSplits(cwd, ['context-cold.md']);
|
|
23293
23642
|
const coldTokens = estimateTokens(coldContent);
|
|
23294
23643
|
|
|
23295
23644
|
console.warn('[sigmap] hot-cold:');
|
|
@@ -23614,6 +23963,9 @@ function runGenerate(cwd, config, reportMode, reportJson = false) {
|
|
|
23614
23963
|
result = runHotColdStrategy(cwd, configWithBudget, fileEntries, recentFiles, inputTokenTotal);
|
|
23615
23964
|
} else {
|
|
23616
23965
|
// 'full' — original behaviour
|
|
23966
|
+
// 'full' writes no split files — anything left from a previous
|
|
23967
|
+
// strategy would still be merged into the index at read time (#555).
|
|
23968
|
+
pruneStaleContextSplits(cwd, []);
|
|
23617
23969
|
fileEntries = applyTokenBudget(fileEntries, effectiveMaxTokens);
|
|
23618
23970
|
const droppedCount = beforeCount - fileEntries.length;
|
|
23619
23971
|
const routingEnabled = !!(config.routing || process.argv.includes('--routing'));
|
|
@@ -26849,23 +27201,16 @@ function main() {
|
|
|
26849
27201
|
process.exit(1);
|
|
26850
27202
|
}
|
|
26851
27203
|
|
|
26852
|
-
|
|
26853
|
-
|
|
26854
|
-
|
|
26855
|
-
|
|
26856
|
-
'.swift': 'swift', '.dart': 'dart', '.scala': 'scala',
|
|
26857
|
-
'.r': 'r', '.R': 'r',
|
|
26858
|
-
'.vue': 'vue_sfc', '.svelte': 'svelte', '.html': 'html',
|
|
26859
|
-
'.css': 'css', '.yml': 'yaml', '.sh': 'shell',
|
|
26860
|
-
};
|
|
26861
|
-
const SPECIAL = { 'Dockerfile': 'dockerfile' };
|
|
27204
|
+
// Resolution goes through the dispatcher — the single source of truth
|
|
27205
|
+
// (#591). This map was a third copy, and it still pointed at `vue.js`
|
|
27206
|
+
// after that module was deleted, which is how a dead extractor survived.
|
|
27207
|
+
const { langFor } = requireSourceOrBundled('./src/extractors/dispatch');
|
|
26862
27208
|
|
|
26863
27209
|
let passed = 0; let failed = 0;
|
|
26864
27210
|
const entries = fs.readdirSync(fixturesDir).sort();
|
|
26865
27211
|
|
|
26866
27212
|
for (const filename of entries) {
|
|
26867
|
-
const
|
|
26868
|
-
const lang = EXT_TO_LANG[ext] || SPECIAL[filename];
|
|
27213
|
+
const lang = langFor(filename);
|
|
26869
27214
|
if (!lang) continue;
|
|
26870
27215
|
|
|
26871
27216
|
const fixturePath = path.join(fixturesDir, filename);
|