sigmap 6.9.0 → 6.10.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 +22 -17
- package/CHANGELOG.md +8 -0
- package/README.md +2 -2
- package/gen-context.js +101 -2
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/core/package.json +1 -1
- package/src/mcp/server.js +1 -1
- package/src/workspace/detector.js +85 -0
package/AGENTS.md
CHANGED
|
@@ -56,6 +56,12 @@ Use this marker block for all appendable context files:
|
|
|
56
56
|
| To query by topic | `sigmap --query "<topic>"` |
|
|
57
57
|
|
|
58
58
|
Always run `sigmap ask` or `sigmap --query` before searching for files relevant to a task.
|
|
59
|
+
## changes (last 5 commits — 0 seconds ago)
|
|
60
|
+
```
|
|
61
|
+
src/eval/usefulness-scorer.js +scoreUsefulness +computeUsefulnessStats
|
|
62
|
+
src/workspace/detector.js +detectWorkspaces +inferPackage +_getMatchLength +scopeToPackage
|
|
63
|
+
```
|
|
64
|
+
|
|
59
65
|
## packages
|
|
60
66
|
|
|
61
67
|
### packages/cli/index.js
|
|
@@ -174,23 +180,6 @@ function write(context, cwd, opts = {})
|
|
|
174
180
|
|
|
175
181
|
## src
|
|
176
182
|
|
|
177
|
-
### src/extractors/php.js
|
|
178
|
-
```
|
|
179
|
-
module.exports = { extract }
|
|
180
|
-
function extract(src) → string[]
|
|
181
|
-
function extractBlock(src, startIndex)
|
|
182
|
-
function extractMembers(block)
|
|
183
|
-
function normalizeParams(params)
|
|
184
|
-
function normalizeType(type)
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
### src/extractors/prdiff.js
|
|
188
|
-
```
|
|
189
|
-
module.exports = { diffSignatures, extractName }
|
|
190
|
-
function diffSignatures(baseSigs, currentSigs) → {added:string[], removed:
|
|
191
|
-
function extractName(sig)
|
|
192
|
-
```
|
|
193
|
-
|
|
194
183
|
### src/extractors/python.js
|
|
195
184
|
```
|
|
196
185
|
module.exports = { extract }
|
|
@@ -667,6 +656,13 @@ function _dedupeNested(scored)
|
|
|
667
656
|
function _computeConfidence(frameworks, languages, scoredCount)
|
|
668
657
|
```
|
|
669
658
|
|
|
659
|
+
### src/eval/usefulness-scorer.js
|
|
660
|
+
```
|
|
661
|
+
module.exports = { scoreUsefulness, computeUsefulnessStats }
|
|
662
|
+
function scoreUsefulness(taskResult, rankingScore)
|
|
663
|
+
function computeUsefulnessStats(taskResults)
|
|
664
|
+
```
|
|
665
|
+
|
|
670
666
|
### src/mcp/server.js
|
|
671
667
|
```
|
|
672
668
|
module.exports = { start }
|
|
@@ -675,3 +671,12 @@ function respondError(id, code, message)
|
|
|
675
671
|
function dispatch(msg, cwd)
|
|
676
672
|
function start(cwd)
|
|
677
673
|
```
|
|
674
|
+
|
|
675
|
+
### src/workspace/detector.js
|
|
676
|
+
```
|
|
677
|
+
module.exports = { detectWorkspaces, inferPackage, scopeToPackage }
|
|
678
|
+
function detectWorkspaces(cwd)
|
|
679
|
+
function inferPackage(query, workspaceDirs, cwd)
|
|
680
|
+
function _getMatchLength(name, token)
|
|
681
|
+
function scopeToPackage(filePath, packageDir)
|
|
682
|
+
```
|
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,14 @@ Format: [Semantic Versioning](https://semver.org/)
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
+
## [6.10.0] — 2026-05-05
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **Workspace-scoped retrieval for monorepos** — New `src/workspace/detector.js` module detects workspace packages from `package.json` workspaces field (npm array and Yarn v2 `packages` format). Automatically infers target package from query tokens (e.g., "rate limiting payments" → `packages/payments/`). Flags `--package <name>` (explicit) and `--global` (disable scoping) control retrieval scope. Files inside inferred package receive +0.30 score boost for tighter context.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
13
21
|
## [6.9.0] — 2026-05-03
|
|
14
22
|
|
|
15
23
|
### Added
|
package/README.md
CHANGED
|
@@ -76,8 +76,8 @@ Ask → Rank → Context → Validate → Judge → Learn
|
|
|
76
76
|
## Benchmark
|
|
77
77
|
|
|
78
78
|
```
|
|
79
|
-
Benchmark : sigmap-v6.
|
|
80
|
-
Date : 2026-
|
|
79
|
+
Benchmark : sigmap-v6.10-main
|
|
80
|
+
Date : 2026-05-05
|
|
81
81
|
|
|
82
82
|
Hit@5 : 80.0% (baseline 13.6% — 5.9× lift)
|
|
83
83
|
Prompt reduction : 41.0%
|
package/gen-context.js
CHANGED
|
@@ -5387,7 +5387,7 @@ __factories["./src/mcp/server"] = function(module, exports) {
|
|
|
5387
5387
|
|
|
5388
5388
|
const SERVER_INFO = {
|
|
5389
5389
|
name: 'sigmap',
|
|
5390
|
-
version: '6.
|
|
5390
|
+
version: '6.10.0',
|
|
5391
5391
|
description: 'SigMap MCP server — code signatures on demand',
|
|
5392
5392
|
};
|
|
5393
5393
|
|
|
@@ -6855,6 +6855,76 @@ __factories["./src/eval/runner"] = function(module, exports) {
|
|
|
6855
6855
|
module.exports = { run, rank, loadTasks, buildSigIndex, formatTable, formatMetrics, tokenize };
|
|
6856
6856
|
};
|
|
6857
6857
|
|
|
6858
|
+
// ── ./src/eval/usefulness-scorer ──
|
|
6859
|
+
__factories["./src/eval/usefulness-scorer"] = function(module, exports) {
|
|
6860
|
+
'use strict';
|
|
6861
|
+
|
|
6862
|
+
/**
|
|
6863
|
+
* Score answer usefulness based on:
|
|
6864
|
+
* 1. Whether right file was retrieved (retrieval hit)
|
|
6865
|
+
* 2. Whether retrieved context covered the answer (coverage)
|
|
6866
|
+
* 3. Confidence in answer quality (from ranking score)
|
|
6867
|
+
*/
|
|
6868
|
+
function scoreUsefulness(taskResult, rankingScore) {
|
|
6869
|
+
const { hitRank } = taskResult;
|
|
6870
|
+
|
|
6871
|
+
// Tier 1: File not retrieved — context cannot be useful
|
|
6872
|
+
if (hitRank === -1 || hitRank > 5) {
|
|
6873
|
+
return {
|
|
6874
|
+
tier: 'not-useful',
|
|
6875
|
+
score: 0.0,
|
|
6876
|
+
reason: 'expected file not in top 5'
|
|
6877
|
+
};
|
|
6878
|
+
}
|
|
6879
|
+
|
|
6880
|
+
// Tier 2: File retrieved but not top ranking — partially useful
|
|
6881
|
+
if (hitRank > 1) {
|
|
6882
|
+
return {
|
|
6883
|
+
tier: 'partially-useful',
|
|
6884
|
+
score: rankingScore * 0.5, // Partial usefulness
|
|
6885
|
+
reason: `file ranked #${hitRank}`
|
|
6886
|
+
};
|
|
6887
|
+
}
|
|
6888
|
+
|
|
6889
|
+
// Tier 3: File at top of ranking — fully useful
|
|
6890
|
+
return {
|
|
6891
|
+
tier: 'fully-useful',
|
|
6892
|
+
score: rankingScore, // Full usefulness
|
|
6893
|
+
reason: 'file ranked first'
|
|
6894
|
+
};
|
|
6895
|
+
}
|
|
6896
|
+
|
|
6897
|
+
function computeUsefulnessStats(taskResults) {
|
|
6898
|
+
const tiers = {
|
|
6899
|
+
'fully-useful': 0,
|
|
6900
|
+
'partially-useful': 0,
|
|
6901
|
+
'not-useful': 0
|
|
6902
|
+
};
|
|
6903
|
+
|
|
6904
|
+
let totalScore = 0;
|
|
6905
|
+
let count = 0;
|
|
6906
|
+
|
|
6907
|
+
taskResults.forEach(result => {
|
|
6908
|
+
const usefulness = scoreUsefulness(result, result.rankingScore || 1.0);
|
|
6909
|
+
tiers[usefulness.tier]++;
|
|
6910
|
+
totalScore += usefulness.score;
|
|
6911
|
+
count++;
|
|
6912
|
+
});
|
|
6913
|
+
|
|
6914
|
+
return {
|
|
6915
|
+
fully_useful: tiers['fully-useful'],
|
|
6916
|
+
partially_useful: tiers['partially-useful'],
|
|
6917
|
+
not_useful: tiers['not-useful'],
|
|
6918
|
+
fully_useful_pct: count > 0 ? (tiers['fully-useful'] / count * 100).toFixed(1) : 0,
|
|
6919
|
+
partially_useful_pct: count > 0 ? (tiers['partially-useful'] / count * 100).toFixed(1) : 0,
|
|
6920
|
+
not_useful_pct: count > 0 ? (tiers['not-useful'] / count * 100).toFixed(1) : 0,
|
|
6921
|
+
average_usefulness_score: count > 0 ? (totalScore / count).toFixed(3) : 0
|
|
6922
|
+
};
|
|
6923
|
+
}
|
|
6924
|
+
|
|
6925
|
+
module.exports = { scoreUsefulness, computeUsefulnessStats };
|
|
6926
|
+
};
|
|
6927
|
+
|
|
6858
6928
|
|
|
6859
6929
|
// ── ./packages/adapters/copilot (bundled) ──
|
|
6860
6930
|
__factories["./packages/adapters/copilot"] = function(module, exports) {
|
|
@@ -7855,7 +7925,7 @@ const path = require('path');
|
|
|
7855
7925
|
const os = require('os');
|
|
7856
7926
|
const { execSync } = require('child_process');
|
|
7857
7927
|
|
|
7858
|
-
const VERSION = '6.
|
|
7928
|
+
const VERSION = '6.10.0';
|
|
7859
7929
|
const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
|
|
7860
7930
|
|
|
7861
7931
|
function requireSourceOrBundled(key) {
|
|
@@ -9919,6 +9989,7 @@ function main() {
|
|
|
9919
9989
|
const { detectIntent, buildSigIndex, rank } = requireSourceOrBundled('./src/retrieval/ranker');
|
|
9920
9990
|
const { coverageScore } = requireSourceOrBundled('./src/analysis/coverage-score');
|
|
9921
9991
|
const { loadSession, saveSession, mergeSessionContext } = requireSourceOrBundled('./src/session/memory');
|
|
9992
|
+
const { detectWorkspaces, inferPackage, scopeToPackage } = requireSourceOrBundled('./src/workspace/detector');
|
|
9922
9993
|
|
|
9923
9994
|
const intent = detectIntent(query);
|
|
9924
9995
|
const intentWeights = getIntentWeights(intent);
|
|
@@ -9931,6 +10002,34 @@ function main() {
|
|
|
9931
10002
|
|
|
9932
10003
|
let ranked = rank(query, sigIndex, { topK: 5, weights: intentWeights, cwd });
|
|
9933
10004
|
|
|
10005
|
+
// v6.10: Workspace scoping — infer package from query and apply boost
|
|
10006
|
+
const workspaces = detectWorkspaces(cwd);
|
|
10007
|
+
const packageFlag = args[args.indexOf('--package') + 1];
|
|
10008
|
+
const globalFlag = args.includes('--global');
|
|
10009
|
+
|
|
10010
|
+
let inferredPackage = null;
|
|
10011
|
+
if (!globalFlag && workspaces.length > 0) {
|
|
10012
|
+
if (packageFlag) {
|
|
10013
|
+
inferredPackage = workspaces.find(d => path.basename(d) === packageFlag) || null;
|
|
10014
|
+
if (!packageFlag || !inferredPackage) {
|
|
10015
|
+
process.stderr.write(`[sigmap] ⚠ package "${packageFlag}" not found — searching entire repo\n`);
|
|
10016
|
+
}
|
|
10017
|
+
} else {
|
|
10018
|
+
inferredPackage = inferPackage(query, workspaces, cwd);
|
|
10019
|
+
}
|
|
10020
|
+
}
|
|
10021
|
+
|
|
10022
|
+
if (inferredPackage) {
|
|
10023
|
+
ranked = ranked.map(r => ({
|
|
10024
|
+
...r,
|
|
10025
|
+
score: r.score + scopeToPackage(r.file, inferredPackage),
|
|
10026
|
+
})).sort((a, b) => b.score - a.score);
|
|
10027
|
+
|
|
10028
|
+
if (!args.includes('--json') && !globalFlag) {
|
|
10029
|
+
process.stderr.write(`[sigmap] 📦 package scope: ${path.relative(cwd, inferredPackage)}\n`);
|
|
10030
|
+
}
|
|
10031
|
+
}
|
|
10032
|
+
|
|
9934
10033
|
// v6.8: --followup support — carry session context forward
|
|
9935
10034
|
const isFollowup = args.includes('--followup');
|
|
9936
10035
|
const session = isFollowup ? loadSession(cwd) : null;
|
package/package.json
CHANGED
package/src/mcp/server.js
CHANGED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
module.exports = { detectWorkspaces, inferPackage, scopeToPackage };
|
|
5
|
+
|
|
6
|
+
function detectWorkspaces(cwd) {
|
|
7
|
+
const pkgPath = path.join(cwd, 'package.json');
|
|
8
|
+
if (!fs.existsSync(pkgPath)) return [];
|
|
9
|
+
|
|
10
|
+
let pkg;
|
|
11
|
+
try {
|
|
12
|
+
pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
13
|
+
} catch {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const patterns = pkg.workspaces || [];
|
|
18
|
+
const dirs = [];
|
|
19
|
+
|
|
20
|
+
// Handle both flat array and object with packages field (Yarn v2 format)
|
|
21
|
+
const patternArray = Array.isArray(patterns) ? patterns : (patterns.packages || []);
|
|
22
|
+
|
|
23
|
+
for (const p of patternArray) {
|
|
24
|
+
const base = p.replace(/\/\*\*?$/, '');
|
|
25
|
+
const resolved = path.join(cwd, base);
|
|
26
|
+
if (fs.existsSync(resolved)) {
|
|
27
|
+
try {
|
|
28
|
+
for (const entry of fs.readdirSync(resolved, { withFileTypes: true })) {
|
|
29
|
+
if (entry.isDirectory()) dirs.push(path.join(resolved, entry.name));
|
|
30
|
+
}
|
|
31
|
+
} catch (_) {}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return dirs;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Infer package from query tokens: "add rate limiting to payments" → "packages/payments"
|
|
39
|
+
function inferPackage(query, workspaceDirs, cwd) {
|
|
40
|
+
const tokens = query.toLowerCase().split(/\W+/).filter(t => t.length > 2);
|
|
41
|
+
|
|
42
|
+
// Find longest matching package name
|
|
43
|
+
let bestMatch = null;
|
|
44
|
+
let bestLen = 0;
|
|
45
|
+
let bestMatchLen = 0;
|
|
46
|
+
|
|
47
|
+
for (const dir of workspaceDirs) {
|
|
48
|
+
const name = path.basename(dir).toLowerCase();
|
|
49
|
+
for (const token of tokens) {
|
|
50
|
+
const matchLen = _getMatchLength(name, token);
|
|
51
|
+
// Only consider matches; use longest match, and break ties by longest package name
|
|
52
|
+
if (matchLen > 0 && (matchLen > bestLen || (matchLen === bestLen && name.length > bestMatchLen))) {
|
|
53
|
+
bestMatch = dir;
|
|
54
|
+
bestLen = matchLen;
|
|
55
|
+
bestMatchLen = name.length;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return bestMatch;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function _getMatchLength(name, token) {
|
|
64
|
+
if (name === token) return 1000 + name.length; // Exact match is best
|
|
65
|
+
if (name.startsWith(token) && token.length >= 3) return 100 + token.length;
|
|
66
|
+
if (token.startsWith(name) && name.length >= 3) return name.length;
|
|
67
|
+
return 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Return boost multiplier for files inside the inferred package
|
|
71
|
+
function scopeToPackage(filePath, packageDir) {
|
|
72
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
73
|
+
const normalizedPkg = packageDir.replace(/\\/g, '/');
|
|
74
|
+
|
|
75
|
+
// Ensure we match the directory boundary, not just a prefix
|
|
76
|
+
// e.g., packages/payment should not match packages/payment-old
|
|
77
|
+
if (normalized.startsWith(normalizedPkg)) {
|
|
78
|
+
const afterPrefix = normalized.slice(normalizedPkg.length);
|
|
79
|
+
// Check if next char is / or if it's the exact match
|
|
80
|
+
if (afterPrefix === '' || afterPrefix[0] === '/') {
|
|
81
|
+
return 0.30;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return 0;
|
|
85
|
+
}
|