sigmap 6.4.0 → 6.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 +102 -111
- package/CHANGELOG.md +32 -0
- package/README.md +9 -8
- package/gen-context.js +59 -2
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/core/package.json +1 -1
- package/src/config/loader.js +26 -2
- package/src/discovery/framework-detector.js +88 -0
- package/src/discovery/language-detector.js +74 -0
- package/src/discovery/sigmapignore.js +29 -0
- package/src/discovery/source-root-registry.js +166 -0
- package/src/discovery/source-root-resolver.js +181 -0
- package/src/discovery/source-root-scorer.js +98 -0
- package/src/mcp/server.js +1 -1
- package/src/retrieval/ranker.js +88 -23
package/AGENTS.md
CHANGED
|
@@ -47,16 +47,24 @@ Use this marker block for all appendable context files:
|
|
|
47
47
|
<!-- Updated by gen-context.js -->
|
|
48
48
|
# Code signatures
|
|
49
49
|
|
|
50
|
-
##
|
|
50
|
+
## SigMap commands
|
|
51
|
+
|
|
52
|
+
| When | Command |
|
|
53
|
+
|------|---------|
|
|
54
|
+
| Before answering a question | `sigmap ask "<your question>"` |
|
|
55
|
+
| After code changes | `sigmap validate` |
|
|
56
|
+
| To query by topic | `sigmap --query "<topic>"` |
|
|
57
|
+
|
|
58
|
+
Always run `sigmap ask` or `sigmap --query` before searching for files relevant to a task.
|
|
59
|
+
## changes (last 5 commits — 6 minutes ago)
|
|
51
60
|
```
|
|
52
|
-
src/
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
packages/adapters/windsurf.js ~format
|
|
61
|
+
src/config/loader.js +_legacyDetectAutoSrcDirs ~detectAutoSrcDirs
|
|
62
|
+
src/discovery/language-detector.js +detectLanguages +_walkDepth
|
|
63
|
+
src/discovery/framework-detector.js +detectFrameworks +_readDeps +_readFile +_existsAnywhere
|
|
64
|
+
src/discovery/source-root-resolver.js +resolveSourceRoots +_detectMonorepo +_enumerateCandidates +_applySpecialRules
|
|
65
|
+
src/discovery/source-root-scorer.js +getRecentlyChangedDirs +scoreCandidate +_countSourceFiles
|
|
66
|
+
src/discovery/sigmapignore.js +loadIgnorePatterns +matchesIgnorePattern
|
|
67
|
+
src/retrieval/ranker.js +_computePenalty ~scoreFile ~rank ~buildSigIndex
|
|
60
68
|
```
|
|
61
69
|
|
|
62
70
|
## packages
|
|
@@ -116,21 +124,21 @@ function score(cwd) → { * score: number, * grad
|
|
|
116
124
|
function adapt(context, adapterName, opts = {}) → string
|
|
117
125
|
```
|
|
118
126
|
|
|
119
|
-
### packages/adapters/
|
|
127
|
+
### packages/adapters/copilot.js
|
|
120
128
|
```
|
|
121
129
|
module.exports = { name, format, outputPath, write }
|
|
122
130
|
function format(context, opts = {}) → string
|
|
131
|
+
function _confidenceMeta(opts)
|
|
123
132
|
function outputPath(cwd) → string
|
|
124
133
|
function write(context, cwd, opts = {})
|
|
125
134
|
```
|
|
126
135
|
|
|
127
|
-
### packages/adapters/
|
|
136
|
+
### packages/adapters/cursor.js
|
|
128
137
|
```
|
|
129
|
-
module.exports = { name, format, outputPath
|
|
138
|
+
module.exports = { name, format, outputPath }
|
|
130
139
|
function format(context, opts = {}) → string
|
|
131
140
|
function _confidenceMeta(opts)
|
|
132
141
|
function outputPath(cwd) → string
|
|
133
|
-
function write(context, cwd, opts = {})
|
|
134
142
|
```
|
|
135
143
|
|
|
136
144
|
### packages/adapters/gemini.js
|
|
@@ -142,16 +150,15 @@ function write(context, cwd, opts = {})
|
|
|
142
150
|
function _confidenceMeta(opts)
|
|
143
151
|
```
|
|
144
152
|
|
|
145
|
-
### packages/adapters/
|
|
153
|
+
### packages/adapters/openai.js
|
|
146
154
|
```
|
|
147
|
-
module.exports = { name, format, outputPath
|
|
155
|
+
module.exports = { name, format, outputPath }
|
|
148
156
|
function format(context, opts = {}) → string
|
|
149
|
-
function _confidenceMeta(opts)
|
|
150
157
|
function outputPath(cwd) → string
|
|
151
|
-
function
|
|
158
|
+
function _confidenceMeta(opts)
|
|
152
159
|
```
|
|
153
160
|
|
|
154
|
-
### packages/adapters/
|
|
161
|
+
### packages/adapters/windsurf.js
|
|
155
162
|
```
|
|
156
163
|
module.exports = { name, format, outputPath }
|
|
157
164
|
function format(context, opts = {}) → string
|
|
@@ -159,91 +166,25 @@ function _confidenceMeta(opts)
|
|
|
159
166
|
function outputPath(cwd) → string
|
|
160
167
|
```
|
|
161
168
|
|
|
162
|
-
### packages/adapters/
|
|
169
|
+
### packages/adapters/codex.js
|
|
163
170
|
```
|
|
164
|
-
module.exports = { name, format, outputPath }
|
|
171
|
+
module.exports = { name, format, outputPath, write }
|
|
165
172
|
function format(context, opts = {}) → string
|
|
166
173
|
function outputPath(cwd) → string
|
|
167
|
-
function
|
|
174
|
+
function write(context, cwd, opts = {})
|
|
168
175
|
```
|
|
169
176
|
|
|
170
|
-
### packages/adapters/
|
|
177
|
+
### packages/adapters/claude.js
|
|
171
178
|
```
|
|
172
|
-
module.exports = { name, format, outputPath }
|
|
179
|
+
module.exports = { name, format, outputPath, write }
|
|
173
180
|
function format(context, opts = {}) → string
|
|
174
181
|
function _confidenceMeta(opts)
|
|
175
182
|
function outputPath(cwd) → string
|
|
183
|
+
function write(context, cwd, opts = {})
|
|
176
184
|
```
|
|
177
185
|
|
|
178
186
|
## src
|
|
179
187
|
|
|
180
|
-
### src/security/patterns.js
|
|
181
|
-
```
|
|
182
|
-
module.exports = { PATTERNS }
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
### src/security/scanner.js
|
|
186
|
-
```
|
|
187
|
-
module.exports = { scan }
|
|
188
|
-
function scan(signatures, filePath) → { safe: string[], redacte
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
### src/extractors/cpp.js
|
|
192
|
-
```
|
|
193
|
-
module.exports = { extract }
|
|
194
|
-
function extract(src) → string[]
|
|
195
|
-
function extractBlock(src, startIndex)
|
|
196
|
-
function extractMembers(block)
|
|
197
|
-
function normalizeParams(params)
|
|
198
|
-
function normalizeType(type)
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
### src/extractors/csharp.js
|
|
202
|
-
```
|
|
203
|
-
module.exports = { extract }
|
|
204
|
-
function extract(src) → string[]
|
|
205
|
-
function extractBlock(src, startIndex)
|
|
206
|
-
function extractMembers(block)
|
|
207
|
-
function normalizeParams(params)
|
|
208
|
-
function normalizeType(type)
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
### src/extractors/dart.js
|
|
212
|
-
```
|
|
213
|
-
module.exports = { extract }
|
|
214
|
-
function extract(src) → string[]
|
|
215
|
-
function extractBlock(src, startIndex)
|
|
216
|
-
function extractMembers(block)
|
|
217
|
-
function normalizeParams(params)
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
### src/extractors/deps.js
|
|
221
|
-
```
|
|
222
|
-
module.exports = { extractPythonDeps, extractTSDeps, buildReverseDepMap }
|
|
223
|
-
function extractPythonDeps(src) → string[]
|
|
224
|
-
function extractTSDeps(src) → string[]
|
|
225
|
-
function buildReverseDepMap(forwardMap) → Map<string, string[]>
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
### src/extractors/go.js
|
|
229
|
-
```
|
|
230
|
-
module.exports = { extract }
|
|
231
|
-
function extract(src) → string[]
|
|
232
|
-
function extractBlock(src, startIndex)
|
|
233
|
-
function extractInterfaceMethods(block)
|
|
234
|
-
function normalizeParams(params)
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
### src/extractors/java.js
|
|
238
|
-
```
|
|
239
|
-
module.exports = { extract }
|
|
240
|
-
function extract(src) → string[]
|
|
241
|
-
function extractBlock(src, startIndex)
|
|
242
|
-
function extractMembers(block)
|
|
243
|
-
function normalizeParams(params)
|
|
244
|
-
function normalizeType(type)
|
|
245
|
-
```
|
|
246
|
-
|
|
247
188
|
### src/extractors/javascript.js
|
|
248
189
|
```
|
|
249
190
|
module.exports = { extract }
|
|
@@ -547,15 +488,6 @@ function formatAnalysisJSON(stats) → object
|
|
|
547
488
|
module.exports = { DEFAULTS }
|
|
548
489
|
```
|
|
549
490
|
|
|
550
|
-
### src/config/loader.js
|
|
551
|
-
```
|
|
552
|
-
module.exports = { loadConfig, loadBaseConfig }
|
|
553
|
-
function loadBaseConfig(extendsVal, cwd)
|
|
554
|
-
function detectAutoSrcDirs(cwd, excludeList) → string[]
|
|
555
|
-
function loadConfig(cwd) → object
|
|
556
|
-
function deepClone(obj)
|
|
557
|
-
```
|
|
558
|
-
|
|
559
491
|
### src/format/dashboard.js
|
|
560
492
|
```
|
|
561
493
|
module.exports = { generateDashboardHtml, renderHistoryCharts, computeExtractorCoverage, percentile, overBudgetStreak }
|
|
@@ -643,18 +575,6 @@ function queryContext(args, cwd)
|
|
|
643
575
|
function getImpact(args, cwd)
|
|
644
576
|
```
|
|
645
577
|
|
|
646
|
-
### src/retrieval/ranker.js
|
|
647
|
-
```
|
|
648
|
-
module.exports = { rank, buildSigIndex, scoreFile, formatRankTable, formatRankJSON, DEFAULT_WEIGHTS, detectIntent }
|
|
649
|
-
function scoreFile(filePath, sigs, queryTokens, weights) → number
|
|
650
|
-
function rank(query, sigIndex, opts) → { file: string, score: nu
|
|
651
|
-
function _parseContextFile(contextPath) → Map<string, string[]>
|
|
652
|
-
function buildSigIndex(cwd, opts) → Map<string, string[]>
|
|
653
|
-
function formatRankTable(results, query) → string
|
|
654
|
-
function formatRankJSON(results, query) → object
|
|
655
|
-
function detectIntent(query)
|
|
656
|
-
```
|
|
657
|
-
|
|
658
578
|
### src/tracking/logger.js
|
|
659
579
|
```
|
|
660
580
|
module.exports = { logRun, readLog, summarize }
|
|
@@ -690,6 +610,77 @@ function exportWeights(cwd, outputPath)
|
|
|
690
610
|
function importWeights(cwd, importPath, replace)
|
|
691
611
|
```
|
|
692
612
|
|
|
613
|
+
### src/config/loader.js
|
|
614
|
+
```
|
|
615
|
+
module.exports = { loadConfig, loadBaseConfig }
|
|
616
|
+
function loadBaseConfig(extendsVal, cwd)
|
|
617
|
+
function detectAutoSrcDirs(cwd, excludeList) → string[]
|
|
618
|
+
function _legacyDetectAutoSrcDirs(cwd, excludeList) → string[]
|
|
619
|
+
function loadConfig(cwd) → object
|
|
620
|
+
function deepClone(obj)
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
### src/discovery/language-detector.js
|
|
624
|
+
```
|
|
625
|
+
module.exports = { detectLanguages }
|
|
626
|
+
function detectLanguages(cwd)
|
|
627
|
+
function _walkDepth(dir, depth, extCount)
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
### src/discovery/framework-detector.js
|
|
631
|
+
```
|
|
632
|
+
module.exports = { detectFrameworks }
|
|
633
|
+
function detectFrameworks(cwd)
|
|
634
|
+
function _readDeps(cwd)
|
|
635
|
+
function _readFile(p)
|
|
636
|
+
function _existsAnywhere(cwd, filename, maxDepth)
|
|
637
|
+
function _walkFind(dir, name, depth)
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
### src/discovery/source-root-registry.js
|
|
641
|
+
```
|
|
642
|
+
module.exports = { REGISTRY }
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
### src/discovery/source-root-resolver.js
|
|
646
|
+
```
|
|
647
|
+
module.exports = { resolveSourceRoots }
|
|
648
|
+
function resolveSourceRoots(cwd, opts = {})
|
|
649
|
+
function _detectMonorepo(cwd)
|
|
650
|
+
function _enumerateCandidates(cwd, isMonorepo, ignorePatterns, excludeList)
|
|
651
|
+
function _applySpecialRules(scored, cwd, primaryFw, fwEntry, frameworks)
|
|
652
|
+
function _dedupeNested(scored)
|
|
653
|
+
function _computeConfidence(frameworks, languages, scoredCount)
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
### src/discovery/source-root-scorer.js
|
|
657
|
+
```
|
|
658
|
+
module.exports = { scoreCandidate, getRecentlyChangedDirs, ROOT_ENTRYPOINTS }
|
|
659
|
+
function getRecentlyChangedDirs(cwd)
|
|
660
|
+
function scoreCandidate(dirName, fullPath, context)
|
|
661
|
+
function _countSourceFiles(dir, depth)
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
### src/discovery/sigmapignore.js
|
|
665
|
+
```
|
|
666
|
+
module.exports = { loadIgnorePatterns, matchesIgnorePattern }
|
|
667
|
+
function loadIgnorePatterns(cwd)
|
|
668
|
+
function matchesIgnorePattern(dirName, patterns)
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
### src/retrieval/ranker.js
|
|
672
|
+
```
|
|
673
|
+
module.exports = { rank, buildSigIndex, scoreFile, formatRankTable, formatRankJSON, DEFAULT_WEIGHTS, detectIntent }
|
|
674
|
+
function _computePenalty(filePath)
|
|
675
|
+
function scoreFile(filePath, sigs, queryTokens, weights) → { score: number, signals:
|
|
676
|
+
function rank(query, sigIndex, opts) → { file: string, score: nu
|
|
677
|
+
function _parseContextFile(contextPath) → Map<string, string[]>
|
|
678
|
+
function buildSigIndex(cwd, opts) → Map<string, string[]>
|
|
679
|
+
function formatRankTable(results, query) → string
|
|
680
|
+
function formatRankJSON(results, query) → object
|
|
681
|
+
function detectIntent(query)
|
|
682
|
+
```
|
|
683
|
+
|
|
693
684
|
### src/mcp/server.js
|
|
694
685
|
```
|
|
695
686
|
module.exports = { start }
|
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,38 @@ Format: [Semantic Versioning](https://semver.org/)
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
+
## [6.5.1] — 2026-04-25
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **Retrieval explain** — `rank()` and `scoreFile()` now return detailed signal breakdown (exactToken, symbolMatch, prefixMatch, pathMatch, penalty) for transparency in ranking decisions
|
|
18
|
+
- **7-intent ranking** — expanded intent detection from 4 to 7 patterns (debug, explain, refactor, review, test, integrate, navigate). Each intent applies tuned weights to prioritize relevant signals.
|
|
19
|
+
- **Negative-signal penalty layer** — formalized penalties for test files (0.4x), generated code (0.3x), documentation (0.2x), and node_modules (0.0x) to deprioritize non-source content
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- `formatRankTable` now shows penalty column and signals breakdown for top 3 results
|
|
24
|
+
- `formatRankJSON` now includes `intent` and `signals` fields in output for API consumers
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## [6.5.0] — 2026-04-25
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- **Source Root Resolver (v6.5)** — intelligent auto-detection of source directories for 17 languages and 50+ frameworks (Next.js, Django, Rails, Spring Boot, Flutter, Go, Rust, etc.). Uses multi-signal scoring: manifest files, language/framework detection, file density, git activity, and framework-specific srcDirs. Returns confidence level (high/medium/low) and detailed explanation. Integrated into `loadConfig()` with graceful fallback to legacy heuristics.
|
|
33
|
+
- **`.sigmapignore` pattern matching** — new `.sigmapignore` file support (fallback to `.contextignore`) for excluding directories. Supports simple patterns like `legacy/` and globs like `src/**`.
|
|
34
|
+
- **`sigmap roots` CLI command** — three modes: `--explain` (default, shows detected languages/frameworks and scores), `--json` (structured output), `--fix` (interactive prompt to correct srcDirs and write to config).
|
|
35
|
+
- **Monorepo detection and enumeration** — auto-detects monorepos via pnpm-workspace.yaml, turbo.json, nx.json, lerna.json, and package.json workspaces. Enumerates all sub-packages and common deep paths.
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- **Framework-discovery tests** — updated registry entries to include all framework-specific srcDirs expected by legacy detector (Rails: db/spec/test, Laravel: resources/tests, Angular: projects/apps/libs, Next: hooks/utils).
|
|
40
|
+
- **Scoring penalty for framework srcDirs** — test directories (spec, test, tests) no longer penalized when explicitly in framework's srcDirs list.
|
|
41
|
+
- **CLI command ordering** — `roots` command handler now executes before `explain` to prevent flag conflict.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
13
45
|
## [6.4.0] — 2026-04-23
|
|
14
46
|
|
|
15
47
|
### Changed
|
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
**SigMap finds the right files before your AI answers.**
|
|
8
8
|
|
|
9
9
|
[](https://www.npmjs.com/package/sigmap)
|
|
10
|
+
[](https://www.npmjs.com/package/sigmap)
|
|
10
11
|
[](https://github.com/manojmallick/sigmap/actions/workflows/ci.yml)
|
|
11
12
|
[](package.json)
|
|
12
13
|
[](LICENSE)
|
|
@@ -37,10 +38,10 @@ Works with Copilot, Claude, Cursor, Windsurf, and any LLM.
|
|
|
37
38
|
|
|
38
39
|
## Why SigMap?
|
|
39
40
|
|
|
40
|
-
- **
|
|
41
|
+
- **81.1% hit@5** — right file found in top 5 results (vs 13.6% baseline)
|
|
41
42
|
- **40–98% token reduction** — 2K–4K tokens instead of 80K+
|
|
42
43
|
- **52.2% task success rate** — up from 10% without context
|
|
43
|
-
- **1.
|
|
44
|
+
- **1.69 prompts per task** — down from 2.84
|
|
44
45
|
- **Works with any LLM** — no API key, no cloud, no accounts
|
|
45
46
|
- **Zero npm dependencies** — `npx sigmap` on any machine
|
|
46
47
|
|
|
@@ -50,7 +51,7 @@ Works with Copilot, Claude, Cursor, Windsurf, and any LLM.
|
|
|
50
51
|
|
|
51
52
|
| Without SigMap | With SigMap |
|
|
52
53
|
|---|---|
|
|
53
|
-
| ❌ Guessing which files are relevant | ✅ Right file in context —
|
|
54
|
+
| ❌ Guessing which files are relevant | ✅ Right file in context — 81% of the time |
|
|
54
55
|
| ❌ Sending the full repo to your AI | ✅ Minimal context — only what matters |
|
|
55
56
|
| ❌ Embeddings / vector DB required | ✅ Grounded answers, no infra needed |
|
|
56
57
|
|
|
@@ -74,13 +75,13 @@ Ask → Rank → Context → Validate → Judge → Learn
|
|
|
74
75
|
## Benchmark
|
|
75
76
|
|
|
76
77
|
```
|
|
77
|
-
Benchmark : sigmap-v6.
|
|
78
|
-
Date : 2026-04-
|
|
78
|
+
Benchmark : sigmap-v6.5-main
|
|
79
|
+
Date : 2026-04-25
|
|
79
80
|
|
|
80
|
-
Hit@5 :
|
|
81
|
-
Prompt reduction :
|
|
81
|
+
Hit@5 : 81.1% (baseline 13.6% — 6.0× lift)
|
|
82
|
+
Prompt reduction : 41.4%
|
|
82
83
|
Task success : 52.2% (baseline 10%)
|
|
83
|
-
Prompts / task : 1.
|
|
84
|
+
Prompts / task : 1.69 (baseline 2.84)
|
|
84
85
|
Token reduction: 40–98% (avg 96.9% across 18 real repos)
|
|
85
86
|
```
|
|
86
87
|
|
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.5.1',
|
|
5391
5391
|
description: 'SigMap MCP server — code signatures on demand',
|
|
5392
5392
|
};
|
|
5393
5393
|
|
|
@@ -7222,7 +7222,7 @@ const path = require('path');
|
|
|
7222
7222
|
const os = require('os');
|
|
7223
7223
|
const { execSync } = require('child_process');
|
|
7224
7224
|
|
|
7225
|
-
const VERSION = '6.
|
|
7225
|
+
const VERSION = '6.5.1';
|
|
7226
7226
|
const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
|
|
7227
7227
|
|
|
7228
7228
|
function requireSourceOrBundled(key) {
|
|
@@ -8477,6 +8477,15 @@ function runGenerate(cwd, config, reportMode, reportJson = false) {
|
|
|
8477
8477
|
fileEntries.push({ filePath, sigs, deps: extractFileDeps(filePath, content, config), content, mtime });
|
|
8478
8478
|
}
|
|
8479
8479
|
|
|
8480
|
+
// v6.5: Coverage feedback loop — warn if symbol density is suspiciously low
|
|
8481
|
+
const symbolsFound = fileEntries.reduce((n, f) => n + (f.sigs?.length || 0), 0);
|
|
8482
|
+
const filesFound = fileEntries.length;
|
|
8483
|
+
const coverageRatio = filesFound > 0 ? symbolsFound / filesFound : 1;
|
|
8484
|
+
if (coverageRatio < 0.25 && !config.srcDirs?.length) {
|
|
8485
|
+
process.stderr.write('[sigmap] ⚠ low symbol coverage — source roots may be wrong\n');
|
|
8486
|
+
process.stderr.write('[sigmap] run: sigmap roots --explain\n');
|
|
8487
|
+
}
|
|
8488
|
+
|
|
8480
8489
|
const strategy = config.strategy || 'full';
|
|
8481
8490
|
const beforeCount = fileEntries.length;
|
|
8482
8491
|
|
|
@@ -9797,6 +9806,54 @@ function main() {
|
|
|
9797
9806
|
process.exit(0);
|
|
9798
9807
|
}
|
|
9799
9808
|
|
|
9809
|
+
// v6.5: `sigmap roots` — detect source roots
|
|
9810
|
+
if (args[0] === 'roots') {
|
|
9811
|
+
const { resolveSourceRoots } = requireSourceOrBundled('./src/discovery/source-root-resolver');
|
|
9812
|
+
const result = resolveSourceRoots(cwd);
|
|
9813
|
+
|
|
9814
|
+
if (args.includes('--json')) {
|
|
9815
|
+
console.log(JSON.stringify(result, null, 2));
|
|
9816
|
+
process.exit(0);
|
|
9817
|
+
}
|
|
9818
|
+
|
|
9819
|
+
if (args.includes('--fix')) {
|
|
9820
|
+
console.log('[sigmap] Current detected roots:', result.roots.join(', '));
|
|
9821
|
+
const readline = require('readline');
|
|
9822
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
9823
|
+
rl.question('Enter correct srcDirs (comma-separated): ', answer => {
|
|
9824
|
+
const dirs = answer.split(',').map(d => d.trim()).filter(Boolean);
|
|
9825
|
+
const cfgPath = path.join(cwd, 'gen-context.config.json');
|
|
9826
|
+
const cfg = fs.existsSync(cfgPath) ? JSON.parse(fs.readFileSync(cfgPath, 'utf8')) : {};
|
|
9827
|
+
cfg.srcDirs = dirs;
|
|
9828
|
+
fs.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2) + '\n');
|
|
9829
|
+
console.log(`[sigmap] Saved srcDirs: ${dirs.join(', ')} → gen-context.config.json`);
|
|
9830
|
+
rl.close();
|
|
9831
|
+
process.exit(0);
|
|
9832
|
+
});
|
|
9833
|
+
return;
|
|
9834
|
+
}
|
|
9835
|
+
|
|
9836
|
+
// --explain (default)
|
|
9837
|
+
console.log('\nDetected languages:');
|
|
9838
|
+
for (const l of result.languages.slice(0, 4)) {
|
|
9839
|
+
console.log(` ${l.name.padEnd(16)} ${l.weight.toFixed(2)}`);
|
|
9840
|
+
}
|
|
9841
|
+
console.log('\nDetected frameworks:');
|
|
9842
|
+
if (result.frameworks.length === 0) console.log(' (none)');
|
|
9843
|
+
for (const f of result.frameworks.slice(0, 3)) {
|
|
9844
|
+
console.log(` ${f.name.padEnd(16)} ${f.confidence.toFixed(2)}`);
|
|
9845
|
+
}
|
|
9846
|
+
console.log('\nChosen source roots:');
|
|
9847
|
+
if (result.roots.length === 0) console.log(' (none — legacy fallback used)');
|
|
9848
|
+
result.roots.forEach((r, i) => {
|
|
9849
|
+
const exp = result.explanation?.find(e => e.dir === r);
|
|
9850
|
+
console.log(` ${i + 1}. ${r.padEnd(20)} ${exp ? 'score ' + exp.score : ''}`);
|
|
9851
|
+
});
|
|
9852
|
+
console.log('\nMonorepo:', result.isMonorepo ? 'yes' : 'no');
|
|
9853
|
+
console.log('Confidence:', result.confidence);
|
|
9854
|
+
process.exit(0);
|
|
9855
|
+
}
|
|
9856
|
+
|
|
9800
9857
|
// Feature 1: `sigmap explain <file>` — why a file is included or excluded
|
|
9801
9858
|
if (args[0] === 'explain' || args.includes('--explain')) {
|
|
9802
9859
|
const target = args[0] === 'explain'
|
package/package.json
CHANGED
package/src/config/loader.js
CHANGED
|
@@ -89,14 +89,38 @@ const SUPPORTED_CODE_EXTS = new Set([
|
|
|
89
89
|
]);
|
|
90
90
|
|
|
91
91
|
/**
|
|
92
|
-
* Detect source directories for the given project root
|
|
93
|
-
*
|
|
92
|
+
* Detect source directories for the given project root.
|
|
93
|
+
* Uses smart resolver (v6.5+) with fallback to legacy heuristics.
|
|
94
94
|
*
|
|
95
95
|
* @param {string} cwd - Project root
|
|
96
96
|
* @param {string[]} excludeList - Folders to skip
|
|
97
97
|
* @returns {string[]}
|
|
98
98
|
*/
|
|
99
99
|
function detectAutoSrcDirs(cwd, excludeList) {
|
|
100
|
+
try {
|
|
101
|
+
const { resolveSourceRoots } = require('../discovery/source-root-resolver');
|
|
102
|
+
const result = resolveSourceRoots(cwd, { exclude: excludeList || [] });
|
|
103
|
+
if (result.roots.length > 0) {
|
|
104
|
+
if (result.confidence === 'low') {
|
|
105
|
+
process.stderr.write(
|
|
106
|
+
'[sigmap] low confidence root detection — run "sigmap roots --explain" to verify\n'
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
return result.roots;
|
|
110
|
+
}
|
|
111
|
+
} catch (_) {}
|
|
112
|
+
|
|
113
|
+
return _legacyDetectAutoSrcDirs(cwd, excludeList);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Legacy source directory detection (fallback).
|
|
118
|
+
*
|
|
119
|
+
* @param {string} cwd - Project root
|
|
120
|
+
* @param {string[]} excludeList - Folders to skip
|
|
121
|
+
* @returns {string[]}
|
|
122
|
+
*/
|
|
123
|
+
function _legacyDetectAutoSrcDirs(cwd, excludeList) {
|
|
100
124
|
const excludeSet = new Set(excludeList || []);
|
|
101
125
|
const candidates = new Set(DEFAULTS.srcDirs);
|
|
102
126
|
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { REGISTRY } = require('./source-root-registry');
|
|
6
|
+
|
|
7
|
+
module.exports = { detectFrameworks };
|
|
8
|
+
|
|
9
|
+
function detectFrameworks(cwd) {
|
|
10
|
+
const detected = [];
|
|
11
|
+
|
|
12
|
+
for (const [lang, reg] of Object.entries(REGISTRY)) {
|
|
13
|
+
if (!reg.frameworks) continue;
|
|
14
|
+
for (const [name, fw] of Object.entries(reg.frameworks)) {
|
|
15
|
+
let confidence = 0;
|
|
16
|
+
|
|
17
|
+
// Detection files: +0.95 / 0.93 / 0.90 depending on specificity
|
|
18
|
+
for (const f of (fw.detectionFiles || [])) {
|
|
19
|
+
if (_existsAnywhere(cwd, f, 3)) { confidence = Math.max(confidence, 0.93); }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Detection deps in package.json
|
|
23
|
+
if (fw.detectionDeps?.length) {
|
|
24
|
+
const deps = _readDeps(cwd);
|
|
25
|
+
for (const dep of fw.detectionDeps) {
|
|
26
|
+
if (deps.has(dep)) { confidence = Math.max(confidence, 0.90); }
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// go.mod and Cargo.toml deps
|
|
31
|
+
if (lang === 'go' && fw.detectionDeps?.length) {
|
|
32
|
+
const goMod = _readFile(path.join(cwd, 'go.mod'));
|
|
33
|
+
for (const dep of fw.detectionDeps) {
|
|
34
|
+
if (goMod.includes(dep)) { confidence = Math.max(confidence, 0.90); }
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (lang === 'rust' && fw.detectionDeps?.length) {
|
|
38
|
+
const cargoToml = _readFile(path.join(cwd, 'Cargo.toml'));
|
|
39
|
+
for (const dep of fw.detectionDeps) {
|
|
40
|
+
if (cargoToml.includes(dep)) { confidence = Math.max(confidence, 0.88); }
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Special rules
|
|
45
|
+
if (fw.specialRule === 'django-app-dirs' && fs.existsSync(path.join(cwd, 'manage.py'))) {
|
|
46
|
+
confidence = Math.max(confidence, 0.95);
|
|
47
|
+
}
|
|
48
|
+
if (fw.specialRule === 'swift-project-dir' && _existsAnywhere(cwd, '.xcodeproj', 2)) {
|
|
49
|
+
confidence = Math.max(confidence, 0.90);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (confidence > 0) detected.push({ name, language: lang, confidence });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return detected.sort((a, b) => b.confidence - a.confidence);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function _readDeps(cwd) {
|
|
60
|
+
try {
|
|
61
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));
|
|
62
|
+
return new Set([...Object.keys(pkg.dependencies || {}), ...Object.keys(pkg.devDependencies || {})]);
|
|
63
|
+
} catch { return new Set(); }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function _readFile(p) {
|
|
67
|
+
try { return fs.readFileSync(p, 'utf8'); } catch { return ''; }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function _existsAnywhere(cwd, filename, maxDepth) {
|
|
71
|
+
const parts = filename.split('/');
|
|
72
|
+
if (parts.length > 1) return fs.existsSync(path.join(cwd, filename));
|
|
73
|
+
return _walkFind(cwd, filename, maxDepth);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function _walkFind(dir, name, depth) {
|
|
77
|
+
if (depth <= 0) return false;
|
|
78
|
+
try {
|
|
79
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
80
|
+
for (const e of entries) {
|
|
81
|
+
if (e.name === name) return true;
|
|
82
|
+
if (e.isDirectory() && depth > 1) {
|
|
83
|
+
if (_walkFind(path.join(dir, e.name), name, depth - 1)) return true;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
} catch (_) {}
|
|
87
|
+
return false;
|
|
88
|
+
}
|