sigmap 6.9.0 → 6.10.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 +105 -137
- package/CHANGELOG.md +24 -0
- package/README.md +21 -4
- package/gen-context.js +214 -20
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/core/index.js +1 -0
- package/packages/core/package.json +1 -1
- package/src/discovery/language-detector.js +1 -0
- package/src/discovery/source-root-registry.js +9 -0
- package/src/discovery/source-root-resolver.js +5 -1
- package/src/eval/analyzer.js +1 -0
- package/src/extractors/python.js +33 -2
- package/src/extractors/python_ast.py +348 -0
- package/src/extractors/r.js +136 -0
- package/src/mcp/server.js +1 -1
- package/src/workspace/detector.js +85 -0
package/AGENTS.md
CHANGED
|
@@ -56,6 +56,19 @@ 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
|
+
## deps
|
|
60
|
+
```
|
|
61
|
+
src/extractors/python_ast.py ← ast
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## changes (last 5 commits — 0 seconds ago)
|
|
65
|
+
```
|
|
66
|
+
src/discovery/language-detector.js ~detectLanguages
|
|
67
|
+
src/extractors/python.js +tryNativeExtract +extract ~extract ~extractDocHint
|
|
68
|
+
src/extractors/python_ast.py +annotation_to_str +format_args +arguments +get_decorator_names
|
|
69
|
+
src/extractors/r.js +extract +definitions +readBalancedParens +normalizeParams
|
|
70
|
+
```
|
|
71
|
+
|
|
59
72
|
## packages
|
|
60
73
|
|
|
61
74
|
### packages/cli/index.js
|
|
@@ -101,18 +114,6 @@ code-fence js
|
|
|
101
114
|
code-fence ---
|
|
102
115
|
```
|
|
103
116
|
|
|
104
|
-
### packages/core/index.js
|
|
105
|
-
```
|
|
106
|
-
module.exports = { extract, rank, buildSigIndex, scan, score, adapt }
|
|
107
|
-
function _resolveExtractor(language)
|
|
108
|
-
function extract(src, language) → string[]
|
|
109
|
-
function rank(query, sigIndex, opts) → { file: string, score: nu
|
|
110
|
-
function buildSigIndex(cwd) → Map<string, string[]>
|
|
111
|
-
function scan(sigs, filePath) → { safe: string[], redacte
|
|
112
|
-
function score(cwd) → { * score: number, * grad
|
|
113
|
-
function adapt(context, adapterName, opts = {}) → string
|
|
114
|
-
```
|
|
115
|
-
|
|
116
117
|
### packages/adapters/copilot.js
|
|
117
118
|
```
|
|
118
119
|
module.exports = { name, format, outputPath, write }
|
|
@@ -172,109 +173,19 @@ function outputPath(cwd) → string
|
|
|
172
173
|
function write(context, cwd, opts = {})
|
|
173
174
|
```
|
|
174
175
|
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
### src/extractors/python.js
|
|
195
|
-
```
|
|
196
|
-
module.exports = { extract }
|
|
197
|
-
function extract(src) → string[]
|
|
198
|
-
function extractClassMethods(stripped, startIndex)
|
|
199
|
-
function tryExtractDataclassFields(stripped, classIndex)
|
|
200
|
-
function tryExtractBaseModelFields(stripped, bodyStart)
|
|
201
|
-
function extractClassConstants(stripped, startIndex)
|
|
202
|
-
function extractReturnType(sigLine)
|
|
203
|
-
function normalizeParams(params)
|
|
204
|
-
function extractDocHint(src, fnName, fnSigLine)
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
### src/extractors/ruby.js
|
|
208
|
-
```
|
|
209
|
-
module.exports = { extract }
|
|
210
|
-
function extract(src) → string[]
|
|
211
|
-
function normalizeParams(params)
|
|
212
|
-
function extractReturnHint(stripped, index)
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
### src/extractors/rust.js
|
|
216
|
-
```
|
|
217
|
-
module.exports = { extract }
|
|
218
|
-
function extract(src) → string[]
|
|
219
|
-
function extractBlock(src, startIndex)
|
|
220
|
-
function extractMethods(block)
|
|
221
|
-
function normalizeParams(params)
|
|
222
|
-
function extractReturnType(afterParen)
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
### src/extractors/scala.js
|
|
226
|
-
```
|
|
227
|
-
module.exports = { extract }
|
|
228
|
-
function extract(src) → string[]
|
|
229
|
-
function extractBlock(src, startIndex)
|
|
230
|
-
function extractMembers(block)
|
|
231
|
-
function normalizeParams(params)
|
|
232
|
-
function normalizeType(type)
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
### src/extractors/svelte.js
|
|
236
|
-
```
|
|
237
|
-
module.exports = { extract }
|
|
238
|
-
function extract(src) → string[]
|
|
239
|
-
function normalizeParams(params)
|
|
240
|
-
function normalizeType(type)
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
### src/extractors/swift.js
|
|
244
|
-
```
|
|
245
|
-
module.exports = { extract }
|
|
246
|
-
function extract(src) → string[]
|
|
247
|
-
function extractBlock(src, startIndex)
|
|
248
|
-
function extractMembers(block)
|
|
249
|
-
function normalizeParams(params)
|
|
250
|
-
function extractArrowType(str)
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
### src/extractors/todos.js
|
|
254
|
-
```
|
|
255
|
-
module.exports = { extractTodos }
|
|
256
|
-
function extractTodos(src) → {line:number, tag:string,
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
### src/extractors/vue.js
|
|
176
|
+
### packages/core/index.js
|
|
260
177
|
```
|
|
261
|
-
module.exports = { extract }
|
|
262
|
-
function
|
|
263
|
-
function
|
|
264
|
-
function
|
|
178
|
+
module.exports = { extract, rank, buildSigIndex, scan, score, adapt }
|
|
179
|
+
function _resolveExtractor(language)
|
|
180
|
+
function extract(src, language) → string[]
|
|
181
|
+
function rank(query, sigIndex, opts) → { file: string, score: nu
|
|
182
|
+
function buildSigIndex(cwd) → Map<string, string[]>
|
|
183
|
+
function scan(sigs, filePath) → { safe: string[], redacte
|
|
184
|
+
function score(cwd) → { * score: number, * grad
|
|
185
|
+
function adapt(context, adapterName, opts = {}) → string
|
|
265
186
|
```
|
|
266
187
|
|
|
267
|
-
|
|
268
|
-
```
|
|
269
|
-
module.exports = { hitAtK, reciprocalRank, precisionAtK, aggregate, firstRank }
|
|
270
|
-
function firstRank(ranked, expected) → number
|
|
271
|
-
function normalizePath(p) → string
|
|
272
|
-
function hitAtK(ranked, expected, k = 5) → 0|1
|
|
273
|
-
function reciprocalRank(ranked, expected) → number
|
|
274
|
-
function precisionAtK(ranked, expected, k = 5) → number
|
|
275
|
-
function aggregate(results, k = 5) → { * hitAt5: number, // fr
|
|
276
|
-
function round(x)
|
|
277
|
-
```
|
|
188
|
+
## src
|
|
278
189
|
|
|
279
190
|
### src/eval/runner.js
|
|
280
191
|
```
|
|
@@ -438,19 +349,6 @@ function detectVersion(cwd)
|
|
|
438
349
|
function format(context, cwd, writtenFiles, sigmapVersion)
|
|
439
350
|
```
|
|
440
351
|
|
|
441
|
-
### src/eval/analyzer.js
|
|
442
|
-
```
|
|
443
|
-
module.exports = { analyzeFiles, formatAnalysisTable, formatAnalysisJSON }
|
|
444
|
-
function isDockerfile(name)
|
|
445
|
-
function getExtractorName(filePath)
|
|
446
|
-
function tokenCount(sigs)
|
|
447
|
-
function hasCoverage(filePath, cwd)
|
|
448
|
-
function loadExtractor(name, cwd)
|
|
449
|
-
function analyzeFiles(files, cwd, opts) → object[]
|
|
450
|
-
function formatAnalysisTable(stats, showSlow) → string
|
|
451
|
-
function formatAnalysisJSON(stats) → object
|
|
452
|
-
```
|
|
453
|
-
|
|
454
352
|
### src/format/dashboard.js
|
|
455
353
|
```
|
|
456
354
|
module.exports = { generateDashboardHtml, renderHistoryCharts, computeExtractorCoverage, percentile, overBudgetStreak }
|
|
@@ -593,13 +491,6 @@ function _existsAnywhere(cwd, filename, maxDepth)
|
|
|
593
491
|
function _walkFind(dir, name, depth)
|
|
594
492
|
```
|
|
595
493
|
|
|
596
|
-
### src/discovery/language-detector.js
|
|
597
|
-
```
|
|
598
|
-
module.exports = { detectLanguages }
|
|
599
|
-
function detectLanguages(cwd)
|
|
600
|
-
function _walkDepth(dir, depth, extCount)
|
|
601
|
-
```
|
|
602
|
-
|
|
603
494
|
### src/discovery/sigmapignore.js
|
|
604
495
|
```
|
|
605
496
|
module.exports = { loadIgnorePatterns, matchesIgnorePattern }
|
|
@@ -607,11 +498,6 @@ function loadIgnorePatterns(cwd)
|
|
|
607
498
|
function matchesIgnorePattern(dirName, patterns)
|
|
608
499
|
```
|
|
609
500
|
|
|
610
|
-
### src/discovery/source-root-registry.js
|
|
611
|
-
```
|
|
612
|
-
module.exports = { REGISTRY }
|
|
613
|
-
```
|
|
614
|
-
|
|
615
501
|
### src/retrieval/ranker.js
|
|
616
502
|
```
|
|
617
503
|
module.exports = { rank, buildSigIndex, scoreFile, formatRankTable, formatRankJSON, DEFAULT_WEIGHTS, GRAPH_BOOST_AMOUNTS, detectIntent }
|
|
@@ -656,6 +542,22 @@ function scoreCandidate(dirName, fullPath, context)
|
|
|
656
542
|
function _countSourceFiles(dir, depth)
|
|
657
543
|
```
|
|
658
544
|
|
|
545
|
+
### src/eval/usefulness-scorer.js
|
|
546
|
+
```
|
|
547
|
+
module.exports = { scoreUsefulness, computeUsefulnessStats }
|
|
548
|
+
function scoreUsefulness(taskResult, rankingScore)
|
|
549
|
+
function computeUsefulnessStats(taskResults)
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
### src/workspace/detector.js
|
|
553
|
+
```
|
|
554
|
+
module.exports = { detectWorkspaces, inferPackage, scopeToPackage }
|
|
555
|
+
function detectWorkspaces(cwd)
|
|
556
|
+
function inferPackage(query, workspaceDirs, cwd)
|
|
557
|
+
function _getMatchLength(name, token)
|
|
558
|
+
function scopeToPackage(filePath, packageDir)
|
|
559
|
+
```
|
|
560
|
+
|
|
659
561
|
### src/discovery/source-root-resolver.js
|
|
660
562
|
```
|
|
661
563
|
module.exports = { resolveSourceRoots }
|
|
@@ -667,6 +569,72 @@ function _dedupeNested(scored)
|
|
|
667
569
|
function _computeConfidence(frameworks, languages, scoredCount)
|
|
668
570
|
```
|
|
669
571
|
|
|
572
|
+
### src/discovery/source-root-registry.js
|
|
573
|
+
```
|
|
574
|
+
module.exports = { REGISTRY }
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
### src/discovery/language-detector.js
|
|
578
|
+
```
|
|
579
|
+
module.exports = { detectLanguages }
|
|
580
|
+
function detectLanguages(cwd)
|
|
581
|
+
function _walkDepth(dir, depth, extCount)
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
### src/eval/analyzer.js
|
|
585
|
+
```
|
|
586
|
+
module.exports = { analyzeFiles, formatAnalysisTable, formatAnalysisJSON }
|
|
587
|
+
function isDockerfile(name)
|
|
588
|
+
function getExtractorName(filePath)
|
|
589
|
+
function tokenCount(sigs)
|
|
590
|
+
function hasCoverage(filePath, cwd)
|
|
591
|
+
function loadExtractor(name, cwd)
|
|
592
|
+
function analyzeFiles(files, cwd, opts) → object[]
|
|
593
|
+
function formatAnalysisTable(stats, showSlow) → string
|
|
594
|
+
function formatAnalysisJSON(stats) → object
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
### src/extractors/python.js
|
|
598
|
+
```
|
|
599
|
+
module.exports = { extract, tryNativeExtract }
|
|
600
|
+
function tryNativeExtract(filePath) → string[]|null
|
|
601
|
+
function extract(src, filePath) → string[]
|
|
602
|
+
function extractClassMethods(stripped, startIndex)
|
|
603
|
+
function tryExtractDataclassFields(stripped, classIndex)
|
|
604
|
+
function tryExtractBaseModelFields(stripped, bodyStart)
|
|
605
|
+
function extractClassConstants(stripped, startIndex)
|
|
606
|
+
function extractReturnType(sigLine)
|
|
607
|
+
function normalizeParams(params)
|
|
608
|
+
function extractDocHint(src, fnName, fnSigLine)
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
### src/extractors/python_ast.py
|
|
612
|
+
```
|
|
613
|
+
def annotation_to_str(node) # Convert an AST annotation node to a string representation
|
|
614
|
+
def format_args(args_node) # Format a function arguments node into a compact signature st
|
|
615
|
+
def get_decorator_names(node) # Return a list of decorator name strings for a function/class
|
|
616
|
+
def is_dataclass(node)
|
|
617
|
+
def is_basemodel(bases) # Check if class bases include BaseModel or BaseSettings
|
|
618
|
+
def is_optional_annotation(annotation) # Check if an annotation represents an Optional type
|
|
619
|
+
def get_docstring_hint(node) # Extract first sentence of docstring, if present
|
|
620
|
+
def extract_dataclass_fields(class_node) # Return a collapsed fields string for a @dataclass class
|
|
621
|
+
def extract_basemodel_fields(class_node) # Return a compact {required*, optional
|
|
622
|
+
def extract_class_constants(class_node) # Yield ALL_CAPS constant assignments from class body
|
|
623
|
+
def extract_method_sig(func_node) # Format a method signature string (already indented by caller
|
|
624
|
+
def extract_function_sig(func_node, src_lines) # Format a top-level function signature string
|
|
625
|
+
def extract_fastapi_routes(tree, src_lines) # Extract FastAPI route signatures from top-level decorated fu
|
|
626
|
+
def extract(filepath)
|
|
627
|
+
def main()
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
### src/extractors/r.js
|
|
631
|
+
```
|
|
632
|
+
module.exports = { extract }
|
|
633
|
+
function extract(src) → string[]
|
|
634
|
+
function readBalancedParens(src, openIdx, cap = 4096)
|
|
635
|
+
function normalizeParams(raw)
|
|
636
|
+
```
|
|
637
|
+
|
|
670
638
|
### src/mcp/server.js
|
|
671
639
|
```
|
|
672
640
|
module.exports = { start }
|
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,30 @@ Format: [Semantic Versioning](https://semver.org/)
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
+
## [6.10.1] — 2026-05-10
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **R language support (Phase 1)** — Extract function signatures from `.r` and `.R` files with support for function definitions (`<-`, `=`, `<<-` forms), multi-line arguments with string-literal protection, S4 patterns (setGeneric, setMethod, setClass), and private function filtering. Shiny framework detection via `app.R`/`ui.R`/`server.R` triplet.
|
|
18
|
+
- **Native Python AST extractor** — Fallback to `python_ast.py` using `ast.parse()` for accurate extraction of complex signatures (multiline args, stacked decorators, complex generics). Preserves regex fallback for Python 2 / no-Python3 environments. Zero breaking changes to output format.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- **ReferenceError in `--query`** — Fixed variable scope issue where `adpIdx` was undefined when no context file present. Moved variable declaration to proper scope before conditional block.
|
|
23
|
+
- **Windows path handling** — Normalized path separators in nested path deduplication. Windows backslashes no longer cause false negatives when matching nested source roots.
|
|
24
|
+
- **.contextignore patterns** — Fixed bracket character classes (`[Bb]in/`) being treated as literals. Fixed trailing slashes on directory patterns not matching nested paths. Added error handling for malformed bracket syntax.
|
|
25
|
+
- **Claude adapter in per-module and hot-cold strategies** — Fixed adapter not being written to output in per-module and hot-cold context strategies.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## [6.10.0] — 2026-05-05
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
- **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.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
13
37
|
## [6.9.0] — 2026-05-03
|
|
14
38
|
|
|
15
39
|
### Added
|
package/README.md
CHANGED
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
[](package.json)
|
|
13
13
|
[](LICENSE)
|
|
14
14
|
[](https://github.com/manojmallick/sigmap/stargazers)
|
|
15
|
-
[](https://star-history.com/#manojmallick/sigmap&Date)
|
|
16
|
+
[](https://shypd.ai/tools/sigmap)
|
|
16
17
|
|
|
17
18
|
</div>
|
|
18
19
|
|
|
@@ -20,9 +21,11 @@
|
|
|
20
21
|
|
|
21
22
|
## Try it now
|
|
22
23
|
|
|
24
|
+
**No install required.** Run instantly on any machine:
|
|
25
|
+
|
|
23
26
|
```bash
|
|
24
27
|
npx sigmap
|
|
25
|
-
sigmap ask "Where is auth handled?"
|
|
28
|
+
npx sigmap ask "Where is auth handled?"
|
|
26
29
|
```
|
|
27
30
|
|
|
28
31
|
Zero config. Zero dependencies. Under 10 seconds.
|
|
@@ -76,8 +79,8 @@ Ask → Rank → Context → Validate → Judge → Learn
|
|
|
76
79
|
## Benchmark
|
|
77
80
|
|
|
78
81
|
```
|
|
79
|
-
Benchmark : sigmap-v6.
|
|
80
|
-
Date : 2026-
|
|
82
|
+
Benchmark : sigmap-v6.10-main
|
|
83
|
+
Date : 2026-05-05
|
|
81
84
|
|
|
82
85
|
Hit@5 : 80.0% (baseline 13.6% — 5.9× lift)
|
|
83
86
|
Prompt reduction : 41.0%
|
|
@@ -228,6 +231,20 @@ If SigMap saves you context or API spend, a ⭐ on [GitHub](https://github.com/m
|
|
|
228
231
|
|
|
229
232
|
---
|
|
230
233
|
|
|
234
|
+
## Contributing
|
|
235
|
+
|
|
236
|
+
SigMap welcomes contributions!
|
|
237
|
+
|
|
238
|
+
**Before submitting a PR:**
|
|
239
|
+
1. Read [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
240
|
+
2. Check [Discussions → Announcements](../../discussions) for workflow setup
|
|
241
|
+
3. Target the `develop` branch (not main)
|
|
242
|
+
4. Follow the [contributor checklist](.github/CONTRIBUTOR_CHECKLIST.txt)
|
|
243
|
+
|
|
244
|
+
See [.github/PULL_REQUEST_TEMPLATE.md](.github/PULL_REQUEST_TEMPLATE.md) for the PR checklist. All contributors are credited in the CHANGELOG and release notes.
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
231
248
|
## Why not embeddings?
|
|
232
249
|
|
|
233
250
|
| | Embeddings | SigMap |
|
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.1',
|
|
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) {
|
|
@@ -7843,6 +7913,95 @@ __factories["./src/discovery/source-root-resolver"] = function(module, exports)
|
|
|
7843
7913
|
module.exports = { resolveSourceRoots };
|
|
7844
7914
|
};
|
|
7845
7915
|
|
|
7916
|
+
// ── ./src/workspace/detector ──
|
|
7917
|
+
__factories["./src/workspace/detector"] = function(module, exports) {
|
|
7918
|
+
'use strict';
|
|
7919
|
+
const fs = require('fs');
|
|
7920
|
+
const path = require('path');
|
|
7921
|
+
module.exports = { detectWorkspaces, inferPackage, scopeToPackage };
|
|
7922
|
+
|
|
7923
|
+
function detectWorkspaces(cwd) {
|
|
7924
|
+
const pkgPath = path.join(cwd, 'package.json');
|
|
7925
|
+
if (!fs.existsSync(pkgPath)) return [];
|
|
7926
|
+
|
|
7927
|
+
let pkg;
|
|
7928
|
+
try {
|
|
7929
|
+
pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
7930
|
+
} catch {
|
|
7931
|
+
return [];
|
|
7932
|
+
}
|
|
7933
|
+
|
|
7934
|
+
const patterns = pkg.workspaces || [];
|
|
7935
|
+
const dirs = [];
|
|
7936
|
+
|
|
7937
|
+
// Handle both flat array and object with packages field (Yarn v2 format)
|
|
7938
|
+
const patternArray = Array.isArray(patterns) ? patterns : (patterns.packages || []);
|
|
7939
|
+
|
|
7940
|
+
for (const p of patternArray) {
|
|
7941
|
+
const base = p.replace(/\/\*\*?$/, '');
|
|
7942
|
+
const resolved = path.join(cwd, base);
|
|
7943
|
+
if (fs.existsSync(resolved)) {
|
|
7944
|
+
try {
|
|
7945
|
+
for (const entry of fs.readdirSync(resolved, { withFileTypes: true })) {
|
|
7946
|
+
if (entry.isDirectory()) dirs.push(path.join(resolved, entry.name));
|
|
7947
|
+
}
|
|
7948
|
+
} catch (_) {}
|
|
7949
|
+
}
|
|
7950
|
+
}
|
|
7951
|
+
|
|
7952
|
+
return dirs;
|
|
7953
|
+
}
|
|
7954
|
+
|
|
7955
|
+
// Infer package from query tokens: "add rate limiting to payments" → "packages/payments"
|
|
7956
|
+
function inferPackage(query, workspaceDirs, cwd) {
|
|
7957
|
+
const tokens = query.toLowerCase().split(/\W+/).filter(t => t.length > 2);
|
|
7958
|
+
|
|
7959
|
+
// Find longest matching package name
|
|
7960
|
+
let bestMatch = null;
|
|
7961
|
+
let bestLen = 0;
|
|
7962
|
+
let bestMatchLen = 0;
|
|
7963
|
+
|
|
7964
|
+
for (const dir of workspaceDirs) {
|
|
7965
|
+
const name = path.basename(dir).toLowerCase();
|
|
7966
|
+
for (const token of tokens) {
|
|
7967
|
+
const matchLen = _getMatchLength(name, token);
|
|
7968
|
+
// Only consider matches; use longest match, and break ties by longest package name
|
|
7969
|
+
if (matchLen > 0 && (matchLen > bestLen || (matchLen === bestLen && name.length > bestMatchLen))) {
|
|
7970
|
+
bestMatch = dir;
|
|
7971
|
+
bestLen = matchLen;
|
|
7972
|
+
bestMatchLen = name.length;
|
|
7973
|
+
}
|
|
7974
|
+
}
|
|
7975
|
+
}
|
|
7976
|
+
|
|
7977
|
+
return bestMatch;
|
|
7978
|
+
}
|
|
7979
|
+
|
|
7980
|
+
function _getMatchLength(name, token) {
|
|
7981
|
+
if (name === token) return 1000 + name.length; // Exact match is best
|
|
7982
|
+
if (name.startsWith(token) && token.length >= 3) return 100 + token.length;
|
|
7983
|
+
if (token.startsWith(name) && name.length >= 3) return name.length;
|
|
7984
|
+
return 0;
|
|
7985
|
+
}
|
|
7986
|
+
|
|
7987
|
+
// Return boost multiplier for files inside the inferred package
|
|
7988
|
+
function scopeToPackage(filePath, packageDir) {
|
|
7989
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
7990
|
+
const normalizedPkg = packageDir.replace(/\\/g, '/');
|
|
7991
|
+
|
|
7992
|
+
// Ensure we match the directory boundary, not just a prefix
|
|
7993
|
+
// e.g., packages/payment should not match packages/payment-old
|
|
7994
|
+
if (normalized.startsWith(normalizedPkg)) {
|
|
7995
|
+
const afterPrefix = normalized.slice(normalizedPkg.length);
|
|
7996
|
+
// Check if next char is / or if it's the exact match
|
|
7997
|
+
if (afterPrefix === '' || afterPrefix[0] === '/') {
|
|
7998
|
+
return 0.30;
|
|
7999
|
+
}
|
|
8000
|
+
}
|
|
8001
|
+
return 0;
|
|
8002
|
+
}
|
|
8003
|
+
};
|
|
8004
|
+
|
|
7846
8005
|
/**
|
|
7847
8006
|
* SigMap — gen-context.js v1.2.0
|
|
7848
8007
|
* Zero-dependency AI context engine.
|
|
@@ -7855,7 +8014,7 @@ const path = require('path');
|
|
|
7855
8014
|
const os = require('os');
|
|
7856
8015
|
const { execSync } = require('child_process');
|
|
7857
8016
|
|
|
7858
|
-
const VERSION = '6.
|
|
8017
|
+
const VERSION = '6.10.1';
|
|
7859
8018
|
const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
|
|
7860
8019
|
|
|
7861
8020
|
function requireSourceOrBundled(key) {
|
|
@@ -7935,14 +8094,22 @@ function loadIgnorePatterns(cwd) {
|
|
|
7935
8094
|
function matchesIgnore(relPath, patterns) {
|
|
7936
8095
|
for (const pat of patterns) {
|
|
7937
8096
|
const normalized = pat.replace(/\\/g, '/');
|
|
7938
|
-
//
|
|
7939
|
-
const
|
|
7940
|
-
.
|
|
8097
|
+
// Strip trailing slash (gitignore style — directory patterns)
|
|
8098
|
+
const patternToUse = normalized.endsWith('/')
|
|
8099
|
+
? normalized.slice(0, -1)
|
|
8100
|
+
: normalized;
|
|
8101
|
+
// Escape regex special chars but NOT brackets (keep them for character classes)
|
|
8102
|
+
const regexStr = patternToUse
|
|
8103
|
+
.replace(/[.+^${}()|\\]/g, '\\$&')
|
|
7941
8104
|
.replace(/\*\*/g, '___DOUBLE___')
|
|
7942
8105
|
.replace(/\*/g, '[^/]*')
|
|
7943
8106
|
.replace(/___DOUBLE___/g, '.*');
|
|
7944
|
-
|
|
7945
|
-
|
|
8107
|
+
try {
|
|
8108
|
+
const regex = new RegExp(`(^|/)${regexStr}($|/)`);
|
|
8109
|
+
if (regex.test(relPath)) return true;
|
|
8110
|
+
} catch (_) {
|
|
8111
|
+
// Malformed bracket syntax or invalid regex — skip this pattern
|
|
8112
|
+
}
|
|
7946
8113
|
}
|
|
7947
8114
|
return false;
|
|
7948
8115
|
}
|
|
@@ -8870,7 +9037,7 @@ function runPerModuleStrategy(cwd, config, fileEntries, inputTokenTotal) {
|
|
|
8870
9037
|
overviewLines.push('> Inject the relevant module file into your IDE context window.');
|
|
8871
9038
|
overviewLines.push('> For cross-module questions load both files.');
|
|
8872
9039
|
const overviewContent = overviewLines.join('\n') + '\n';
|
|
8873
|
-
const primaryTargets =
|
|
9040
|
+
const primaryTargets = config.outputs || ['copilot'];
|
|
8874
9041
|
writeOutputs(overviewContent, primaryTargets, cwd, config);
|
|
8875
9042
|
|
|
8876
9043
|
const overviewTokens = estimateTokens(overviewContent);
|
|
@@ -8889,7 +9056,7 @@ function runHotColdStrategy(cwd, config, fileEntries, recentFiles, inputTokenTot
|
|
|
8889
9056
|
const hotContent = hotEntries.length > 0
|
|
8890
9057
|
? formatOutput(hotEntries, cwd, false, config, null)
|
|
8891
9058
|
: '<!-- Generated by SigMap — no recently changed files -->\n';
|
|
8892
|
-
const primaryTargets =
|
|
9059
|
+
const primaryTargets = config.outputs || ['copilot'];
|
|
8893
9060
|
writeOutputs(hotContent, primaryTargets, cwd, config);
|
|
8894
9061
|
const hotTokens = estimateTokens(hotContent);
|
|
8895
9062
|
|
|
@@ -9919,6 +10086,7 @@ function main() {
|
|
|
9919
10086
|
const { detectIntent, buildSigIndex, rank } = requireSourceOrBundled('./src/retrieval/ranker');
|
|
9920
10087
|
const { coverageScore } = requireSourceOrBundled('./src/analysis/coverage-score');
|
|
9921
10088
|
const { loadSession, saveSession, mergeSessionContext } = requireSourceOrBundled('./src/session/memory');
|
|
10089
|
+
const { detectWorkspaces, inferPackage, scopeToPackage } = requireSourceOrBundled('./src/workspace/detector');
|
|
9922
10090
|
|
|
9923
10091
|
const intent = detectIntent(query);
|
|
9924
10092
|
const intentWeights = getIntentWeights(intent);
|
|
@@ -9931,6 +10099,34 @@ function main() {
|
|
|
9931
10099
|
|
|
9932
10100
|
let ranked = rank(query, sigIndex, { topK: 5, weights: intentWeights, cwd });
|
|
9933
10101
|
|
|
10102
|
+
// v6.10: Workspace scoping — infer package from query and apply boost
|
|
10103
|
+
const workspaces = detectWorkspaces(cwd);
|
|
10104
|
+
const packageFlag = args[args.indexOf('--package') + 1];
|
|
10105
|
+
const globalFlag = args.includes('--global');
|
|
10106
|
+
|
|
10107
|
+
let inferredPackage = null;
|
|
10108
|
+
if (!globalFlag && workspaces.length > 0) {
|
|
10109
|
+
if (packageFlag) {
|
|
10110
|
+
inferredPackage = workspaces.find(d => path.basename(d) === packageFlag) || null;
|
|
10111
|
+
if (!packageFlag || !inferredPackage) {
|
|
10112
|
+
process.stderr.write(`[sigmap] ⚠ package "${packageFlag}" not found — searching entire repo\n`);
|
|
10113
|
+
}
|
|
10114
|
+
} else {
|
|
10115
|
+
inferredPackage = inferPackage(query, workspaces, cwd);
|
|
10116
|
+
}
|
|
10117
|
+
}
|
|
10118
|
+
|
|
10119
|
+
if (inferredPackage) {
|
|
10120
|
+
ranked = ranked.map(r => ({
|
|
10121
|
+
...r,
|
|
10122
|
+
score: r.score + scopeToPackage(r.file, inferredPackage),
|
|
10123
|
+
})).sort((a, b) => b.score - a.score);
|
|
10124
|
+
|
|
10125
|
+
if (!args.includes('--json') && !globalFlag) {
|
|
10126
|
+
process.stderr.write(`[sigmap] 📦 package scope: ${path.relative(cwd, inferredPackage)}\n`);
|
|
10127
|
+
}
|
|
10128
|
+
}
|
|
10129
|
+
|
|
9934
10130
|
// v6.8: --followup support — carry session context forward
|
|
9935
10131
|
const isFollowup = args.includes('--followup');
|
|
9936
10132
|
const session = isFollowup ? loadSession(cwd) : null;
|
|
@@ -10994,6 +11190,7 @@ function main() {
|
|
|
10994
11190
|
// Priority: --output flag > --adapter flag > buildSigIndex probe order
|
|
10995
11191
|
// (customOutput from config is handled inside buildSigIndex itself)
|
|
10996
11192
|
let queryOpts;
|
|
11193
|
+
const adpIdx = args.indexOf('--adapter');
|
|
10997
11194
|
|
|
10998
11195
|
// 1. --output <file> pins to an explicit path
|
|
10999
11196
|
if (config.customOutput) {
|
|
@@ -11001,17 +11198,14 @@ function main() {
|
|
|
11001
11198
|
}
|
|
11002
11199
|
|
|
11003
11200
|
// 2. --adapter <name> pins to that adapter's output path (if --output not given)
|
|
11004
|
-
if (!queryOpts) {
|
|
11005
|
-
const
|
|
11006
|
-
|
|
11007
|
-
|
|
11008
|
-
|
|
11009
|
-
|
|
11010
|
-
|
|
11011
|
-
|
|
11012
|
-
queryOpts = { contextPath: adapterMod.outputPath(cwd) };
|
|
11013
|
-
} catch (_) {}
|
|
11014
|
-
}
|
|
11201
|
+
if (!queryOpts && adpIdx >= 0) {
|
|
11202
|
+
const adapterName = (args[adpIdx + 1] || '').trim().toLowerCase();
|
|
11203
|
+
const VALID_ADAPTERS = ['copilot', 'claude', 'cursor', 'windsurf', 'openai', 'gemini', 'codex'];
|
|
11204
|
+
if (VALID_ADAPTERS.includes(adapterName)) {
|
|
11205
|
+
try {
|
|
11206
|
+
const adapterMod = __require('./packages/adapters/' + adapterName);
|
|
11207
|
+
queryOpts = { contextPath: adapterMod.outputPath(cwd) };
|
|
11208
|
+
} catch (_) {}
|
|
11015
11209
|
}
|
|
11016
11210
|
}
|
|
11017
11211
|
|