sigmap 3.4.0 → 3.5.1
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 +610 -17
- package/CHANGELOG.md +35 -0
- package/README.md +18 -14
- package/gen-context.js +561 -8
- package/package.json +2 -1
- package/packages/adapters/llm-full.js +25 -0
- package/packages/cli/package.json +1 -1
- package/packages/core/index.js +10 -0
- package/packages/core/package.json +1 -1
- package/src/eval/analyzer.js +3 -0
- package/src/extractors/generic.js +26 -0
- package/src/extractors/patterns.js +135 -0
- package/src/extractors/python_dataclass.js +77 -0
- package/src/extractors/typescript_react.js +60 -0
- package/src/extractors/vue_sfc.js +99 -0
- package/src/format/llm-txt.js +28 -0
- package/src/format/llms-txt.js +70 -0
- package/src/mcp/server.js +1 -1
package/gen-context.js
CHANGED
|
@@ -2179,6 +2179,285 @@ __factories["./src/extractors/markdown"] = function(module, exports) {
|
|
|
2179
2179
|
|
|
2180
2180
|
};
|
|
2181
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
|
+
|
|
2182
2461
|
// ── ./src/extractors/deps ──
|
|
2183
2462
|
__factories["./src/extractors/deps"] = function(module, exports) {
|
|
2184
2463
|
|
|
@@ -4199,7 +4478,7 @@ __factories["./src/mcp/server"] = function(module, exports) {
|
|
|
4199
4478
|
|
|
4200
4479
|
const SERVER_INFO = {
|
|
4201
4480
|
name: 'sigmap',
|
|
4202
|
-
version: '3.
|
|
4481
|
+
version: '3.5.0',
|
|
4203
4482
|
description: 'SigMap MCP server — code signatures on demand',
|
|
4204
4483
|
};
|
|
4205
4484
|
|
|
@@ -5098,7 +5377,7 @@ __factories["./src/eval/analyzer"] = function(module, exports) {
|
|
|
5098
5377
|
const path = require('path');
|
|
5099
5378
|
|
|
5100
5379
|
const EXT_MAP = {
|
|
5101
|
-
'.ts': 'typescript', '.tsx': '
|
|
5380
|
+
'.ts': 'typescript', '.tsx': 'typescript_react',
|
|
5102
5381
|
'.js': 'javascript', '.jsx': 'javascript', '.mjs': 'javascript', '.cjs': 'javascript',
|
|
5103
5382
|
'.py': 'python', '.pyw': 'python',
|
|
5104
5383
|
'.java': 'java',
|
|
@@ -5112,7 +5391,7 @@ __factories["./src/eval/analyzer"] = function(module, exports) {
|
|
|
5112
5391
|
'.swift': 'swift',
|
|
5113
5392
|
'.dart': 'dart',
|
|
5114
5393
|
'.scala': 'scala', '.sc': 'scala',
|
|
5115
|
-
'.vue': '
|
|
5394
|
+
'.vue': 'vue_sfc',
|
|
5116
5395
|
'.svelte': 'svelte',
|
|
5117
5396
|
'.html': 'html', '.htm': 'html',
|
|
5118
5397
|
'.css': 'css', '.scss': 'css', '.sass': 'css', '.less': 'css',
|
|
@@ -5602,6 +5881,153 @@ __factories["./packages/adapters/index"] = function(module, exports) {
|
|
|
5602
5881
|
module.exports = { getAdapter, listAdapters, adapt, outputsToAdapters };
|
|
5603
5882
|
};
|
|
5604
5883
|
|
|
5884
|
+
// ── ./src/extractors/generic ──
|
|
5885
|
+
__factories["./src/extractors/generic"] = function(module, exports) {
|
|
5886
|
+
'use strict';
|
|
5887
|
+
const PATTERNS = [
|
|
5888
|
+
/^(?:pub\s+)?(?:async\s+)?function\s+\w+\s*\(/,
|
|
5889
|
+
/^(?:pub\s+)?(?:async\s+)?fn\s+\w+[\s(<]/,
|
|
5890
|
+
/^def\s+\w+[\s(|:]/,
|
|
5891
|
+
/^(?:pub\s+)?func\s+\w+\s*\(/,
|
|
5892
|
+
/^(?:let|let\s+rec)\s+\w+\s*[=(]/,
|
|
5893
|
+
/^class\s+\w+/,
|
|
5894
|
+
/^(?:proc|sub|method)\s+\w+\s*\(/,
|
|
5895
|
+
];
|
|
5896
|
+
function extract(src) {
|
|
5897
|
+
if (!src || typeof src !== 'string') return [];
|
|
5898
|
+
const results = [];
|
|
5899
|
+
for (const raw of src.split('\n')) {
|
|
5900
|
+
const line = raw.trim();
|
|
5901
|
+
if (!line || /^[#\-]/.test(line) || /^\/\//.test(line) || line.includes('\0')) continue;
|
|
5902
|
+
for (const pat of PATTERNS) {
|
|
5903
|
+
if (pat.test(line)) { results.push(line.slice(0, 120)); break; }
|
|
5904
|
+
}
|
|
5905
|
+
if (results.length >= 15) break;
|
|
5906
|
+
}
|
|
5907
|
+
return results;
|
|
5908
|
+
}
|
|
5909
|
+
module.exports = { extract };
|
|
5910
|
+
};
|
|
5911
|
+
|
|
5912
|
+
// ── ./src/format/llm-txt ──
|
|
5913
|
+
__factories["./src/format/llm-txt"] = function(module, exports) {
|
|
5914
|
+
'use strict';
|
|
5915
|
+
const path = require('path');
|
|
5916
|
+
function outputPath(cwd) { return path.join(cwd, 'llm.txt'); }
|
|
5917
|
+
function format(context, cwd, version) {
|
|
5918
|
+
const name = context.projectName || path.basename(cwd);
|
|
5919
|
+
const langs = [...new Set((context.fileEntries || []).map(f => f.language).filter(Boolean))];
|
|
5920
|
+
const mods = context.srcDirs || [];
|
|
5921
|
+
return [
|
|
5922
|
+
`# Project: ${name}`,
|
|
5923
|
+
`Languages: ${langs.join(', ') || 'unknown'}`,
|
|
5924
|
+
`Root: ${mods[0] || 'src/'}`,
|
|
5925
|
+
'',
|
|
5926
|
+
'## Modules',
|
|
5927
|
+
...mods.map(m => `- ${m}/`),
|
|
5928
|
+
'',
|
|
5929
|
+
'## Key flows',
|
|
5930
|
+
'- <!-- describe your main user flows here -->',
|
|
5931
|
+
'',
|
|
5932
|
+
'## Rules',
|
|
5933
|
+
'- <!-- describe your team conventions here -->',
|
|
5934
|
+
'',
|
|
5935
|
+
`Generated: ${new Date().toISOString()} | SigMap v${version}`,
|
|
5936
|
+
].join('\n');
|
|
5937
|
+
}
|
|
5938
|
+
module.exports = { format, outputPath };
|
|
5939
|
+
};
|
|
5940
|
+
|
|
5941
|
+
// ── ./src/format/llms-txt ──
|
|
5942
|
+
__factories["./src/format/llms-txt"] = function(module, exports) {
|
|
5943
|
+
'use strict';
|
|
5944
|
+
const path = require('path');
|
|
5945
|
+
const fs = require('fs');
|
|
5946
|
+
const { execSync } = require('child_process');
|
|
5947
|
+
function outputPath(cwd) { return path.join(cwd, 'llms.txt'); }
|
|
5948
|
+
function getShortCommit(cwd) {
|
|
5949
|
+
try { return execSync('git rev-parse --short HEAD', { cwd, timeout: 2000 }).toString().trim(); }
|
|
5950
|
+
catch (_) { return ''; }
|
|
5951
|
+
}
|
|
5952
|
+
function detectVersion(cwd) {
|
|
5953
|
+
try {
|
|
5954
|
+
const pkg = path.join(cwd, 'package.json');
|
|
5955
|
+
if (fs.existsSync(pkg)) return JSON.parse(fs.readFileSync(pkg, 'utf8')).version || '';
|
|
5956
|
+
} catch (_) {}
|
|
5957
|
+
return '';
|
|
5958
|
+
}
|
|
5959
|
+
function format(context, cwd, writtenFiles, sigmapVersion) {
|
|
5960
|
+
writtenFiles = writtenFiles || [];
|
|
5961
|
+
sigmapVersion = sigmapVersion || '';
|
|
5962
|
+
const name = context.projectName || path.basename(cwd);
|
|
5963
|
+
const ver = detectVersion(cwd);
|
|
5964
|
+
const commit = getShortCommit(cwd);
|
|
5965
|
+
const langs = [...new Set((context.fileEntries || []).map(f => f.language).filter(Boolean))];
|
|
5966
|
+
const lines = [
|
|
5967
|
+
'# SigMap Context Index',
|
|
5968
|
+
`> Generated by SigMap v${sigmapVersion} — zero-dependency AI context engine`,
|
|
5969
|
+
'',
|
|
5970
|
+
'## Project',
|
|
5971
|
+
`- Name: ${name}`,
|
|
5972
|
+
];
|
|
5973
|
+
if (ver) lines.push(`- Version: ${ver}`);
|
|
5974
|
+
if (langs.length) lines.push(`- Language: ${langs.join(', ')}`);
|
|
5975
|
+
if (commit) lines.push(`- Commit: ${commit}`);
|
|
5976
|
+
if (writtenFiles.length) {
|
|
5977
|
+
lines.push('', '## Context Files');
|
|
5978
|
+
for (const f of writtenFiles) {
|
|
5979
|
+
const rel = path.relative(cwd, f.path);
|
|
5980
|
+
const extra = f.tokens ? `: ${f.tokens} tokens` : '';
|
|
5981
|
+
lines.push(`- [${f.label || rel}](${rel})${extra}`);
|
|
5982
|
+
}
|
|
5983
|
+
}
|
|
5984
|
+
const mods = context.srcDirs || [];
|
|
5985
|
+
if (mods.length) {
|
|
5986
|
+
lines.push('', '## Source Modules');
|
|
5987
|
+
for (const m of mods) lines.push(`- [${m}/](${m}/)`);
|
|
5988
|
+
}
|
|
5989
|
+
const top5 = (context.fileEntries || [])
|
|
5990
|
+
.sort((a, b) => (b.sigs || []).length - (a.sigs || []).length)
|
|
5991
|
+
.slice(0, 5);
|
|
5992
|
+
if (top5.length) {
|
|
5993
|
+
lines.push('', '## Key Files');
|
|
5994
|
+
for (const f of top5) {
|
|
5995
|
+
const rel = path.relative(cwd, f.filePath);
|
|
5996
|
+
const preview = (f.sigs || []).slice(0, 3).join(', ');
|
|
5997
|
+
lines.push(`- ${rel}: ${preview}`);
|
|
5998
|
+
}
|
|
5999
|
+
}
|
|
6000
|
+
return lines.join('\n');
|
|
6001
|
+
}
|
|
6002
|
+
module.exports = { format, outputPath };
|
|
6003
|
+
};
|
|
6004
|
+
|
|
6005
|
+
// ── ./packages/adapters/llm-full ──
|
|
6006
|
+
__factories["./packages/adapters/llm-full"] = function(module, exports) {
|
|
6007
|
+
'use strict';
|
|
6008
|
+
const path = require('path');
|
|
6009
|
+
const fs = require('fs');
|
|
6010
|
+
function outputPath(cwd) { return path.join(cwd, 'llm-full.txt'); }
|
|
6011
|
+
function format(context, opts) {
|
|
6012
|
+
opts = opts || {};
|
|
6013
|
+
const lines = [
|
|
6014
|
+
`# ${context.projectName || 'Project'} — SigMap Context`,
|
|
6015
|
+
`Generated: ${new Date().toISOString()} | SigMap v${opts.version || ''}`,
|
|
6016
|
+
'',
|
|
6017
|
+
];
|
|
6018
|
+
for (const entry of (context.fileEntries || [])) {
|
|
6019
|
+
const rel = path.relative(opts.cwd || '', entry.filePath);
|
|
6020
|
+
lines.push(`## ${rel}`, '```', ...(entry.sigs || []), '```', '');
|
|
6021
|
+
}
|
|
6022
|
+
return lines.join('\n');
|
|
6023
|
+
}
|
|
6024
|
+
function write(context, cwd, opts) {
|
|
6025
|
+
opts = opts || {};
|
|
6026
|
+
fs.writeFileSync(outputPath(cwd), format(context, { ...opts, cwd }));
|
|
6027
|
+
}
|
|
6028
|
+
module.exports = { name: 'llm-full', format, outputPath, write };
|
|
6029
|
+
};
|
|
6030
|
+
|
|
5605
6031
|
/**
|
|
5606
6032
|
* SigMap — gen-context.js v1.2.0
|
|
5607
6033
|
* Zero-dependency AI context engine.
|
|
@@ -5614,7 +6040,7 @@ const path = require('path');
|
|
|
5614
6040
|
const os = require('os');
|
|
5615
6041
|
const { execSync } = require('child_process');
|
|
5616
6042
|
|
|
5617
|
-
const VERSION = '3.
|
|
6043
|
+
const VERSION = '3.5.1';
|
|
5618
6044
|
const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
|
|
5619
6045
|
|
|
5620
6046
|
function requireSourceOrBundled(key) {
|
|
@@ -5636,7 +6062,7 @@ const { DEFAULTS } = requireSourceOrBundled('./src/config/defaults');
|
|
|
5636
6062
|
// Language → extractor mapping (by file extension)
|
|
5637
6063
|
// ---------------------------------------------------------------------------
|
|
5638
6064
|
const EXT_MAP = {
|
|
5639
|
-
'.ts': 'typescript', '.tsx': '
|
|
6065
|
+
'.ts': 'typescript', '.tsx': 'typescript_react',
|
|
5640
6066
|
'.js': 'javascript', '.jsx': 'javascript', '.mjs': 'javascript', '.cjs': 'javascript',
|
|
5641
6067
|
'.py': 'python', '.pyw': 'python',
|
|
5642
6068
|
'.java': 'java',
|
|
@@ -5650,7 +6076,7 @@ const EXT_MAP = {
|
|
|
5650
6076
|
'.swift': 'swift',
|
|
5651
6077
|
'.dart': 'dart',
|
|
5652
6078
|
'.scala': 'scala', '.sc': 'scala',
|
|
5653
|
-
'.vue': '
|
|
6079
|
+
'.vue': 'vue_sfc',
|
|
5654
6080
|
'.svelte': 'svelte',
|
|
5655
6081
|
'.html': 'html', '.htm': 'html',
|
|
5656
6082
|
'.css': 'css', '.scss': 'css', '.sass': 'css', '.less': 'css',
|
|
@@ -5763,7 +6189,8 @@ function detectAndExtract(filePath, content, maxSigsPerFile) {
|
|
|
5763
6189
|
const ext = path.extname(base).toLowerCase();
|
|
5764
6190
|
let extractorName = EXT_MAP[ext] || null;
|
|
5765
6191
|
if (!extractorName && isDockerfile(base)) extractorName = 'dockerfile';
|
|
5766
|
-
|
|
6192
|
+
// Feature 7: generic fallback — catches Nix, Elixir, Gleam, Zig, Go templates, etc.
|
|
6193
|
+
if (!extractorName) extractorName = 'generic';
|
|
5767
6194
|
|
|
5768
6195
|
const extractor = getExtractor(extractorName);
|
|
5769
6196
|
if (!extractor) return [];
|
|
@@ -6701,7 +7128,37 @@ function runGenerate(cwd, config, reportMode, reportJson = false) {
|
|
|
6701
7128
|
const finalTokens = estimateTokens(content);
|
|
6702
7129
|
const formatIdx = process.argv.indexOf('--format');
|
|
6703
7130
|
const formatValue = formatIdx >= 0 ? process.argv[formatIdx + 1] : (config.format || 'default');
|
|
6704
|
-
|
|
7131
|
+
// Feature 2: --mode write matrix
|
|
7132
|
+
const activeMode = process.argv.indexOf('--mode') >= 0
|
|
7133
|
+
? process.argv[process.argv.indexOf('--mode') + 1]
|
|
7134
|
+
: (config.mode || 'default');
|
|
7135
|
+
if (activeMode === 'fast') {
|
|
7136
|
+
const llmTxtMod = requireSourceOrBundled('./src/format/llm-txt');
|
|
7137
|
+
const syncContext = { projectName: path.basename(cwd), fileEntries, srcDirs: config.srcDirs || [] };
|
|
7138
|
+
fs.writeFileSync(llmTxtMod.outputPath(cwd), llmTxtMod.format(syncContext, cwd, VERSION));
|
|
7139
|
+
console.warn(`[sigmap] --mode fast → llm.txt`);
|
|
7140
|
+
} else if (activeMode === 'full') {
|
|
7141
|
+
const llmFullMod = requireSourceOrBundled('./packages/adapters/llm-full');
|
|
7142
|
+
const syncContext = { projectName: path.basename(cwd), fileEntries, srcDirs: config.srcDirs || [] };
|
|
7143
|
+
llmFullMod.write(syncContext, cwd, { version: VERSION });
|
|
7144
|
+
console.warn(`[sigmap] --mode full → llm-full.txt`);
|
|
7145
|
+
} else if (activeMode === 'both') {
|
|
7146
|
+
const llmTxtMod = requireSourceOrBundled('./src/format/llm-txt');
|
|
7147
|
+
const llmFullMod = requireSourceOrBundled('./packages/adapters/llm-full');
|
|
7148
|
+
const llmsMod = requireSourceOrBundled('./src/format/llms-txt');
|
|
7149
|
+
const syncContext = { projectName: path.basename(cwd), fileEntries, srcDirs: config.srcDirs || [] };
|
|
7150
|
+
fs.writeFileSync(llmTxtMod.outputPath(cwd), llmTxtMod.format(syncContext, cwd, VERSION));
|
|
7151
|
+
llmFullMod.write(syncContext, cwd, { version: VERSION });
|
|
7152
|
+
writeOutputs(content, config.outputs, cwd, config);
|
|
7153
|
+
const llmsContent = llmsMod.format(syncContext, cwd, [
|
|
7154
|
+
{ path: llmTxtMod.outputPath(cwd), label: 'llm.txt' },
|
|
7155
|
+
{ path: llmFullMod.outputPath(cwd), label: 'llm-full.txt' },
|
|
7156
|
+
], VERSION);
|
|
7157
|
+
fs.writeFileSync(llmsMod.outputPath(cwd), llmsContent);
|
|
7158
|
+
console.warn(`[sigmap] --mode both → copilot-instructions.md + llm.txt + llm-full.txt + llms.txt`);
|
|
7159
|
+
} else {
|
|
7160
|
+
writeOutputs(content, config.outputs, cwd, config);
|
|
7161
|
+
}
|
|
6705
7162
|
if (formatValue === 'cache') writeCacheOutput(content, cwd);
|
|
6706
7163
|
result = { inputTokenTotal, finalTokens, fileCount: beforeCount, droppedCount };
|
|
6707
7164
|
}
|
|
@@ -6737,6 +7194,29 @@ function runGenerate(cwd, config, reportMode, reportJson = false) {
|
|
|
6737
7194
|
}
|
|
6738
7195
|
}
|
|
6739
7196
|
|
|
7197
|
+
// Feature 8: post-run summary — 6-line stdout after every normal run
|
|
7198
|
+
if (!reportMode
|
|
7199
|
+
&& !process.argv.includes('--json')
|
|
7200
|
+
&& !process.argv.includes('--report')
|
|
7201
|
+
&& !process.argv.includes('--quiet')) {
|
|
7202
|
+
const bar = '\u2500'.repeat(43);
|
|
7203
|
+
const syms = fileEntries.reduce((n, f) => n + (f.sigs ? f.sigs.length : 0), 0);
|
|
7204
|
+
const pct = result.inputTokenTotal > 0
|
|
7205
|
+
? Math.round((1 - result.finalTokens / result.inputTokenTotal) * 100)
|
|
7206
|
+
: 0;
|
|
7207
|
+
process.stderr.write([
|
|
7208
|
+
bar,
|
|
7209
|
+
` SigMap v${VERSION}`,
|
|
7210
|
+
` Files scanned : ${result.fileCount}`,
|
|
7211
|
+
` Symbols found : ${syms.toLocaleString()}`,
|
|
7212
|
+
` Token reduction: ${pct}% (${result.inputTokenTotal.toLocaleString()} \u2192 ${result.finalTokens.toLocaleString()})`,
|
|
7213
|
+
` Output : .github/copilot-instructions.md`,
|
|
7214
|
+
bar,
|
|
7215
|
+
` Try: "explain the architecture" \u00b7 "find the auth module"`,
|
|
7216
|
+
bar, '',
|
|
7217
|
+
].join('\n'));
|
|
7218
|
+
}
|
|
7219
|
+
|
|
6740
7220
|
return result;
|
|
6741
7221
|
}
|
|
6742
7222
|
|
|
@@ -7048,6 +7528,12 @@ function registerMcp(cwd, scriptPath) {
|
|
|
7048
7528
|
|
|
7049
7529
|
function main() {
|
|
7050
7530
|
const args = process.argv.slice(2);
|
|
7531
|
+
|
|
7532
|
+
// Feature 1: `sigmap run` alias — strip positional 'run' so existing logic handles it
|
|
7533
|
+
if (args[0] === 'run' && !args[0].startsWith('--')) {
|
|
7534
|
+
args.shift();
|
|
7535
|
+
}
|
|
7536
|
+
|
|
7051
7537
|
const invokedFrom = process.cwd();
|
|
7052
7538
|
const cwd = resolveProjectRoot(invokedFrom);
|
|
7053
7539
|
const scriptPath = process.argv[1] || path.join(invokedFrom, 'gen-context.js');
|
|
@@ -7075,6 +7561,73 @@ function main() {
|
|
|
7075
7561
|
|
|
7076
7562
|
const config = loadConfig(cwd);
|
|
7077
7563
|
|
|
7564
|
+
// Feature 2: `--mode fast|full|both`
|
|
7565
|
+
const modeIdx = args.indexOf('--mode');
|
|
7566
|
+
const mode = modeIdx !== -1
|
|
7567
|
+
? args[modeIdx + 1]
|
|
7568
|
+
: (config.mode || 'default');
|
|
7569
|
+
const VALID_MODES = ['default', 'fast', 'full', 'both'];
|
|
7570
|
+
if (mode && !VALID_MODES.includes(mode)) {
|
|
7571
|
+
console.error(`[sigmap] unknown --mode "${mode}". Valid: ${VALID_MODES.join(', ')}`);
|
|
7572
|
+
process.exit(1);
|
|
7573
|
+
}
|
|
7574
|
+
|
|
7575
|
+
// Feature 6: `sigmap sync` — write all outputs + llms.txt + print compact diff
|
|
7576
|
+
if (args[0] === 'sync') {
|
|
7577
|
+
try {
|
|
7578
|
+
// Generate copilot-instructions.md first (existing output)
|
|
7579
|
+
runGenerate(cwd, config, false);
|
|
7580
|
+
const llmTxt = requireSourceOrBundled('./src/format/llm-txt');
|
|
7581
|
+
const llmsGen = requireSourceOrBundled('./src/format/llms-txt');
|
|
7582
|
+
const llmFullMod = requireSourceOrBundled('./packages/adapters/llm-full');
|
|
7583
|
+
|
|
7584
|
+
// Gather file entries for llm.txt / llm-full.txt
|
|
7585
|
+
const ignorePatterns = loadIgnorePatterns(cwd);
|
|
7586
|
+
let syncFiles = buildFileList(cwd, config).filter((f) => {
|
|
7587
|
+
const rel = path.relative(cwd, f).replace(/\\/g, '/');
|
|
7588
|
+
return !matchesIgnore(rel, ignorePatterns);
|
|
7589
|
+
});
|
|
7590
|
+
const syncEntries = [];
|
|
7591
|
+
for (const fp of syncFiles) {
|
|
7592
|
+
let content = '';
|
|
7593
|
+
try { content = fs.readFileSync(fp, 'utf8'); } catch (_) { continue; }
|
|
7594
|
+
const sigs = detectAndExtract(fp, content, config.maxSigsPerFile || 25);
|
|
7595
|
+
if (sigs.length === 0) continue;
|
|
7596
|
+
syncEntries.push({ filePath: fp, sigs, language: null });
|
|
7597
|
+
}
|
|
7598
|
+
const syncContext = {
|
|
7599
|
+
projectName: path.basename(cwd),
|
|
7600
|
+
fileEntries: syncEntries,
|
|
7601
|
+
srcDirs: config.srcDirs || [],
|
|
7602
|
+
};
|
|
7603
|
+
|
|
7604
|
+
const llmTxtPath = llmTxt.outputPath(cwd);
|
|
7605
|
+
const llmFullPath = llmFullMod.outputPath(cwd);
|
|
7606
|
+
const llmsTxtPath = llmsGen.outputPath(cwd);
|
|
7607
|
+
|
|
7608
|
+
fs.writeFileSync(llmTxtPath, llmTxt.format(syncContext, cwd, VERSION));
|
|
7609
|
+
llmFullMod.write(syncContext, cwd, { version: VERSION });
|
|
7610
|
+
|
|
7611
|
+
const prev = fs.existsSync(llmsTxtPath) ? fs.readFileSync(llmsTxtPath, 'utf8') : '';
|
|
7612
|
+
const next = llmsGen.format(syncContext, cwd, [
|
|
7613
|
+
{ path: llmTxtPath, label: 'llm.txt' },
|
|
7614
|
+
{ path: llmFullPath, label: 'llm-full.txt' },
|
|
7615
|
+
], VERSION);
|
|
7616
|
+
const changed = prev !== next ? 'updated' : 'no change';
|
|
7617
|
+
fs.writeFileSync(llmsTxtPath, next);
|
|
7618
|
+
|
|
7619
|
+
console.log('[sigmap] sync complete');
|
|
7620
|
+
console.log(` .github/copilot-instructions.md updated`);
|
|
7621
|
+
console.log(` llm.txt updated`);
|
|
7622
|
+
console.log(` llm-full.txt updated`);
|
|
7623
|
+
console.log(` llms.txt ${changed}`);
|
|
7624
|
+
} catch (err) {
|
|
7625
|
+
console.error(`[sigmap] sync error: ${err.message}`);
|
|
7626
|
+
process.exit(1);
|
|
7627
|
+
}
|
|
7628
|
+
process.exit(0);
|
|
7629
|
+
}
|
|
7630
|
+
|
|
7078
7631
|
if (args.includes('--init')) {
|
|
7079
7632
|
writeInitConfig(cwd);
|
|
7080
7633
|
process.exit(0);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sigmap",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.1",
|
|
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": {
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"init": "node gen-context.js --init",
|
|
25
25
|
"report": "node gen-context.js --report",
|
|
26
26
|
"audit:strategies": "node scripts/run-strategy-audit.mjs",
|
|
27
|
+
"benchmark:matrix": "node scripts/run-benchmark-matrix.mjs --save --skip-clone",
|
|
27
28
|
"health": "node gen-context.js --health",
|
|
28
29
|
"map": "node gen-project-map.js",
|
|
29
30
|
"mcp": "node gen-context.js --mcp",
|