sigmap 8.19.0 → 8.21.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/CHANGELOG.md +26 -0
- package/README.md +10 -10
- package/gen-context.js +445 -19
- package/llms-full.txt +9 -7
- package/llms.txt +6 -6
- package/package.json +2 -1
- package/packages/cli/package.json +1 -1
- package/packages/core/package.json +1 -1
- package/src/config/defaults.js +2 -0
- package/src/extractors/go.js +31 -3
- package/src/extractors/java.js +37 -2
- package/src/extractors/javascript.js +42 -1
- package/src/extractors/rust.js +33 -5
- package/src/extractors/typescript.js +41 -1
- package/src/graph/centrality.js +61 -0
- package/src/mcp/handlers.js +6 -2
- package/src/mcp/server.js +1 -1
- package/src/retrieval/ranker.js +26 -1
- package/src/session/memory-inspect.js +86 -0
package/gen-context.js
CHANGED
|
@@ -1533,6 +1533,8 @@ __factories["./src/config/defaults"] = function(module, exports) {
|
|
|
1533
1533
|
recencyBoost: 1.5,
|
|
1534
1534
|
// Boost files call-graph-connected to query matches (opt-in, measure-gated)
|
|
1535
1535
|
callGraphBoost: false,
|
|
1536
|
+
// Blend import-graph centrality into ranking as a small prior (opt-in, measure-gated)
|
|
1537
|
+
centralityBlend: false,
|
|
1536
1538
|
// Append route pseudo-signatures to the rankable index (opt-in, measure-gated)
|
|
1537
1539
|
surfaceEnrichment: false,
|
|
1538
1540
|
},
|
|
@@ -5928,6 +5930,10 @@ __factories["./src/extractors/go"] = function(module, exports) {
|
|
|
5928
5930
|
function extract(src) {
|
|
5929
5931
|
if (!src || typeof src !== 'string') return [];
|
|
5930
5932
|
const sigs = [];
|
|
5933
|
+
const docHints = buildDocHints(src);
|
|
5934
|
+
// Append the godoc hint after the anchor as ` # <hint>` — same convention
|
|
5935
|
+
// as the Python/JS extractors' doc hints.
|
|
5936
|
+
const hinted = (sig, name) => (docHints.has(name) ? `${sig} # ${docHints.get(name)}` : sig);
|
|
5931
5937
|
|
|
5932
5938
|
const stripped = src
|
|
5933
5939
|
.replace(/\/\/.*$/gm, '')
|
|
@@ -5939,14 +5945,14 @@ __factories["./src/extractors/go"] = function(module, exports) {
|
|
|
5939
5945
|
// Structs
|
|
5940
5946
|
for (const m of stripped.matchAll(/^type\s+(\w+)\s+struct\s*\{/gm)) {
|
|
5941
5947
|
const end = blockEndIdx(m.index + m[0].length);
|
|
5942
|
-
sigs.push(withAnchor(`type ${m[1]} struct`, lineAt(stripped, m.index), lineAt(stripped, end)));
|
|
5948
|
+
sigs.push(hinted(withAnchor(`type ${m[1]} struct`, lineAt(stripped, m.index), lineAt(stripped, end)), m[1]));
|
|
5943
5949
|
}
|
|
5944
5950
|
|
|
5945
5951
|
// Interfaces
|
|
5946
5952
|
for (const m of stripped.matchAll(/^type\s+(\w+)\s+interface\s*\{/gm)) {
|
|
5947
5953
|
const bodyStart = m.index + m[0].length;
|
|
5948
5954
|
const block = extractBlock(stripped, bodyStart);
|
|
5949
|
-
sigs.push(withAnchor(`type ${m[1]} interface`, lineAt(stripped, m.index), lineAt(stripped, bodyStart + block.length)));
|
|
5955
|
+
sigs.push(hinted(withAnchor(`type ${m[1]} interface`, lineAt(stripped, m.index), lineAt(stripped, bodyStart + block.length)), m[1]));
|
|
5950
5956
|
for (const meth of extractInterfaceMethods(block)) {
|
|
5951
5957
|
sigs.push(withAnchor(` ${meth.text}`, lineAt(stripped, bodyStart + meth.declIdx), lineAt(stripped, bodyStart + meth.endIdx)));
|
|
5952
5958
|
}
|
|
@@ -5958,7 +5964,7 @@ __factories["./src/extractors/go"] = function(module, exports) {
|
|
|
5958
5964
|
const retType = m[4] ? m[4].trim().replace(/\s+/g, ' ') : '';
|
|
5959
5965
|
const retStr = retType ? ` → ${retType.slice(0, 30)}` : '';
|
|
5960
5966
|
const end = blockEndIdx(m.index + m[0].length);
|
|
5961
|
-
sigs.push(withAnchor(`func ${receiver}${m[2]}(${normalizeParams(m[3])})${retStr}`, lineAt(stripped, m.index), lineAt(stripped, end)));
|
|
5967
|
+
sigs.push(hinted(withAnchor(`func ${receiver}${m[2]}(${normalizeParams(m[3])})${retStr}`, lineAt(stripped, m.index), lineAt(stripped, end)), m[2]));
|
|
5962
5968
|
}
|
|
5963
5969
|
|
|
5964
5970
|
return sigs.slice(0, 25);
|
|
@@ -5994,6 +6000,30 @@ __factories["./src/extractors/go"] = function(module, exports) {
|
|
|
5994
6000
|
return params.trim().replace(/\s+/g, ' ');
|
|
5995
6001
|
}
|
|
5996
6002
|
|
|
6003
|
+
// Godoc: the `//` comment block directly above a top-level func/type/method
|
|
6004
|
+
// declaration → first prose sentence, 60-char cap. Runs on the ORIGINAL src
|
|
6005
|
+
// (extract strips comments before matching). Compiler directives (`//go:...`)
|
|
6006
|
+
// carry no prose and are skipped.
|
|
6007
|
+
function buildDocHints(src) {
|
|
6008
|
+
const hints = new Map();
|
|
6009
|
+
const re = /((?:^\/\/[^\n]*\n)+)(?:func\s+(?:\(\w+\s+[\w*]+\)\s+)?(\w+)\s*\(|type\s+(\w+)\s+(?:struct|interface)\b)/gm;
|
|
6010
|
+
for (const m of src.matchAll(re)) {
|
|
6011
|
+
const name = m[2] || m[3];
|
|
6012
|
+
const hint = firstDocSentence(m[1]);
|
|
6013
|
+
if (hint && !hints.has(name)) hints.set(name, hint);
|
|
6014
|
+
}
|
|
6015
|
+
return hints;
|
|
6016
|
+
}
|
|
6017
|
+
|
|
6018
|
+
// First non-directive prose line of a `//` block → first sentence, 60-char cap.
|
|
6019
|
+
function firstDocSentence(block) {
|
|
6020
|
+
const line = String(block).split('\n')
|
|
6021
|
+
.map((l) => l.replace(/^\/\/\s?/, '').trim())
|
|
6022
|
+
.find((l) => l && !l.startsWith('go:') && !l.startsWith('nolint'));
|
|
6023
|
+
if (!line) return '';
|
|
6024
|
+
return line.split(/[.!?]/)[0].trim().slice(0, 60);
|
|
6025
|
+
}
|
|
6026
|
+
|
|
5997
6027
|
module.exports = { extract };
|
|
5998
6028
|
|
|
5999
6029
|
};
|
|
@@ -6126,6 +6156,10 @@ __factories["./src/extractors/java"] = function(module, exports) {
|
|
|
6126
6156
|
function extract(src) {
|
|
6127
6157
|
if (!src || typeof src !== 'string') return [];
|
|
6128
6158
|
const sigs = [];
|
|
6159
|
+
const docHints = buildDocHints(src);
|
|
6160
|
+
// Append the Javadoc hint after the anchor as ` # <hint>` — same convention
|
|
6161
|
+
// as the Python/JS extractors' doc hints.
|
|
6162
|
+
const hinted = (sig, name) => (docHints.has(name) ? `${sig} # ${docHints.get(name)}` : sig);
|
|
6129
6163
|
|
|
6130
6164
|
const stripped = src
|
|
6131
6165
|
.replace(/\/\/.*$/gm, '')
|
|
@@ -6136,9 +6170,9 @@ __factories["./src/extractors/java"] = function(module, exports) {
|
|
|
6136
6170
|
for (const m of stripped.matchAll(typeRegex)) {
|
|
6137
6171
|
const bodyStart = m.index + m[0].length;
|
|
6138
6172
|
const block = extractBlock(stripped, bodyStart);
|
|
6139
|
-
sigs.push(withAnchor(`${m[1]} ${m[2]}`, lineAt(stripped, m.index), lineAt(stripped, bodyStart + block.length)));
|
|
6173
|
+
sigs.push(hinted(withAnchor(`${m[1]} ${m[2]}`, lineAt(stripped, m.index), lineAt(stripped, bodyStart + block.length)), m[2]));
|
|
6140
6174
|
for (const meth of extractMembers(block)) {
|
|
6141
|
-
sigs.push(withAnchor(` ${meth.text}`, lineAt(stripped, bodyStart + meth.declIdx), lineAt(stripped, bodyStart + meth.endIdx)));
|
|
6175
|
+
sigs.push(hinted(withAnchor(` ${meth.text}`, lineAt(stripped, bodyStart + meth.declIdx), lineAt(stripped, bodyStart + meth.endIdx)), meth.name));
|
|
6142
6176
|
}
|
|
6143
6177
|
}
|
|
6144
6178
|
|
|
@@ -6165,6 +6199,7 @@ __factories["./src/extractors/java"] = function(module, exports) {
|
|
|
6165
6199
|
const retStr = ret ? ` → ${ret}` : '';
|
|
6166
6200
|
members.push({
|
|
6167
6201
|
text: `${m[2]}(${normalizeParams(m[3])})${retStr}`,
|
|
6202
|
+
name: m[2],
|
|
6168
6203
|
declIdx: m.index + (m[0].length - m[0].trimStart().length),
|
|
6169
6204
|
endIdx: m.index + m[0].length,
|
|
6170
6205
|
});
|
|
@@ -6182,6 +6217,36 @@ __factories["./src/extractors/java"] = function(module, exports) {
|
|
|
6182
6217
|
return type.trim().replace(/\s+/g, ' ').slice(0, 30);
|
|
6183
6218
|
}
|
|
6184
6219
|
|
|
6220
|
+
// Javadoc: the `/** ... */` block directly above a type or public/protected
|
|
6221
|
+
// member declaration → first prose sentence, 60-char cap. Runs on the
|
|
6222
|
+
// ORIGINAL src (extract strips comments before matching). Annotation lines
|
|
6223
|
+
// (`@Override` etc.) between the doc block and the declaration are tolerated.
|
|
6224
|
+
// Body may not contain `*/` so a failed adjacency check can't expand across
|
|
6225
|
+
// code to the next comment block and misattribute the hint.
|
|
6226
|
+
function buildDocHints(src) {
|
|
6227
|
+
const hints = new Map();
|
|
6228
|
+
const patterns = [
|
|
6229
|
+
/\/\*\*((?:[^*]|\*(?!\/))*)\*\/\s*(?:@\w+(?:\([^)]*\))?\s*)*(?:public\s+|protected\s+)?(?:abstract\s+|final\s+)?(?:class|interface|enum)\s+(\w+)/g,
|
|
6230
|
+
/\/\*\*((?:[^*]|\*(?!\/))*)\*\/\s*(?:@\w+(?:\([^)]*\))?\s*)*(?:public|protected)\s+(?:static\s+)?(?:final\s+)?(?:synchronized\s+)?(?:<[^>]+>\s+)?[\w<>\[\], ?.]+\s+(\w+)\s*\(/g,
|
|
6231
|
+
];
|
|
6232
|
+
for (const re of patterns) {
|
|
6233
|
+
for (const m of src.matchAll(re)) {
|
|
6234
|
+
const hint = firstDocSentence(m[1]);
|
|
6235
|
+
if (hint && !hints.has(m[2])) hints.set(m[2], hint);
|
|
6236
|
+
}
|
|
6237
|
+
}
|
|
6238
|
+
return hints;
|
|
6239
|
+
}
|
|
6240
|
+
|
|
6241
|
+
// First non-tag prose line of a Javadoc body → first sentence, 60-char cap.
|
|
6242
|
+
function firstDocSentence(body) {
|
|
6243
|
+
const line = String(body).split('\n')
|
|
6244
|
+
.map((l) => l.replace(/^\s*\*\s?/, '').trim())
|
|
6245
|
+
.find((l) => l && !l.startsWith('@'));
|
|
6246
|
+
if (!line) return '';
|
|
6247
|
+
return line.split(/[.!?]/)[0].trim().slice(0, 60);
|
|
6248
|
+
}
|
|
6249
|
+
|
|
6185
6250
|
module.exports = { extract };
|
|
6186
6251
|
|
|
6187
6252
|
};
|
|
@@ -6203,7 +6268,12 @@ __factories["./src/extractors/javascript"] = function(module, exports) {
|
|
|
6203
6268
|
if (!src || typeof src !== 'string') return [];
|
|
6204
6269
|
const sigs = [];
|
|
6205
6270
|
const anchors = [];
|
|
6271
|
+
// docHintFor[i] is the doc-comment hint for sigs[i] (top-level functions
|
|
6272
|
+
// only), appended after the anchor as ` # <hint>` — same convention as the
|
|
6273
|
+
// Python extractor's extractDocHint.
|
|
6274
|
+
const docHintFor = [];
|
|
6206
6275
|
const returnHints = buildReturnHints(src);
|
|
6276
|
+
const docHints = buildDocHints(src);
|
|
6207
6277
|
|
|
6208
6278
|
// Block comments are blanked newline-by-newline (non-newline chars → spaces)
|
|
6209
6279
|
// so character offsets AND line numbers stay exact for anchors.
|
|
@@ -6238,6 +6308,7 @@ __factories["./src/extractors/javascript"] = function(module, exports) {
|
|
|
6238
6308
|
const retStr = formatReturnHint(returnHints.get(m[1]));
|
|
6239
6309
|
const startLn = lineAt(stripped, m.index);
|
|
6240
6310
|
sigs.push(`export ${asyncKw}function ${m[1]}(${normalizeParams(m[2])})${retStr}`);
|
|
6311
|
+
docHintFor[sigs.length - 1] = docHints.get(m[1]);
|
|
6241
6312
|
anchors.push([startLn, fnEndLine(m.index + m[0].length, startLn)]);
|
|
6242
6313
|
}
|
|
6243
6314
|
|
|
@@ -6247,6 +6318,7 @@ __factories["./src/extractors/javascript"] = function(module, exports) {
|
|
|
6247
6318
|
const retStr = formatReturnHint(returnHints.get(m[1]));
|
|
6248
6319
|
const startLn = lineAt(stripped, m.index);
|
|
6249
6320
|
sigs.push(`export const ${m[1]} = ${asyncKw}(${normalizeParams(m[2])}) =>${retStr}`);
|
|
6321
|
+
docHintFor[sigs.length - 1] = docHints.get(m[1]);
|
|
6250
6322
|
anchors.push([startLn, fnEndLine(m.index + m[0].length, startLn)]);
|
|
6251
6323
|
}
|
|
6252
6324
|
|
|
@@ -6267,10 +6339,14 @@ __factories["./src/extractors/javascript"] = function(module, exports) {
|
|
|
6267
6339
|
const retStr = formatReturnHint(returnHints.get(m[1]));
|
|
6268
6340
|
const startLn = lineAt(stripped, m.index);
|
|
6269
6341
|
sigs.push(`${asyncKw}function ${m[1]}(${normalizeParams(m[2])})${retStr}`);
|
|
6342
|
+
docHintFor[sigs.length - 1] = docHints.get(m[1]);
|
|
6270
6343
|
anchors.push([startLn, fnEndLine(m.index + m[0].length, startLn)]);
|
|
6271
6344
|
}
|
|
6272
6345
|
|
|
6273
|
-
const withAnchors = sigs.map((s, i) =>
|
|
6346
|
+
const withAnchors = sigs.map((s, i) => {
|
|
6347
|
+
const anchored = anchors[i] ? withAnchor(s, anchors[i][0], anchors[i][1]) : s;
|
|
6348
|
+
return docHintFor[i] ? `${anchored} # ${docHintFor[i]}` : anchored;
|
|
6349
|
+
});
|
|
6274
6350
|
return capWithNotice(withAnchors, 25, 'signatures');
|
|
6275
6351
|
}
|
|
6276
6352
|
|
|
@@ -6319,6 +6395,36 @@ __factories["./src/extractors/javascript"] = function(module, exports) {
|
|
|
6319
6395
|
return hints;
|
|
6320
6396
|
}
|
|
6321
6397
|
|
|
6398
|
+
// First prose sentence of the JSDoc block immediately preceding a top-level
|
|
6399
|
+
// function (same three shapes as buildReturnHints). Mirrors the Python
|
|
6400
|
+
// extractor's extractDocHint: first sentence only, 60-char cap.
|
|
6401
|
+
function buildDocHints(src) {
|
|
6402
|
+
const hints = new Map();
|
|
6403
|
+
// Body may not contain `*/` — otherwise a failed adjacency check would let
|
|
6404
|
+
// the match expand across a whole function to the next comment block and
|
|
6405
|
+
// misattribute the hint.
|
|
6406
|
+
const patterns = [
|
|
6407
|
+
/\/\*\*((?:[^*]|\*(?!\/))*)\*\/\s*(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(/g,
|
|
6408
|
+
/\/\*\*((?:[^*]|\*(?!\/))*)\*\/\s*export\s+const\s+(\w+)\s*=\s*(?:async\s+)?\(/g,
|
|
6409
|
+
];
|
|
6410
|
+
for (const re of patterns) {
|
|
6411
|
+
for (const m of src.matchAll(re)) {
|
|
6412
|
+
const hint = firstDocSentence(m[1]);
|
|
6413
|
+
if (hint && !hints.has(m[2])) hints.set(m[2], hint);
|
|
6414
|
+
}
|
|
6415
|
+
}
|
|
6416
|
+
return hints;
|
|
6417
|
+
}
|
|
6418
|
+
|
|
6419
|
+
// First non-tag prose line of a JSDoc body → first sentence, 60-char cap.
|
|
6420
|
+
function firstDocSentence(body) {
|
|
6421
|
+
const line = String(body).split('\n')
|
|
6422
|
+
.map((l) => l.replace(/^\s*\*\s?/, '').trim())
|
|
6423
|
+
.find((l) => l && !l.startsWith('@'));
|
|
6424
|
+
if (!line) return '';
|
|
6425
|
+
return line.split(/[.!?]/)[0].trim().slice(0, 60);
|
|
6426
|
+
}
|
|
6427
|
+
|
|
6322
6428
|
function normalizeType(type) {
|
|
6323
6429
|
if (!type) return '';
|
|
6324
6430
|
return type.trim().replace(/\s+/g, ' ').slice(0, 25);
|
|
@@ -7639,6 +7745,10 @@ __factories["./src/extractors/rust"] = function(module, exports) {
|
|
|
7639
7745
|
function extract(src) {
|
|
7640
7746
|
if (!src || typeof src !== 'string') return [];
|
|
7641
7747
|
const sigs = [];
|
|
7748
|
+
const docHints = buildDocHints(src);
|
|
7749
|
+
// Append the doc-comment hint after the anchor as ` # <hint>` — same
|
|
7750
|
+
// convention as the Python/JS extractors' doc hints.
|
|
7751
|
+
const hinted = (sig, name) => (docHints.has(name) ? `${sig} # ${docHints.get(name)}` : sig);
|
|
7642
7752
|
|
|
7643
7753
|
const stripped = src
|
|
7644
7754
|
.replace(/\/\/.*$/gm, '')
|
|
@@ -7660,19 +7770,19 @@ __factories["./src/extractors/rust"] = function(module, exports) {
|
|
|
7660
7770
|
// Structs
|
|
7661
7771
|
for (const m of stripped.matchAll(/^pub\s+struct\s+(\w+)(?:<[^{]*>)?/gm)) {
|
|
7662
7772
|
const [s, e] = rangeFor(m.index, m.index + m[0].length);
|
|
7663
|
-
sigs.push(withAnchor(`pub struct ${m[1]}`, s, e));
|
|
7773
|
+
sigs.push(hinted(withAnchor(`pub struct ${m[1]}`, s, e), m[1]));
|
|
7664
7774
|
}
|
|
7665
7775
|
|
|
7666
7776
|
// Enums
|
|
7667
7777
|
for (const m of stripped.matchAll(/^pub\s+enum\s+(\w+)(?:<[^{]*>)?/gm)) {
|
|
7668
7778
|
const [s, e] = rangeFor(m.index, m.index + m[0].length);
|
|
7669
|
-
sigs.push(withAnchor(`pub enum ${m[1]}`, s, e));
|
|
7779
|
+
sigs.push(hinted(withAnchor(`pub enum ${m[1]}`, s, e), m[1]));
|
|
7670
7780
|
}
|
|
7671
7781
|
|
|
7672
7782
|
// Traits
|
|
7673
7783
|
for (const m of stripped.matchAll(/^pub\s+trait\s+(\w+)(?:<[^{]*>)?/gm)) {
|
|
7674
7784
|
const [s, e] = rangeFor(m.index, m.index + m[0].length);
|
|
7675
|
-
sigs.push(withAnchor(`pub trait ${m[1]}`, s, e));
|
|
7785
|
+
sigs.push(hinted(withAnchor(`pub trait ${m[1]}`, s, e), m[1]));
|
|
7676
7786
|
}
|
|
7677
7787
|
|
|
7678
7788
|
// impl blocks
|
|
@@ -7681,7 +7791,7 @@ __factories["./src/extractors/rust"] = function(module, exports) {
|
|
|
7681
7791
|
const block = extractBlock(stripped, bodyStart);
|
|
7682
7792
|
sigs.push(withAnchor(`impl ${m[1]}`, lineAt(stripped, m.index), lineAt(stripped, bodyStart + block.length)));
|
|
7683
7793
|
for (const fn of extractMethods(block)) {
|
|
7684
|
-
sigs.push(withAnchor(` ${fn.text}`, lineAt(stripped, bodyStart + fn.declIdx), lineAt(stripped, bodyStart + fn.endIdx)));
|
|
7794
|
+
sigs.push(hinted(withAnchor(` ${fn.text}`, lineAt(stripped, bodyStart + fn.declIdx), lineAt(stripped, bodyStart + fn.endIdx)), fn.name));
|
|
7685
7795
|
}
|
|
7686
7796
|
}
|
|
7687
7797
|
|
|
@@ -7690,7 +7800,7 @@ __factories["./src/extractors/rust"] = function(module, exports) {
|
|
|
7690
7800
|
const asyncKw = m[0].includes('async') ? 'async ' : '';
|
|
7691
7801
|
const retStr = extractReturnType(m[3]);
|
|
7692
7802
|
const [s, e] = rangeFor(m.index, m.index + m[0].length);
|
|
7693
|
-
sigs.push(withAnchor(`pub ${asyncKw}fn ${m[1]}(${normalizeParams(m[2])})${retStr}`, s, e));
|
|
7803
|
+
sigs.push(hinted(withAnchor(`pub ${asyncKw}fn ${m[1]}(${normalizeParams(m[2])})${retStr}`, s, e), m[1]));
|
|
7694
7804
|
}
|
|
7695
7805
|
|
|
7696
7806
|
return sigs.slice(0, 25);
|
|
@@ -7714,6 +7824,7 @@ __factories["./src/extractors/rust"] = function(module, exports) {
|
|
|
7714
7824
|
const retStr = extractReturnType(m[3]);
|
|
7715
7825
|
methods.push({
|
|
7716
7826
|
text: `pub ${asyncKw}fn ${m[1]}(${normalizeParams(m[2])})${retStr}`,
|
|
7827
|
+
name: m[1],
|
|
7717
7828
|
declIdx: m.index + (m[0].length - m[0].trimStart().length),
|
|
7718
7829
|
endIdx: m.index + m[0].length,
|
|
7719
7830
|
});
|
|
@@ -7734,6 +7845,29 @@ __factories["./src/extractors/rust"] = function(module, exports) {
|
|
|
7734
7845
|
return ` → ${rt.length > 30 ? rt.slice(0, 27) + '...' : rt}`;
|
|
7735
7846
|
}
|
|
7736
7847
|
|
|
7848
|
+
// Rustdoc: the `///` block directly above a declaration → first prose
|
|
7849
|
+
// sentence, 60-char cap. Runs on the ORIGINAL src (extract strips comments
|
|
7850
|
+
// before matching). Attribute lines (`#[...]`) between the doc block and the
|
|
7851
|
+
// declaration are tolerated.
|
|
7852
|
+
function buildDocHints(src) {
|
|
7853
|
+
const hints = new Map();
|
|
7854
|
+
const re = /((?:^[ \t]*\/\/\/[^\n]*\n)+)(?:[ \t]*#\[[^\n]*\n)*[ \t]*pub(?:\s+async)?\s+(?:fn|struct|enum|trait)\s+(\w+)/gm;
|
|
7855
|
+
for (const m of src.matchAll(re)) {
|
|
7856
|
+
const hint = firstDocSentence(m[1]);
|
|
7857
|
+
if (hint && !hints.has(m[2])) hints.set(m[2], hint);
|
|
7858
|
+
}
|
|
7859
|
+
return hints;
|
|
7860
|
+
}
|
|
7861
|
+
|
|
7862
|
+
// First prose line of a `///` block → first sentence, 60-char cap.
|
|
7863
|
+
function firstDocSentence(block) {
|
|
7864
|
+
const line = String(block).split('\n')
|
|
7865
|
+
.map((l) => l.replace(/^[ \t]*\/\/\/\s?/, '').trim())
|
|
7866
|
+
.find((l) => l);
|
|
7867
|
+
if (!line) return '';
|
|
7868
|
+
return line.split(/[.!?]/)[0].trim().slice(0, 60);
|
|
7869
|
+
}
|
|
7870
|
+
|
|
7737
7871
|
module.exports = { extract };
|
|
7738
7872
|
|
|
7739
7873
|
};
|
|
@@ -8307,6 +8441,11 @@ __factories["./src/extractors/typescript"] = function(module, exports) {
|
|
|
8307
8441
|
function extract(src) {
|
|
8308
8442
|
if (!src || typeof src !== 'string') return [];
|
|
8309
8443
|
const sigs = [];
|
|
8444
|
+
// docHintFor[i] is the doc-comment hint for sigs[i] (exported top-level
|
|
8445
|
+
// functions only), appended after the anchor as ` # <hint>` — same
|
|
8446
|
+
// convention as the Python extractor's extractDocHint.
|
|
8447
|
+
const docHintFor = [];
|
|
8448
|
+
const docHints = buildDocHints(src);
|
|
8310
8449
|
// anchors[i] is [start, end] for a top-level sig, or null for an indented member.
|
|
8311
8450
|
// Kept parallel to `sigs` so existing push/mutation logic stays untouched;
|
|
8312
8451
|
// anchors are applied once at return.
|
|
@@ -8374,6 +8513,7 @@ __factories["./src/extractors/typescript"] = function(module, exports) {
|
|
|
8374
8513
|
const retStr = retType ? ` → ${retType}` : '';
|
|
8375
8514
|
const bodyStart = m.index + m[0].length;
|
|
8376
8515
|
sigs.push(`export ${asyncKw}function ${m[1]}(${params})${retStr}`);
|
|
8516
|
+
docHintFor[sigs.length - 1] = docHints.get(m[1]);
|
|
8377
8517
|
anchors.push([lineAt(stripped, m.index), lineAt(stripped, blockEndIdx(bodyStart))]);
|
|
8378
8518
|
|
|
8379
8519
|
// Hooks: capture compact return object shape for use* functions.
|
|
@@ -8398,6 +8538,7 @@ __factories["./src/extractors/typescript"] = function(module, exports) {
|
|
|
8398
8538
|
const asyncKw = /=\s*async\s+/.test(m[0]) ? 'async ' : '';
|
|
8399
8539
|
const params = normalizeParams(m[2]);
|
|
8400
8540
|
sigs.push(`export const ${m[1]} = ${asyncKw}(${params}) =>`);
|
|
8541
|
+
docHintFor[sigs.length - 1] = docHints.get(m[1]);
|
|
8401
8542
|
const bodyStart = stripped.indexOf('{', m.index + m[0].length);
|
|
8402
8543
|
const endLn = bodyStart !== -1
|
|
8403
8544
|
? lineAt(stripped, blockEndIdx(bodyStart + 1))
|
|
@@ -8447,7 +8588,10 @@ __factories["./src/extractors/typescript"] = function(module, exports) {
|
|
|
8447
8588
|
}
|
|
8448
8589
|
}
|
|
8449
8590
|
|
|
8450
|
-
const withAnchors = sigs.map((s, i) =>
|
|
8591
|
+
const withAnchors = sigs.map((s, i) => {
|
|
8592
|
+
const anchored = anchors[i] ? withAnchor(s, anchors[i][0], anchors[i][1]) : s;
|
|
8593
|
+
return docHintFor[i] ? `${anchored} # ${docHintFor[i]}` : anchored;
|
|
8594
|
+
});
|
|
8451
8595
|
return capWithNotice(withAnchors, 35, 'signatures');
|
|
8452
8596
|
}
|
|
8453
8597
|
|
|
@@ -8512,6 +8656,36 @@ __factories["./src/extractors/typescript"] = function(module, exports) {
|
|
|
8512
8656
|
return params.trim().replace(/\s+/g, ' ').replace(/:[^,)]+/g, '').trim();
|
|
8513
8657
|
}
|
|
8514
8658
|
|
|
8659
|
+
// First prose sentence of the JSDoc block immediately preceding an exported
|
|
8660
|
+
// top-level function (function or arrow-const form). Mirrors the Python
|
|
8661
|
+
// extractor's extractDocHint: first sentence only, 60-char cap.
|
|
8662
|
+
function buildDocHints(src) {
|
|
8663
|
+
const hints = new Map();
|
|
8664
|
+
// Body may not contain `*/` — otherwise a failed adjacency check would let
|
|
8665
|
+
// the match expand across a whole function to the next comment block and
|
|
8666
|
+
// misattribute the hint.
|
|
8667
|
+
const patterns = [
|
|
8668
|
+
/\/\*\*((?:[^*]|\*(?!\/))*)\*\/\s*export\s+(?:async\s+)?function\s+(\w+)\s*[<(]/g,
|
|
8669
|
+
/\/\*\*((?:[^*]|\*(?!\/))*)\*\/\s*export\s+const\s+(\w+)\s*[:=]/g,
|
|
8670
|
+
];
|
|
8671
|
+
for (const re of patterns) {
|
|
8672
|
+
for (const m of src.matchAll(re)) {
|
|
8673
|
+
const hint = firstDocSentence(m[1]);
|
|
8674
|
+
if (hint && !hints.has(m[2])) hints.set(m[2], hint);
|
|
8675
|
+
}
|
|
8676
|
+
}
|
|
8677
|
+
return hints;
|
|
8678
|
+
}
|
|
8679
|
+
|
|
8680
|
+
// First non-tag prose line of a JSDoc body → first sentence, 60-char cap.
|
|
8681
|
+
function firstDocSentence(body) {
|
|
8682
|
+
const line = String(body).split('\n')
|
|
8683
|
+
.map((l) => l.replace(/^\s*\*\s?/, '').trim())
|
|
8684
|
+
.find((l) => l && !l.startsWith('@'));
|
|
8685
|
+
if (!line) return '';
|
|
8686
|
+
return line.split(/[.!?]/)[0].trim().slice(0, 60);
|
|
8687
|
+
}
|
|
8688
|
+
|
|
8515
8689
|
module.exports = { extract };
|
|
8516
8690
|
|
|
8517
8691
|
};
|
|
@@ -11725,6 +11899,71 @@ __factories["./src/graph/call-graph"] = function(module, exports) {
|
|
|
11725
11899
|
|
|
11726
11900
|
};
|
|
11727
11901
|
|
|
11902
|
+
// ── ./src/graph/centrality ──
|
|
11903
|
+
__factories["./src/graph/centrality"] = function(module, exports) {
|
|
11904
|
+
|
|
11905
|
+
/**
|
|
11906
|
+
* Zero-dependency import-graph centrality (Semantic Bridge II, B3).
|
|
11907
|
+
*
|
|
11908
|
+
* Power iteration over the forward dependency graph: rank flows from each
|
|
11909
|
+
* importer to the files it imports, so heavily-referenced files accumulate
|
|
11910
|
+
* centrality and one-off helpers do not. Deterministic — fixed damping,
|
|
11911
|
+
* fixed iteration count, nodes processed in sorted order.
|
|
11912
|
+
*
|
|
11913
|
+
* The result feeds the opt-in `retrieval.centralityBlend` ranking prior
|
|
11914
|
+
* (see src/retrieval/ranker.js) — a principled deepening of the existing
|
|
11915
|
+
* graph-boost idea, not a replacement for query relevance.
|
|
11916
|
+
*/
|
|
11917
|
+
|
|
11918
|
+
const DAMPING = 0.85;
|
|
11919
|
+
const ITERATIONS = 20;
|
|
11920
|
+
|
|
11921
|
+
/**
|
|
11922
|
+
* Compute a normalized centrality score for every file in a dependency graph.
|
|
11923
|
+
*
|
|
11924
|
+
* @param {{ forward: Map<string, string[]> }} graph - forward dependency graph
|
|
11925
|
+
* (file → files it imports), as built by src/graph/builder.js
|
|
11926
|
+
* @returns {Map<string, number>} file → centrality in (0, 1], max-normalized;
|
|
11927
|
+
* empty Map when the graph is missing or empty
|
|
11928
|
+
*/
|
|
11929
|
+
function computeCentrality(graph) {
|
|
11930
|
+
if (!graph || !(graph.forward instanceof Map) || graph.forward.size === 0) return new Map();
|
|
11931
|
+
|
|
11932
|
+
const nodes = new Set(graph.forward.keys());
|
|
11933
|
+
for (const deps of graph.forward.values()) {
|
|
11934
|
+
for (const dep of deps || []) nodes.add(dep);
|
|
11935
|
+
}
|
|
11936
|
+
const nodeList = [...nodes].sort();
|
|
11937
|
+
const n = nodeList.length;
|
|
11938
|
+
const indexOf = new Map(nodeList.map((file, i) => [file, i]));
|
|
11939
|
+
const outLinks = nodeList.map((file) =>
|
|
11940
|
+
(graph.forward.get(file) || []).map((dep) => indexOf.get(dep)).filter((i) => i !== undefined));
|
|
11941
|
+
|
|
11942
|
+
let ranks = new Array(n).fill(1 / n);
|
|
11943
|
+
for (let iter = 0; iter < ITERATIONS; iter++) {
|
|
11944
|
+
const next = new Array(n).fill((1 - DAMPING) / n);
|
|
11945
|
+
let dangling = 0;
|
|
11946
|
+
for (let i = 0; i < n; i++) {
|
|
11947
|
+
if (outLinks[i].length === 0) { dangling += ranks[i]; continue; }
|
|
11948
|
+
const share = (DAMPING * ranks[i]) / outLinks[i].length;
|
|
11949
|
+
for (const j of outLinks[i]) next[j] += share;
|
|
11950
|
+
}
|
|
11951
|
+
// Dangling mass (files that import nothing) is redistributed uniformly.
|
|
11952
|
+
const danglingShare = (DAMPING * dangling) / n;
|
|
11953
|
+
for (let i = 0; i < n; i++) next[i] += danglingShare;
|
|
11954
|
+
ranks = next;
|
|
11955
|
+
}
|
|
11956
|
+
|
|
11957
|
+
const max = Math.max(...ranks) || 1;
|
|
11958
|
+
const result = new Map();
|
|
11959
|
+
for (let i = 0; i < n; i++) result.set(nodeList[i], ranks[i] / max);
|
|
11960
|
+
return result;
|
|
11961
|
+
}
|
|
11962
|
+
|
|
11963
|
+
module.exports = { computeCentrality, DAMPING, ITERATIONS };
|
|
11964
|
+
|
|
11965
|
+
};
|
|
11966
|
+
|
|
11728
11967
|
// ── ./src/graph/impact ──
|
|
11729
11968
|
__factories["./src/graph/impact"] = function(module, exports) {
|
|
11730
11969
|
|
|
@@ -13882,8 +14121,9 @@ __factories["./src/mcp/handlers"] = function(module, exports) {
|
|
|
13882
14121
|
// Build dependency graph for neighbor boost — non-fatal if it fails
|
|
13883
14122
|
let graph = null;
|
|
13884
14123
|
try { graph = buildFromCwd(cwd); } catch (_) {}
|
|
13885
|
-
// Opt-in call-graph neighbor boost + surface enrichment — non-fatal
|
|
14124
|
+
// Opt-in call-graph neighbor boost + surface enrichment + centrality blend — non-fatal
|
|
13886
14125
|
let callGraph = null;
|
|
14126
|
+
let centrality = null;
|
|
13887
14127
|
try {
|
|
13888
14128
|
const { loadConfig } = __require('./src/config/loader');
|
|
13889
14129
|
const retrieval = loadConfig(cwd).retrieval;
|
|
@@ -13893,8 +14133,11 @@ __factories["./src/mcp/handlers"] = function(module, exports) {
|
|
|
13893
14133
|
if (retrieval && retrieval.surfaceEnrichment) {
|
|
13894
14134
|
__require('./src/retrieval/enrich-from-maps').enrichWithSurfaces(index, cwd);
|
|
13895
14135
|
}
|
|
14136
|
+
if (retrieval && retrieval.centralityBlend && graph) {
|
|
14137
|
+
centrality = __require('./src/graph/centrality').computeCentrality(graph);
|
|
14138
|
+
}
|
|
13896
14139
|
} catch (_) {}
|
|
13897
|
-
const results = rank(args.query, index, { topK, cwd, graph, callGraph });
|
|
14140
|
+
const results = rank(args.query, index, { topK, cwd, graph, callGraph, centrality });
|
|
13898
14141
|
return formatRankTable(results, args.query);
|
|
13899
14142
|
} catch (err) {
|
|
13900
14143
|
return `_query_context failed: ${err.message}_`;
|
|
@@ -14607,7 +14850,7 @@ __factories["./src/mcp/server"] = function(module, exports) {
|
|
|
14607
14850
|
|
|
14608
14851
|
const SERVER_INFO = {
|
|
14609
14852
|
name: 'sigmap',
|
|
14610
|
-
version: '8.
|
|
14853
|
+
version: '8.21.0',
|
|
14611
14854
|
description: 'SigMap MCP server — code signatures on demand',
|
|
14612
14855
|
};
|
|
14613
14856
|
|
|
@@ -15737,6 +15980,9 @@ __factories["./src/retrieval/ranker"] = function(module, exports) {
|
|
|
15737
15980
|
callHop: 0.30, // call-graph file neighbor (opt-in retrieval.callGraphBoost)
|
|
15738
15981
|
};
|
|
15739
15982
|
|
|
15983
|
+
// Max additive prior for import-graph centrality (opt-in retrieval.centralityBlend)
|
|
15984
|
+
const CENTRALITY_BLEND_WEIGHT = 0.3;
|
|
15985
|
+
|
|
15740
15986
|
// Intent-specific weight adjustments
|
|
15741
15987
|
const INTENT_WEIGHTS = {
|
|
15742
15988
|
search: DEFAULT_WEIGHTS,
|
|
@@ -15869,6 +16115,8 @@ __factories["./src/retrieval/ranker"] = function(module, exports) {
|
|
|
15869
16115
|
* @param {{ forward: Map<string,string[]> }} [opts.graph] - dependency graph for neighbor boost
|
|
15870
16116
|
* @param {{ forward: Map<string,string[]> }} [opts.callGraph] - file-level call-graph edges
|
|
15871
16117
|
* (from buildCallFileGraph) for the opt-in call-neighbor boost
|
|
16118
|
+
* @param {Map<string,number>} [opts.centrality] - absolute file → normalized
|
|
16119
|
+
* centrality (from computeCentrality) for the opt-in centrality blend
|
|
15872
16120
|
* @returns {{ file: string, score: number, sigs: string[], tokens: number, intent: string, signals: object }[]}
|
|
15873
16121
|
*/
|
|
15874
16122
|
function rank(query, sigIndex, opts) {
|
|
@@ -16017,6 +16265,26 @@ __factories["./src/retrieval/ranker"] = function(module, exports) {
|
|
|
16017
16265
|
}
|
|
16018
16266
|
}
|
|
16019
16267
|
|
|
16268
|
+
// Centrality blend (opt-in via retrieval.centralityBlend): a small additive
|
|
16269
|
+
// prior from import-graph centrality so heavily-referenced files rank above
|
|
16270
|
+
// one-off helpers on ambiguous queries. Applied only to positively-scored
|
|
16271
|
+
// files — a tie-breaker among matches, never a way to surface non-matches.
|
|
16272
|
+
const centrality = (opts && opts.centrality instanceof Map && opts.centrality.size > 0) ? opts.centrality : null;
|
|
16273
|
+
if (centrality && cwd) {
|
|
16274
|
+
const path = require('path');
|
|
16275
|
+
for (const entry of scored) {
|
|
16276
|
+
if (entry.score <= 0) continue;
|
|
16277
|
+
const abs = path.resolve(cwd, entry.file);
|
|
16278
|
+
// The graph builder lowercases paths (normalizePath) — probe both forms.
|
|
16279
|
+
const c = centrality.get(abs) || centrality.get(abs.toLowerCase());
|
|
16280
|
+
if (c) {
|
|
16281
|
+
const bonus = CENTRALITY_BLEND_WEIGHT * c;
|
|
16282
|
+
entry.score += bonus;
|
|
16283
|
+
entry.signals.centrality = bonus;
|
|
16284
|
+
}
|
|
16285
|
+
}
|
|
16286
|
+
}
|
|
16287
|
+
|
|
16020
16288
|
// Compute confidence levels based on score distribution
|
|
16021
16289
|
if (scored.length > 0) {
|
|
16022
16290
|
const scores = scored.map(s => s.score);
|
|
@@ -16292,7 +16560,7 @@ __factories["./src/retrieval/ranker"] = function(module, exports) {
|
|
|
16292
16560
|
return 'search';
|
|
16293
16561
|
}
|
|
16294
16562
|
|
|
16295
|
-
module.exports = { rank, buildSigIndex, scoreFile, formatRankTable, formatRankJSON, DEFAULT_WEIGHTS, GRAPH_BOOST_AMOUNTS, detectIntent };
|
|
16563
|
+
module.exports = { rank, buildSigIndex, scoreFile, formatRankTable, formatRankJSON, DEFAULT_WEIGHTS, GRAPH_BOOST_AMOUNTS, CENTRALITY_BLEND_WEIGHT, detectIntent };
|
|
16296
16564
|
|
|
16297
16565
|
};
|
|
16298
16566
|
|
|
@@ -17202,6 +17470,96 @@ __factories["./src/session/memory"] = function(module, exports) {
|
|
|
17202
17470
|
|
|
17203
17471
|
};
|
|
17204
17472
|
|
|
17473
|
+
// ── ./src/session/memory-inspect ──
|
|
17474
|
+
__factories["./src/session/memory-inspect"] = function(module, exports) {
|
|
17475
|
+
|
|
17476
|
+
/**
|
|
17477
|
+
* memory-inspect.js — one view over SigMap's existing cross-session stores.
|
|
17478
|
+
* No new storage: reads the JSON/NDJSON files the session, notes, weights,
|
|
17479
|
+
* evidence, and tracking modules already own under `.context/`.
|
|
17480
|
+
*/
|
|
17481
|
+
|
|
17482
|
+
const fs = require('fs');
|
|
17483
|
+
const path = require('path');
|
|
17484
|
+
|
|
17485
|
+
/** store name → { file, kind } (kind drives the entry count). */
|
|
17486
|
+
const STORES = {
|
|
17487
|
+
session: { file: 'session.json', kind: 'json' },
|
|
17488
|
+
notes: { file: 'notes.ndjson', kind: 'ndjson' },
|
|
17489
|
+
weights: { file: 'weights.json', kind: 'weights' },
|
|
17490
|
+
evidence: { file: 'evidence-pack.json', kind: 'json' },
|
|
17491
|
+
gain: { file: 'gain.ndjson', kind: 'ndjson' },
|
|
17492
|
+
usage: { file: 'usage.ndjson', kind: 'ndjson' },
|
|
17493
|
+
};
|
|
17494
|
+
|
|
17495
|
+
/** Stores `clearMemory` may delete ('gain'/'usage' have their own reset flows). */
|
|
17496
|
+
const CLEARABLE = ['session', 'notes', 'weights', 'evidence'];
|
|
17497
|
+
|
|
17498
|
+
function storePath(cwd, name) {
|
|
17499
|
+
return path.join(cwd, '.context', STORES[name].file);
|
|
17500
|
+
}
|
|
17501
|
+
|
|
17502
|
+
function countEntries(kind, filePath) {
|
|
17503
|
+
try {
|
|
17504
|
+
if (kind === 'ndjson') {
|
|
17505
|
+
return fs.readFileSync(filePath, 'utf8').split('\n').filter(Boolean).length;
|
|
17506
|
+
}
|
|
17507
|
+
if (kind === 'weights') {
|
|
17508
|
+
const w = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
17509
|
+
return Object.keys((w && w.files) || w || {}).length;
|
|
17510
|
+
}
|
|
17511
|
+
return 1; // json: a single snapshot object
|
|
17512
|
+
} catch (_) {
|
|
17513
|
+
return 0;
|
|
17514
|
+
}
|
|
17515
|
+
}
|
|
17516
|
+
|
|
17517
|
+
/**
|
|
17518
|
+
* Describe every cross-session store.
|
|
17519
|
+
* @param {string} cwd
|
|
17520
|
+
* @returns {Array<{store:string, path:string, exists:boolean, entries:number, bytes:number, modified:string|null, clearable:boolean}>}
|
|
17521
|
+
*/
|
|
17522
|
+
function inspectMemory(cwd) {
|
|
17523
|
+
return Object.entries(STORES).map(([store, { file, kind }]) => {
|
|
17524
|
+
const p = storePath(cwd, store);
|
|
17525
|
+
let stat = null;
|
|
17526
|
+
try { stat = fs.statSync(p); } catch (_) {}
|
|
17527
|
+
return {
|
|
17528
|
+
store,
|
|
17529
|
+
path: path.join('.context', file),
|
|
17530
|
+
exists: !!stat,
|
|
17531
|
+
entries: stat ? countEntries(kind, p) : 0,
|
|
17532
|
+
bytes: stat ? stat.size : 0,
|
|
17533
|
+
modified: stat ? new Date(stat.mtimeMs).toISOString() : null,
|
|
17534
|
+
clearable: CLEARABLE.includes(store),
|
|
17535
|
+
};
|
|
17536
|
+
});
|
|
17537
|
+
}
|
|
17538
|
+
|
|
17539
|
+
/**
|
|
17540
|
+
* Delete one clearable store (or 'all' clearable stores).
|
|
17541
|
+
* @param {string} cwd
|
|
17542
|
+
* @param {string} store - session|notes|weights|evidence|all
|
|
17543
|
+
* @returns {string[]} names of stores actually removed
|
|
17544
|
+
*/
|
|
17545
|
+
function clearMemory(cwd, store) {
|
|
17546
|
+
const targets = store === 'all' ? CLEARABLE : [store];
|
|
17547
|
+
for (const t of targets) {
|
|
17548
|
+
if (!CLEARABLE.includes(t)) {
|
|
17549
|
+
throw new Error(`unknown or protected store "${t}" — clearable: ${CLEARABLE.join(', ')}, all`);
|
|
17550
|
+
}
|
|
17551
|
+
}
|
|
17552
|
+
const removed = [];
|
|
17553
|
+
for (const t of targets) {
|
|
17554
|
+
try { fs.unlinkSync(storePath(cwd, t)); removed.push(t); } catch (_) {}
|
|
17555
|
+
}
|
|
17556
|
+
return removed;
|
|
17557
|
+
}
|
|
17558
|
+
|
|
17559
|
+
module.exports = { inspectMemory, clearMemory, STORES, CLEARABLE };
|
|
17560
|
+
|
|
17561
|
+
};
|
|
17562
|
+
|
|
17205
17563
|
// ── ./src/session/notes ──
|
|
17206
17564
|
__factories["./src/session/notes"] = function(module, exports) {
|
|
17207
17565
|
|
|
@@ -19710,7 +20068,7 @@ function __tryGit(args, opts = {}) {
|
|
|
19710
20068
|
catch (_) { return ''; }
|
|
19711
20069
|
}
|
|
19712
20070
|
|
|
19713
|
-
const VERSION = '8.
|
|
20071
|
+
const VERSION = '8.21.0';
|
|
19714
20072
|
const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
|
|
19715
20073
|
|
|
19716
20074
|
function requireSourceOrBundled(key) {
|
|
@@ -21593,6 +21951,8 @@ Usage:
|
|
|
21593
21951
|
${cmd} evidence "<query>" Build a deterministic Evidence Pack (JSON) → .context/evidence-pack.json
|
|
21594
21952
|
${cmd} evidence "<query>" --markdown Emit the Markdown handoff rendering to stdout
|
|
21595
21953
|
${cmd} evidence "<query>" --top <n> --budget <n> --out <path> Tune ranked files / token budget / write rendered output
|
|
21954
|
+
${cmd} memory List cross-session stores (.context/) — entries, size, age
|
|
21955
|
+
${cmd} memory --clear <store> Clear one store: session|notes|weights|evidence|all (--json supported)
|
|
21596
21956
|
${cmd} note "<text>" Append a note to the cross-session decision log
|
|
21597
21957
|
${cmd} note List recent notes (also: note --list <N>)
|
|
21598
21958
|
${cmd} status Show repo state — branch, dirty files, index freshness, notes
|
|
@@ -22042,8 +22402,16 @@ function main() {
|
|
|
22042
22402
|
if (config && config.retrieval && config.retrieval.surfaceEnrichment) {
|
|
22043
22403
|
try { requireSourceOrBundled('./src/retrieval/enrich-from-maps').enrichWithSurfaces(sigIndex, cwd); } catch (_) {}
|
|
22044
22404
|
}
|
|
22405
|
+
// Opt-in import-graph centrality blend (retrieval.centralityBlend) — non-fatal
|
|
22406
|
+
let askCentrality = null;
|
|
22407
|
+
if (config && config.retrieval && config.retrieval.centralityBlend) {
|
|
22408
|
+
try {
|
|
22409
|
+
const askCentralityGraph = requireSourceOrBundled('./src/graph/builder').buildFromCwd(cwd);
|
|
22410
|
+
askCentrality = requireSourceOrBundled('./src/graph/centrality').computeCentrality(askCentralityGraph);
|
|
22411
|
+
} catch (_) {}
|
|
22412
|
+
}
|
|
22045
22413
|
|
|
22046
|
-
let ranked = rank(query, sigIndex, { topK: 5, weights: intentWeights, cwd, callGraph: askCallGraph });
|
|
22414
|
+
let ranked = rank(query, sigIndex, { topK: 5, weights: intentWeights, cwd, callGraph: askCallGraph, centrality: askCentrality });
|
|
22047
22415
|
|
|
22048
22416
|
// v6.10: Workspace scoping — infer package from query and apply boost
|
|
22049
22417
|
const workspaces = detectWorkspaces(cwd);
|
|
@@ -22986,6 +23354,56 @@ function main() {
|
|
|
22986
23354
|
|
|
22987
23355
|
// `sigmap note "<text>"` — append to the cross-session decision log.
|
|
22988
23356
|
// With no text, lists recent notes (also `note --list [N]`).
|
|
23357
|
+
// `sigmap memory` — one view over the cross-session stores in .context/.
|
|
23358
|
+
if (args[0] === 'memory') {
|
|
23359
|
+
const jsonOut = args.includes('--json');
|
|
23360
|
+
const { inspectMemory, clearMemory } = requireSourceOrBundled('./src/session/memory-inspect');
|
|
23361
|
+
const clearIdx = args.indexOf('--clear');
|
|
23362
|
+
|
|
23363
|
+
if (clearIdx !== -1) {
|
|
23364
|
+
const store = args[clearIdx + 1];
|
|
23365
|
+
if (!store || store.startsWith('--')) {
|
|
23366
|
+
console.error('[sigmap] usage: sigmap memory --clear <session|notes|weights|evidence|all>');
|
|
23367
|
+
process.exit(1);
|
|
23368
|
+
}
|
|
23369
|
+
let removed;
|
|
23370
|
+
try {
|
|
23371
|
+
removed = clearMemory(cwd, store);
|
|
23372
|
+
} catch (err) {
|
|
23373
|
+
console.error(`[sigmap] ${err.message}`);
|
|
23374
|
+
process.exit(1);
|
|
23375
|
+
}
|
|
23376
|
+
if (jsonOut) {
|
|
23377
|
+
process.stdout.write(JSON.stringify({ cleared: removed }) + '\n');
|
|
23378
|
+
} else {
|
|
23379
|
+
console.log(removed.length
|
|
23380
|
+
? `[sigmap] cleared: ${removed.join(', ')}`
|
|
23381
|
+
: `[sigmap] nothing to clear for "${store}"`);
|
|
23382
|
+
}
|
|
23383
|
+
process.exit(0);
|
|
23384
|
+
}
|
|
23385
|
+
|
|
23386
|
+
const stores = inspectMemory(cwd);
|
|
23387
|
+
if (jsonOut) {
|
|
23388
|
+
process.stdout.write(JSON.stringify({ stores }) + '\n');
|
|
23389
|
+
process.exit(0);
|
|
23390
|
+
}
|
|
23391
|
+
const fmtAge = (iso) => {
|
|
23392
|
+
if (!iso) return '—';
|
|
23393
|
+
const m = Math.floor((Date.now() - Date.parse(iso)) / 60000);
|
|
23394
|
+
const h = Math.floor(m / 60), d = Math.floor(h / 24);
|
|
23395
|
+
return d > 0 ? `${d}d ago` : h > 0 ? `${h}h ago` : `${m}m ago`;
|
|
23396
|
+
};
|
|
23397
|
+
const fmtKB = (b) => (b >= 1024 ? `${(b / 1024).toFixed(1)}KB` : `${b}B`);
|
|
23398
|
+
console.log('[sigmap] cross-session memory (.context/)');
|
|
23399
|
+
for (const s of stores) {
|
|
23400
|
+
const state = s.exists ? `${String(s.entries).padStart(5)} entries ${fmtKB(s.bytes).padStart(8)} ${fmtAge(s.modified)}` : ' — empty';
|
|
23401
|
+
console.log(` ${s.store.padEnd(9)} ${state}${s.clearable ? '' : ' (reset via its own command)'}`);
|
|
23402
|
+
}
|
|
23403
|
+
console.log(' clear: sigmap memory --clear <session|notes|weights|evidence|all>');
|
|
23404
|
+
process.exit(0);
|
|
23405
|
+
}
|
|
23406
|
+
|
|
22989
23407
|
if (args[0] === 'note') {
|
|
22990
23408
|
const jsonOut = args.includes('--json');
|
|
22991
23409
|
const { addNote, readNotes, formatNotes } = requireSourceOrBundled('./src/session/notes');
|
|
@@ -24224,7 +24642,15 @@ function main() {
|
|
|
24224
24642
|
if (config && config.retrieval && config.retrieval.surfaceEnrichment) {
|
|
24225
24643
|
try { requireSourceOrBundled('./src/retrieval/enrich-from-maps').enrichWithSurfaces(index, cwd); } catch (_) {}
|
|
24226
24644
|
}
|
|
24227
|
-
|
|
24645
|
+
// Opt-in import-graph centrality blend (retrieval.centralityBlend) — non-fatal
|
|
24646
|
+
let queryCentrality = null;
|
|
24647
|
+
if (config && config.retrieval && config.retrieval.centralityBlend) {
|
|
24648
|
+
try {
|
|
24649
|
+
const centralityGraph = requireSourceOrBundled('./src/graph/builder').buildFromCwd(cwd);
|
|
24650
|
+
queryCentrality = requireSourceOrBundled('./src/graph/centrality').computeCentrality(centralityGraph);
|
|
24651
|
+
} catch (_) {}
|
|
24652
|
+
}
|
|
24653
|
+
const results = rank(query, index, { topK, recencyBoost, cwd, callGraph: queryCallGraph, centrality: queryCentrality });
|
|
24228
24654
|
if (args.includes('--context')) {
|
|
24229
24655
|
const miniCtx = buildMiniContext(results, cwd);
|
|
24230
24656
|
const ctxOut = path.join(cwd, '.context', 'query-context.md');
|