sigmap 3.3.4 → 3.5.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 +600 -17
- package/CHANGELOG.md +39 -0
- package/README.md +3 -1
- package/gen-context.js +474 -6
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/core/index.js +15 -0
- package/packages/core/package.json +1 -1
- package/src/config/defaults.js +4 -0
- package/src/config/loader.js +2 -1
- package/src/eval/analyzer.js +7 -0
- package/src/extractors/markdown.js +30 -0
- package/src/extractors/patterns.js +135 -0
- package/src/extractors/properties.js +37 -0
- package/src/extractors/python_dataclass.js +77 -0
- package/src/extractors/toml.js +42 -0
- package/src/extractors/typescript_react.js +60 -0
- package/src/extractors/vue_sfc.js +99 -0
- package/src/extractors/xml.js +46 -0
- package/src/mcp/server.js +1 -1
package/gen-context.js
CHANGED
|
@@ -2000,6 +2000,464 @@ __factories["./src/extractors/terraform"] = function(module, exports) {
|
|
|
2000
2000
|
|
|
2001
2001
|
};
|
|
2002
2002
|
|
|
2003
|
+
// ── ./src/extractors/toml ──
|
|
2004
|
+
__factories["./src/extractors/toml"] = function(module, exports) {
|
|
2005
|
+
|
|
2006
|
+
'use strict';
|
|
2007
|
+
|
|
2008
|
+
/**
|
|
2009
|
+
* Extract signatures from TOML configuration files.
|
|
2010
|
+
* Focuses on section/table names and high-value keys.
|
|
2011
|
+
*
|
|
2012
|
+
* @param {string} src - Raw TOML content
|
|
2013
|
+
* @returns {string[]} Array of signature strings
|
|
2014
|
+
*/
|
|
2015
|
+
function extract(src) {
|
|
2016
|
+
if (!src || typeof src !== 'string') return [];
|
|
2017
|
+
const sigs = [];
|
|
2018
|
+
|
|
2019
|
+
// Remove # comments while preserving values before comment markers.
|
|
2020
|
+
const stripped = src.replace(/\s+#.*$/gm, '');
|
|
2021
|
+
|
|
2022
|
+
// [section] and [[array.section]]
|
|
2023
|
+
for (const m of stripped.matchAll(/^\s*(\[\[?[^\]]+\]\]?)\s*$/gm)) {
|
|
2024
|
+
sigs.push(`table ${m[1].trim()}`);
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
// Key-value lines (top-level and nested) — keep key names only.
|
|
2028
|
+
for (const m of stripped.matchAll(/^\s*([A-Za-z0-9_.-]+)\s*=\s*(.+)$/gm)) {
|
|
2029
|
+
const key = m[1].trim();
|
|
2030
|
+
const value = m[2].trim();
|
|
2031
|
+
|
|
2032
|
+
// Prefer common metadata/config keys for compact, useful output.
|
|
2033
|
+
if (/^(name|version|description|authors|license|requires-python|dependencies|optional-dependencies|scripts|tool\.|build-system\.|project\.)/.test(key)) {
|
|
2034
|
+
sigs.push(`key ${key}`);
|
|
2035
|
+
continue;
|
|
2036
|
+
}
|
|
2037
|
+
|
|
2038
|
+
// Include booleans and simple scalar keys as generic config signal.
|
|
2039
|
+
if (/^(true|false|"[^"]*"|'[^']*'|[0-9._-]+)$/.test(value)) {
|
|
2040
|
+
sigs.push(`key ${key}`);
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
|
|
2044
|
+
return Array.from(new Set(sigs)).slice(0, 40);
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
module.exports = { extract };
|
|
2048
|
+
|
|
2049
|
+
};
|
|
2050
|
+
|
|
2051
|
+
// ── ./src/extractors/properties ──
|
|
2052
|
+
__factories["./src/extractors/properties"] = function(module, exports) {
|
|
2053
|
+
|
|
2054
|
+
'use strict';
|
|
2055
|
+
|
|
2056
|
+
/**
|
|
2057
|
+
* Extract signatures from .properties configuration files.
|
|
2058
|
+
* Captures key names, grouped by prefixes where possible.
|
|
2059
|
+
*
|
|
2060
|
+
* @param {string} src - Raw properties content
|
|
2061
|
+
* @returns {string[]} Array of signature strings
|
|
2062
|
+
*/
|
|
2063
|
+
function extract(src) {
|
|
2064
|
+
if (!src || typeof src !== 'string') return [];
|
|
2065
|
+
const sigs = [];
|
|
2066
|
+
|
|
2067
|
+
const lines = src.split(/\r?\n/);
|
|
2068
|
+
for (const line of lines) {
|
|
2069
|
+
const trimmed = line.trim();
|
|
2070
|
+
if (!trimmed || trimmed.startsWith('#') || trimmed.startsWith('!')) continue;
|
|
2071
|
+
|
|
2072
|
+
const idxEq = trimmed.indexOf('=');
|
|
2073
|
+
const idxColon = trimmed.indexOf(':');
|
|
2074
|
+
const idx = idxEq >= 0 && idxColon >= 0 ? Math.min(idxEq, idxColon) : Math.max(idxEq, idxColon);
|
|
2075
|
+
if (idx <= 0) continue;
|
|
2076
|
+
|
|
2077
|
+
const key = trimmed.slice(0, idx).trim();
|
|
2078
|
+
if (!key) continue;
|
|
2079
|
+
|
|
2080
|
+
const parts = key.split('.').filter(Boolean);
|
|
2081
|
+
if (parts.length >= 2) {
|
|
2082
|
+
sigs.push(`group ${parts[0]}.${parts[1]}`);
|
|
2083
|
+
}
|
|
2084
|
+
sigs.push(`key ${key}`);
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
return Array.from(new Set(sigs)).slice(0, 50);
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
module.exports = { extract };
|
|
2091
|
+
|
|
2092
|
+
};
|
|
2093
|
+
|
|
2094
|
+
// ── ./src/extractors/xml ──
|
|
2095
|
+
__factories["./src/extractors/xml"] = function(module, exports) {
|
|
2096
|
+
|
|
2097
|
+
'use strict';
|
|
2098
|
+
|
|
2099
|
+
/**
|
|
2100
|
+
* Lightweight XML config extractor.
|
|
2101
|
+
* Captures root tags, key config tags, and id/name/class attributes.
|
|
2102
|
+
*
|
|
2103
|
+
* @param {string} src - Raw XML content
|
|
2104
|
+
* @returns {string[]} Array of signature strings
|
|
2105
|
+
*/
|
|
2106
|
+
function extract(src) {
|
|
2107
|
+
if (!src || typeof src !== 'string') return [];
|
|
2108
|
+
const sigs = [];
|
|
2109
|
+
|
|
2110
|
+
// Remove comments and XML declaration for simpler scanning.
|
|
2111
|
+
const stripped = src
|
|
2112
|
+
.replace(/<!--[\s\S]*?-->/g, '')
|
|
2113
|
+
.replace(/<\?xml[\s\S]*?\?>/gi, '');
|
|
2114
|
+
|
|
2115
|
+
// Root element (first opening tag).
|
|
2116
|
+
const root = stripped.match(/<\s*([A-Za-z_][\w:.-]*)\b[^>]*>/);
|
|
2117
|
+
if (root) sigs.push(`root ${root[1]}`);
|
|
2118
|
+
|
|
2119
|
+
// High-value config-like tags.
|
|
2120
|
+
const tagRe = /<\s*([A-Za-z_][\w:.-]*)\b([^>]*)>/g;
|
|
2121
|
+
for (const m of stripped.matchAll(tagRe)) {
|
|
2122
|
+
const tag = m[1];
|
|
2123
|
+
const attrs = m[2] || '';
|
|
2124
|
+
|
|
2125
|
+
if (/^(bean|beans|route|routes|property|properties|dependency|dependencies|plugin|plugins|configuration|settings|profile|profiles|module|modules)$/i.test(tag)) {
|
|
2126
|
+
sigs.push(`tag ${tag}`);
|
|
2127
|
+
}
|
|
2128
|
+
|
|
2129
|
+
const id = attrs.match(/\bid\s*=\s*"([^"]+)"/i);
|
|
2130
|
+
if (id) sigs.push(`${tag}#${id[1]}`);
|
|
2131
|
+
|
|
2132
|
+
const name = attrs.match(/\bname\s*=\s*"([^"]+)"/i);
|
|
2133
|
+
if (name) sigs.push(`${tag}[name=${name[1]}]`);
|
|
2134
|
+
|
|
2135
|
+
const cls = attrs.match(/\bclass\s*=\s*"([^"]+)"/i);
|
|
2136
|
+
if (cls) sigs.push(`${tag} -> ${cls[1]}`);
|
|
2137
|
+
}
|
|
2138
|
+
|
|
2139
|
+
return Array.from(new Set(sigs)).slice(0, 50);
|
|
2140
|
+
}
|
|
2141
|
+
|
|
2142
|
+
module.exports = { extract };
|
|
2143
|
+
|
|
2144
|
+
};
|
|
2145
|
+
|
|
2146
|
+
// ── ./src/extractors/markdown ──
|
|
2147
|
+
__factories["./src/extractors/markdown"] = function(module, exports) {
|
|
2148
|
+
|
|
2149
|
+
'use strict';
|
|
2150
|
+
|
|
2151
|
+
/**
|
|
2152
|
+
* Lightweight markdown technical indexer.
|
|
2153
|
+
* Captures headings and fenced code block language hints only.
|
|
2154
|
+
*
|
|
2155
|
+
* @param {string} src - Raw markdown content
|
|
2156
|
+
* @returns {string[]} Array of signature strings
|
|
2157
|
+
*/
|
|
2158
|
+
function extract(src) {
|
|
2159
|
+
if (!src || typeof src !== 'string') return [];
|
|
2160
|
+
const sigs = [];
|
|
2161
|
+
|
|
2162
|
+
// Headings: # .. ######
|
|
2163
|
+
for (const m of src.matchAll(/^(#{1,6})\s+(.+)$/gm)) {
|
|
2164
|
+
const level = m[1].length;
|
|
2165
|
+
const title = m[2].trim().replace(/\s+/g, ' ');
|
|
2166
|
+
if (title) sigs.push(`h${level} ${title}`);
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
// Fenced code blocks: ```lang
|
|
2170
|
+
for (const m of src.matchAll(/^```\s*([A-Za-z0-9_+-]*)\s*$/gm)) {
|
|
2171
|
+
const lang = m[1] ? m[1].toLowerCase() : 'plain';
|
|
2172
|
+
sigs.push(`code-fence ${lang}`);
|
|
2173
|
+
}
|
|
2174
|
+
|
|
2175
|
+
return Array.from(new Set(sigs)).slice(0, 40);
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
module.exports = { extract };
|
|
2179
|
+
|
|
2180
|
+
};
|
|
2181
|
+
|
|
2182
|
+
// ── ./src/extractors/typescript_react ──
|
|
2183
|
+
__factories["./src/extractors/typescript_react"] = function(module, exports) {
|
|
2184
|
+
|
|
2185
|
+
'use strict';
|
|
2186
|
+
|
|
2187
|
+
function extract(src) {
|
|
2188
|
+
if (!src || typeof src !== 'string') return [];
|
|
2189
|
+
const sigs = [];
|
|
2190
|
+
const stripped = src
|
|
2191
|
+
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
2192
|
+
.replace(/\/\/.*$/gm, '');
|
|
2193
|
+
const compRe = /(?:export\s+)?(?:const|function)\s+([A-Z]\w*)\s*(?:<[^>]*>)?\s*\(\s*(?:props|{\s*[^}]*})?/g;
|
|
2194
|
+
for (const m of stripped.matchAll(compRe)) {
|
|
2195
|
+
sigs.push(`component ${m[1]}`);
|
|
2196
|
+
}
|
|
2197
|
+
const propsRe = /interface\s+(\w*Props)\s*(?:<[^>]*>)?\s*{/g;
|
|
2198
|
+
for (const m of stripped.matchAll(propsRe)) {
|
|
2199
|
+
sigs.push(`props ${m[1]}`);
|
|
2200
|
+
}
|
|
2201
|
+
const hookRe = /use([A-Z]\w*)\s*(?:<[^>]*>)?\s*\(/g;
|
|
2202
|
+
const hooks = new Set();
|
|
2203
|
+
for (const m of stripped.matchAll(hookRe)) {
|
|
2204
|
+
hooks.add(m[1]);
|
|
2205
|
+
}
|
|
2206
|
+
for (const h of hooks) {
|
|
2207
|
+
sigs.push(`hook use${h}`);
|
|
2208
|
+
}
|
|
2209
|
+
const exportRe = /export\s+(?:const|function|default|interface|type)\s+([A-Z]\w*)/g;
|
|
2210
|
+
for (const m of stripped.matchAll(exportRe)) {
|
|
2211
|
+
sigs.push(`export ${m[1]}`);
|
|
2212
|
+
}
|
|
2213
|
+
const handlerRe = /on([A-Z]\w+)\s*=\s*{?\s*\(?[a-zA-Z_$]/g;
|
|
2214
|
+
const handlers = new Set();
|
|
2215
|
+
for (const m of stripped.matchAll(handlerRe)) {
|
|
2216
|
+
handlers.add(m[1]);
|
|
2217
|
+
}
|
|
2218
|
+
for (const h of handlers) {
|
|
2219
|
+
sigs.push(`handler on${h}`);
|
|
2220
|
+
}
|
|
2221
|
+
return Array.from(new Set(sigs)).slice(0, 50);
|
|
2222
|
+
}
|
|
2223
|
+
|
|
2224
|
+
module.exports = { extract };
|
|
2225
|
+
|
|
2226
|
+
};
|
|
2227
|
+
|
|
2228
|
+
// ── ./src/extractors/vue_sfc ──
|
|
2229
|
+
__factories["./src/extractors/vue_sfc"] = function(module, exports) {
|
|
2230
|
+
|
|
2231
|
+
'use strict';
|
|
2232
|
+
|
|
2233
|
+
function extract(src) {
|
|
2234
|
+
if (!src || typeof src !== 'string') return [];
|
|
2235
|
+
const sigs = [];
|
|
2236
|
+
const scriptMatch = src.match(/<script[^>]*>([\s\S]*?)<\/script>/);
|
|
2237
|
+
if (!scriptMatch) return sigs;
|
|
2238
|
+
const script = scriptMatch[1];
|
|
2239
|
+
const nameRe = /(?:name\s*:\s*['"`]([a-zA-Z0-9]+)['"`]|export\s+default\s+defineComponent\s*\(\s*{\s*name\s*:\s*['"`]([a-zA-Z0-9]+)['"`])/;
|
|
2240
|
+
const nameMatch = script.match(nameRe);
|
|
2241
|
+
if (nameMatch) {
|
|
2242
|
+
sigs.push(`component ${nameMatch[1] || nameMatch[2]}`);
|
|
2243
|
+
}
|
|
2244
|
+
const propsRe = /props\s*:\s*{([^}]*)}/;
|
|
2245
|
+
const propsMatch = script.match(propsRe);
|
|
2246
|
+
if (propsMatch) {
|
|
2247
|
+
const propLines = propsMatch[1].split(',');
|
|
2248
|
+
for (const line of propLines) {
|
|
2249
|
+
const propName = line.trim().match(/([a-zA-Z_$]\w*)/);
|
|
2250
|
+
if (propName) sigs.push(`prop ${propName[1]}`);
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
const definePropsRe = /defineProps\s*(?:<([^>]+)>)?\s*\(/;
|
|
2254
|
+
if (definePropsRe.test(script)) {
|
|
2255
|
+
sigs.push('props composition-api');
|
|
2256
|
+
}
|
|
2257
|
+
const emitsRe = /emits\s*:\s*\[([^\]]+)\]/;
|
|
2258
|
+
const emitsMatch = script.match(emitsRe);
|
|
2259
|
+
if (emitsMatch) {
|
|
2260
|
+
const emitNames = emitsMatch[1].split(',').map(e => e.trim().replace(/['"`]/g, ''));
|
|
2261
|
+
for (const e of emitNames) {
|
|
2262
|
+
if (e) sigs.push(`emit ${e}`);
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
const defineEmitsRe = /defineEmits\s*(?:<([^>]+)>)?\s*\(/;
|
|
2266
|
+
if (defineEmitsRe.test(script)) {
|
|
2267
|
+
sigs.push('emits composition-api');
|
|
2268
|
+
}
|
|
2269
|
+
const lifecycleHooks = ['setup', 'created', 'mounted', 'updated', 'unmounted'];
|
|
2270
|
+
for (const hook of lifecycleHooks) {
|
|
2271
|
+
if (new RegExp(`\\b${hook}\\s*\\(`).test(script)) {
|
|
2272
|
+
sigs.push(`lifecycle ${hook}`);
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
const namedSlotRe = /<slot\s+name=['"]([a-zA-Z_$]\w*)['"][^>]*>/g;
|
|
2276
|
+
const templateSlotRe = /<template\s+#([a-zA-Z_$]\w*)|v-slot:([a-zA-Z_$]\w*)/g;
|
|
2277
|
+
const slots = new Set();
|
|
2278
|
+
for (const m of src.matchAll(namedSlotRe)) {
|
|
2279
|
+
if (m[1]) slots.add(m[1]);
|
|
2280
|
+
}
|
|
2281
|
+
for (const m of src.matchAll(templateSlotRe)) {
|
|
2282
|
+
const slotName = m[1] || m[2];
|
|
2283
|
+
if (slotName) slots.add(slotName);
|
|
2284
|
+
}
|
|
2285
|
+
for (const s of slots) {
|
|
2286
|
+
sigs.push(`slot ${s}`);
|
|
2287
|
+
}
|
|
2288
|
+
return Array.from(new Set(sigs)).slice(0, 50);
|
|
2289
|
+
}
|
|
2290
|
+
|
|
2291
|
+
module.exports = { extract };
|
|
2292
|
+
|
|
2293
|
+
};
|
|
2294
|
+
|
|
2295
|
+
// ── ./src/extractors/python_dataclass ──
|
|
2296
|
+
__factories["./src/extractors/python_dataclass"] = function(module, exports) {
|
|
2297
|
+
|
|
2298
|
+
'use strict';
|
|
2299
|
+
|
|
2300
|
+
function extract(src) {
|
|
2301
|
+
if (!src || typeof src !== 'string') return [];
|
|
2302
|
+
const sigs = [];
|
|
2303
|
+
const dataclassRe = /@dataclass(?:\([^)]*\))?[\s\n]+class\s+([A-Z]\w*)/g;
|
|
2304
|
+
for (const m of src.matchAll(dataclassRe)) {
|
|
2305
|
+
sigs.push(`dataclass ${m[1]}`);
|
|
2306
|
+
}
|
|
2307
|
+
const pydanticRe = /class\s+([A-Z]\w*)\s*\([^)]*BaseModel[^)]*\)/g;
|
|
2308
|
+
for (const m of src.matchAll(pydanticRe)) {
|
|
2309
|
+
sigs.push(`model ${m[1]}`);
|
|
2310
|
+
}
|
|
2311
|
+
if (/model_validate|field_validator|computed_field/.test(src)) {
|
|
2312
|
+
sigs.push('pydantic v2+');
|
|
2313
|
+
}
|
|
2314
|
+
const sqlalchemyRe = /class\s+([A-Z]\w*)\s*\([^)]*(?:Base|declarative_base)[^)]*\)/g;
|
|
2315
|
+
for (const m of src.matchAll(sqlalchemyRe)) {
|
|
2316
|
+
sigs.push(`orm ${m[1]}`);
|
|
2317
|
+
}
|
|
2318
|
+
const fieldRe = /^\s+([a-z_]\w*)\s*:\s*([A-Z]\w*|List|Dict|Optional|Union)[^=]*/gm;
|
|
2319
|
+
const fields = new Set();
|
|
2320
|
+
for (const m of src.matchAll(fieldRe)) {
|
|
2321
|
+
if (fields.size < 15) fields.add(m[1]);
|
|
2322
|
+
}
|
|
2323
|
+
for (const f of fields) {
|
|
2324
|
+
sigs.push(`field ${f}`);
|
|
2325
|
+
}
|
|
2326
|
+
const columnRe = /([a-z_]\w*)\s*=\s*Column\s*\([^)]*\)/g;
|
|
2327
|
+
for (const m of src.matchAll(columnRe)) {
|
|
2328
|
+
sigs.push(`column ${m[1]}`);
|
|
2329
|
+
}
|
|
2330
|
+
const relRe = /(?:ForeignKey|relationship)\s*\(\s*['"]([a-zA-Z_]\w*)['"]/g;
|
|
2331
|
+
for (const m of src.matchAll(relRe)) {
|
|
2332
|
+
sigs.push(`relation ${m[1]}`);
|
|
2333
|
+
}
|
|
2334
|
+
const validatorRe = /@(?:validator|field_validator)\s*\(\s*['"]?([a-z_]\w*)(?:['"]|,|\s|\))/g;
|
|
2335
|
+
const validators = new Set();
|
|
2336
|
+
for (const m of src.matchAll(validatorRe)) {
|
|
2337
|
+
validators.add(m[1]);
|
|
2338
|
+
}
|
|
2339
|
+
for (const v of validators) {
|
|
2340
|
+
sigs.push(`validator ${v}`);
|
|
2341
|
+
}
|
|
2342
|
+
if (/class\s+Config\s*:/.test(src)) {
|
|
2343
|
+
sigs.push('config-class');
|
|
2344
|
+
}
|
|
2345
|
+
return Array.from(new Set(sigs)).slice(0, 50);
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2348
|
+
module.exports = { extract };
|
|
2349
|
+
|
|
2350
|
+
};
|
|
2351
|
+
|
|
2352
|
+
// ── ./src/extractors/patterns ──
|
|
2353
|
+
__factories["./src/extractors/patterns"] = function(module, exports) {
|
|
2354
|
+
|
|
2355
|
+
'use strict';
|
|
2356
|
+
|
|
2357
|
+
function extract(src) {
|
|
2358
|
+
if (!src || typeof src !== 'string') return [];
|
|
2359
|
+
const sigs = [];
|
|
2360
|
+
|
|
2361
|
+
// Service container / factory patterns
|
|
2362
|
+
const containerRe = /(?:class|function|const)\s+([A-Z]\w*(?:Container|Factory|Registry|Provider|Injector))\b/g;
|
|
2363
|
+
for (const m of src.matchAll(containerRe)) {
|
|
2364
|
+
sigs.push(`di-container ${m[1]}`);
|
|
2365
|
+
}
|
|
2366
|
+
|
|
2367
|
+
// Service decorators: @Injectable, @Service, @Singleton, @Provide
|
|
2368
|
+
const decoratorRe = /@(?:Injectable|Service|Singleton|Provide|Module|Component)\s*(?:\([^)]*\))?\s*(?:class|export\s+class|const\s+)\s+([A-Z]\w*)/g;
|
|
2369
|
+
for (const m of src.matchAll(decoratorRe)) {
|
|
2370
|
+
sigs.push(`service-decorated ${m[1]}`);
|
|
2371
|
+
}
|
|
2372
|
+
|
|
2373
|
+
// Dependency injection via constructor: constructor(private readonly ...: Service)
|
|
2374
|
+
const ctorDiRe = /constructor\s*\([^)]*(?:private|protected)?\s+(?:readonly\s+)?([a-z_]\w*)\s*:\s*([A-Z]\w*)/g;
|
|
2375
|
+
const diServices = new Set();
|
|
2376
|
+
for (const m of src.matchAll(ctorDiRe)) {
|
|
2377
|
+
diServices.add(m[1]);
|
|
2378
|
+
}
|
|
2379
|
+
if (diServices.size > 0) {
|
|
2380
|
+
sigs.push(`di-injection ${diServices.size} params`);
|
|
2381
|
+
}
|
|
2382
|
+
|
|
2383
|
+
// Repository pattern: extends Repository, implements IRepository
|
|
2384
|
+
const repoRe = /class\s+([A-Z]\w*(?:Repository|Repo|DataAccess))\b/g;
|
|
2385
|
+
for (const m of src.matchAll(repoRe)) {
|
|
2386
|
+
sigs.push(`repo ${m[1]}`);
|
|
2387
|
+
}
|
|
2388
|
+
|
|
2389
|
+
// Service layer: @Service or ServiceImpl pattern
|
|
2390
|
+
const serviceRe = /(?:export\s+)?class\s+([A-Z]\w*Service\b)/g;
|
|
2391
|
+
for (const m of src.matchAll(serviceRe)) {
|
|
2392
|
+
sigs.push(`service ${m[1]}`);
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2395
|
+
// Middleware detection: app.use(), router.use(), middleware function
|
|
2396
|
+
if (/app\.use\s*\(|router\.use\s*\(|\.use\s*\(\s*function|middleware|app\.get\s*\(\s*['"`]\/[^'"`]*['"`]/.test(src)) {
|
|
2397
|
+
sigs.push('middleware-present');
|
|
2398
|
+
}
|
|
2399
|
+
|
|
2400
|
+
// Export type/interface followed by class implementing it
|
|
2401
|
+
const exportTypeRe = /export\s+(?:type|interface)\s+([A-Z]\w*)/g;
|
|
2402
|
+
const exportedTypes = new Set();
|
|
2403
|
+
for (const m of src.matchAll(exportTypeRe)) {
|
|
2404
|
+
exportedTypes.add(m[1]);
|
|
2405
|
+
}
|
|
2406
|
+
|
|
2407
|
+
// Check if exported types have implementations
|
|
2408
|
+
for (const type of exportedTypes) {
|
|
2409
|
+
const implRe = new RegExp(`class\\s+([A-Z]\\w*)\\s+(?:extends|implements)\\s+(?:.*\\s+)?${type}\\b`, 'i');
|
|
2410
|
+
if (implRe.test(src)) {
|
|
2411
|
+
sigs.push(`type-impl ${type}`);
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
|
|
2415
|
+
// Unchecked nulls / optional without validation
|
|
2416
|
+
if (/\?\s*{|Optional\s*<|\?\s*\.get\(\)|\?\s*\[|\?\s*\.length|\.split\(\)\.filter\(Boolean\)|if\s*\(\s*!.*\).*throw/.test(src)) {
|
|
2417
|
+
sigs.push('unsafe-null-check');
|
|
2418
|
+
}
|
|
2419
|
+
|
|
2420
|
+
// Missing validation (direct use of user input)
|
|
2421
|
+
const userInputRe = /(?:request|input|params|body|query|args)\s*\[\s*['"][^'"]*['"]\s*\]|req(?:uest)?\..*\s*==|params\.split|String\(.*\)\.toLowerCase/;
|
|
2422
|
+
if (userInputRe.test(src)) {
|
|
2423
|
+
sigs.push('unsafe-input-validation');
|
|
2424
|
+
}
|
|
2425
|
+
|
|
2426
|
+
// Weak error handling (catch {} or empty catch)
|
|
2427
|
+
if (/catch\s*\(\s*\)\s*\{|\}\s*catch\s*\{(\s*\/\/|\s*\})/.test(src)) {
|
|
2428
|
+
sigs.push('weak-error-handling');
|
|
2429
|
+
}
|
|
2430
|
+
|
|
2431
|
+
// Direct error exposure
|
|
2432
|
+
if (/throw\s+new\s+Error\(|console\s*\.\s*error|res\.status\(500\)\.send\(err\)/.test(src)) {
|
|
2433
|
+
sigs.push('unsafe-error-exposure');
|
|
2434
|
+
}
|
|
2435
|
+
|
|
2436
|
+
// Heavy imports
|
|
2437
|
+
const importRe = /(?:import|require)\s+(?:{[^}]*}|[a-zA-Z_]\w*)\s+from\s+['"]\.?\.?\/[^'"]+['"]/g;
|
|
2438
|
+
const importCount = (src.match(importRe) || []).length;
|
|
2439
|
+
if (importCount > 5) {
|
|
2440
|
+
sigs.push(`heavy-imports ${importCount}`);
|
|
2441
|
+
}
|
|
2442
|
+
|
|
2443
|
+
// Controller/Handler layer
|
|
2444
|
+
const controllerRe = /(?:class|export)\s+([A-Z]\w*(?:Controller|Handler|Route))\b/g;
|
|
2445
|
+
for (const m of src.matchAll(controllerRe)) {
|
|
2446
|
+
sigs.push(`controller ${m[1]}`);
|
|
2447
|
+
}
|
|
2448
|
+
|
|
2449
|
+
// Use case / Domain logic
|
|
2450
|
+
if (/UseCase|Command|Query|UseCase\b|Interactor/.test(src)) {
|
|
2451
|
+
sigs.push('domain-usecase');
|
|
2452
|
+
}
|
|
2453
|
+
|
|
2454
|
+
return Array.from(new Set(sigs)).slice(0, 60);
|
|
2455
|
+
}
|
|
2456
|
+
|
|
2457
|
+
module.exports = { extract };
|
|
2458
|
+
|
|
2459
|
+
};
|
|
2460
|
+
|
|
2003
2461
|
// ── ./src/extractors/deps ──
|
|
2004
2462
|
__factories["./src/extractors/deps"] = function(module, exports) {
|
|
2005
2463
|
|
|
@@ -4020,7 +4478,7 @@ __factories["./src/mcp/server"] = function(module, exports) {
|
|
|
4020
4478
|
|
|
4021
4479
|
const SERVER_INFO = {
|
|
4022
4480
|
name: 'sigmap',
|
|
4023
|
-
version: '3.
|
|
4481
|
+
version: '3.5.0',
|
|
4024
4482
|
description: 'SigMap MCP server — code signatures on demand',
|
|
4025
4483
|
};
|
|
4026
4484
|
|
|
@@ -4919,7 +5377,7 @@ __factories["./src/eval/analyzer"] = function(module, exports) {
|
|
|
4919
5377
|
const path = require('path');
|
|
4920
5378
|
|
|
4921
5379
|
const EXT_MAP = {
|
|
4922
|
-
'.ts': 'typescript', '.tsx': '
|
|
5380
|
+
'.ts': 'typescript', '.tsx': 'typescript_react',
|
|
4923
5381
|
'.js': 'javascript', '.jsx': 'javascript', '.mjs': 'javascript', '.cjs': 'javascript',
|
|
4924
5382
|
'.py': 'python', '.pyw': 'python',
|
|
4925
5383
|
'.java': 'java',
|
|
@@ -4933,7 +5391,7 @@ __factories["./src/eval/analyzer"] = function(module, exports) {
|
|
|
4933
5391
|
'.swift': 'swift',
|
|
4934
5392
|
'.dart': 'dart',
|
|
4935
5393
|
'.scala': 'scala', '.sc': 'scala',
|
|
4936
|
-
'.vue': '
|
|
5394
|
+
'.vue': 'vue_sfc',
|
|
4937
5395
|
'.svelte': 'svelte',
|
|
4938
5396
|
'.html': 'html', '.htm': 'html',
|
|
4939
5397
|
'.css': 'css', '.scss': 'css', '.sass': 'css', '.less': 'css',
|
|
@@ -4944,6 +5402,11 @@ __factories["./src/eval/analyzer"] = function(module, exports) {
|
|
|
4944
5402
|
'.graphql': 'graphql', '.gql': 'graphql',
|
|
4945
5403
|
'.tf': 'terraform', '.tfvars': 'terraform',
|
|
4946
5404
|
'.proto': 'protobuf',
|
|
5405
|
+
// Phase A formats
|
|
5406
|
+
'.toml': 'toml',
|
|
5407
|
+
'.properties': 'properties',
|
|
5408
|
+
'.xml': 'xml',
|
|
5409
|
+
'.md': 'markdown',
|
|
4947
5410
|
};
|
|
4948
5411
|
|
|
4949
5412
|
function isDockerfile(name) { return name === 'Dockerfile' || name.startsWith('Dockerfile.'); }
|
|
@@ -5430,7 +5893,7 @@ const path = require('path');
|
|
|
5430
5893
|
const os = require('os');
|
|
5431
5894
|
const { execSync } = require('child_process');
|
|
5432
5895
|
|
|
5433
|
-
const VERSION = '3.
|
|
5896
|
+
const VERSION = '3.5.0';
|
|
5434
5897
|
const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
|
|
5435
5898
|
|
|
5436
5899
|
function requireSourceOrBundled(key) {
|
|
@@ -5452,7 +5915,7 @@ const { DEFAULTS } = requireSourceOrBundled('./src/config/defaults');
|
|
|
5452
5915
|
// Language → extractor mapping (by file extension)
|
|
5453
5916
|
// ---------------------------------------------------------------------------
|
|
5454
5917
|
const EXT_MAP = {
|
|
5455
|
-
'.ts': 'typescript', '.tsx': '
|
|
5918
|
+
'.ts': 'typescript', '.tsx': 'typescript_react',
|
|
5456
5919
|
'.js': 'javascript', '.jsx': 'javascript', '.mjs': 'javascript', '.cjs': 'javascript',
|
|
5457
5920
|
'.py': 'python', '.pyw': 'python',
|
|
5458
5921
|
'.java': 'java',
|
|
@@ -5466,7 +5929,7 @@ const EXT_MAP = {
|
|
|
5466
5929
|
'.swift': 'swift',
|
|
5467
5930
|
'.dart': 'dart',
|
|
5468
5931
|
'.scala': 'scala', '.sc': 'scala',
|
|
5469
|
-
'.vue': '
|
|
5932
|
+
'.vue': 'vue_sfc',
|
|
5470
5933
|
'.svelte': 'svelte',
|
|
5471
5934
|
'.html': 'html', '.htm': 'html',
|
|
5472
5935
|
'.css': 'css', '.scss': 'css', '.sass': 'css', '.less': 'css',
|
|
@@ -5477,6 +5940,11 @@ const EXT_MAP = {
|
|
|
5477
5940
|
'.graphql': 'graphql', '.gql': 'graphql',
|
|
5478
5941
|
'.tf': 'terraform', '.tfvars': 'terraform',
|
|
5479
5942
|
'.proto': 'protobuf',
|
|
5943
|
+
// Phase A formats
|
|
5944
|
+
'.toml': 'toml',
|
|
5945
|
+
'.properties': 'properties',
|
|
5946
|
+
'.xml': 'xml',
|
|
5947
|
+
'.md': 'markdown',
|
|
5480
5948
|
};
|
|
5481
5949
|
|
|
5482
5950
|
// Dockerfile handled separately (no extension)
|
package/package.json
CHANGED
package/packages/core/index.js
CHANGED
|
@@ -40,6 +40,21 @@ const EXT_MAP = {
|
|
|
40
40
|
'.graphql': 'graphql', '.gql': 'graphql',
|
|
41
41
|
'.tf': 'terraform', '.tfvars': 'terraform',
|
|
42
42
|
'.proto': 'protobuf',
|
|
43
|
+
// Phase A formats
|
|
44
|
+
'.toml': 'toml',
|
|
45
|
+
'.properties': 'properties',
|
|
46
|
+
'.xml': 'xml',
|
|
47
|
+
'.md': 'markdown',
|
|
48
|
+
// Phase C specialized extractors
|
|
49
|
+
'.tsx': 'typescript_react',
|
|
50
|
+
'.vue': 'vue_sfc',
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// Phase C fallback: also try specialized extractors for base extensions
|
|
54
|
+
const PHASE_C_EXTRACTORS = {
|
|
55
|
+
'typescript_react': '.tsx',
|
|
56
|
+
'vue_sfc': '.vue',
|
|
57
|
+
'python_dataclass': '.py',
|
|
43
58
|
};
|
|
44
59
|
|
|
45
60
|
const SRC_ROOT = path.resolve(__dirname, '..', '..', 'src');
|
package/src/config/defaults.js
CHANGED
|
@@ -22,6 +22,10 @@ const DEFAULTS = {
|
|
|
22
22
|
'server', 'client', 'web', 'frontend', 'backend',
|
|
23
23
|
'desktop', 'mobile', 'shared', 'common', 'core',
|
|
24
24
|
'workers', 'functions', 'lambda', 'cmd',
|
|
25
|
+
// framework convention folders
|
|
26
|
+
'pages', 'components', 'hooks', 'routes', 'controllers',
|
|
27
|
+
'models', 'views', 'resources', 'config', 'db',
|
|
28
|
+
'projects', 'apps', 'libs', 'instance', 'blueprints',
|
|
25
29
|
],
|
|
26
30
|
|
|
27
31
|
// Directory/file names to exclude entirely
|
package/src/config/loader.js
CHANGED
|
@@ -26,6 +26,7 @@ const SUPPORTED_CODE_EXTS = new Set([
|
|
|
26
26
|
'.html', '.htm', '.css', '.scss', '.sass', '.less',
|
|
27
27
|
'.yml', '.yaml', '.sh', '.bash', '.zsh', '.fish',
|
|
28
28
|
'.sql', '.graphql', '.gql', '.tf', '.tfvars', '.proto',
|
|
29
|
+
'.toml', '.properties', '.xml', '.md',
|
|
29
30
|
]);
|
|
30
31
|
|
|
31
32
|
/**
|
|
@@ -71,7 +72,7 @@ function detectAutoSrcDirs(cwd, excludeList) {
|
|
|
71
72
|
const hasRequirements = fs.existsSync(path.join(cwd, 'requirements.txt'));
|
|
72
73
|
const hasSetupPy = fs.existsSync(path.join(cwd, 'setup.py'));
|
|
73
74
|
if (hasPyproject || hasRequirements || hasSetupPy) {
|
|
74
|
-
for (const d of ['src', 'app', 'tests', 'examples']) candidates.add(d);
|
|
75
|
+
for (const d of ['src', 'app', 'apps', 'tests', 'examples', 'instance', 'blueprints']) candidates.add(d);
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
if (fs.existsSync(path.join(cwd, 'Gemfile'))) {
|
package/src/eval/analyzer.js
CHANGED
|
@@ -35,6 +35,13 @@ const EXT_MAP = {
|
|
|
35
35
|
'.css': 'css', '.scss': 'css', '.sass': 'css', '.less': 'css',
|
|
36
36
|
'.yml': 'yaml', '.yaml': 'yaml',
|
|
37
37
|
'.sh': 'shell', '.bash': 'shell', '.zsh': 'shell', '.fish': 'shell',
|
|
38
|
+
'.toml': 'toml',
|
|
39
|
+
'.properties': 'properties',
|
|
40
|
+
'.xml': 'xml',
|
|
41
|
+
'.md': 'markdown',
|
|
42
|
+
// Phase C specialized extractors
|
|
43
|
+
'.tsx': 'typescript_react',
|
|
44
|
+
'.vue': 'vue_sfc',
|
|
38
45
|
};
|
|
39
46
|
|
|
40
47
|
function isDockerfile(name) {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lightweight markdown technical indexer.
|
|
5
|
+
* Captures headings and fenced code block language hints only.
|
|
6
|
+
*
|
|
7
|
+
* @param {string} src - Raw markdown content
|
|
8
|
+
* @returns {string[]} Array of signature strings
|
|
9
|
+
*/
|
|
10
|
+
function extract(src) {
|
|
11
|
+
if (!src || typeof src !== 'string') return [];
|
|
12
|
+
const sigs = [];
|
|
13
|
+
|
|
14
|
+
// Headings: # .. ######
|
|
15
|
+
for (const m of src.matchAll(/^(#{1,6})\s+(.+)$/gm)) {
|
|
16
|
+
const level = m[1].length;
|
|
17
|
+
const title = m[2].trim().replace(/\s+/g, ' ');
|
|
18
|
+
if (title) sigs.push(`h${level} ${title}`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Fenced code blocks: ```lang
|
|
22
|
+
for (const m of src.matchAll(/^```\s*([A-Za-z0-9_+-]*)\s*$/gm)) {
|
|
23
|
+
const lang = m[1] ? m[1].toLowerCase() : 'plain';
|
|
24
|
+
sigs.push(`code-fence ${lang}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return Array.from(new Set(sigs)).slice(0, 40);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = { extract };
|