sigmap 4.0.0 → 4.0.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 CHANGED
@@ -12,7 +12,7 @@ Use this marker block for all appendable context files:
12
12
  ## Auto-generated signatures
13
13
  <!-- Updated by gen-context.js -->
14
14
  You are a coding assistant with full knowledge of this codebase.
15
- Below are the code signatures extracted by SigMap v4.0.0 on 2026-04-15T05:40:32.012Z.
15
+ Below are the code signatures extracted by SigMap v4.0.0 on 2026-04-15T06:03:22.464Z.
16
16
 
17
17
  Use these signatures to answer questions about the code accurately.
18
18
 
@@ -23,24 +23,24 @@ Use these signatures to answer questions about the code accurately.
23
23
 
24
24
  # Code signatures
25
25
 
26
- ## changes (last 5 commits — 6 hours ago)
26
+ ## changes (last 5 commits — 7 minutes ago)
27
27
  ```
28
+ src/analysis/coverage-score.js +coverageScore +_walk
29
+ src/eval/analyzer.js ~analyzeFiles
28
30
  src/extractors/generic.js +extract
29
31
  src/format/llm-txt.js +outputPath +format
30
32
  src/format/llms-txt.js +outputPath +getShortCommit +detectVersion +format
33
+ packages/adapters/claude.js +_confidenceMeta ~format
34
+ packages/adapters/copilot.js +_confidenceMeta ~format
35
+ packages/adapters/cursor.js +_confidenceMeta ~format
36
+ packages/adapters/gemini.js +_confidenceMeta ~format ~write
31
37
  packages/adapters/llm-full.js +outputPath +format +write
38
+ packages/adapters/openai.js +_confidenceMeta ~format ~outputPath
39
+ packages/adapters/windsurf.js +_confidenceMeta ~format
32
40
  ```
33
41
 
34
42
  ## packages
35
43
 
36
- ### packages/adapters/llm-full.js
37
- ```
38
- module.exports = { name: 'llm-full', format, outputPath, write }
39
- function outputPath(cwd)
40
- function format(context, opts)
41
- function write(context, cwd, opts)
42
- ```
43
-
44
44
  ### packages/adapters/claude.js
45
45
  ```
46
46
  module.exports = { name, format, outputPath, write }
@@ -50,14 +50,6 @@ function outputPath(cwd) → string
50
50
  function write(context, cwd, opts = {})
51
51
  ```
52
52
 
53
- ### packages/adapters/codex.js
54
- ```
55
- module.exports = { name, format, outputPath, write }
56
- function format(context, opts = {}) → string
57
- function outputPath(cwd) → string
58
- function write(context, cwd, opts = {})
59
- ```
60
-
61
53
  ### packages/adapters/copilot.js
62
54
  ```
63
55
  module.exports = { name, format, outputPath, write }
@@ -84,13 +76,12 @@ function write(context, cwd, opts = {})
84
76
  function _confidenceMeta(opts)
85
77
  ```
86
78
 
87
- ### packages/adapters/index.js
79
+ ### packages/adapters/llm-full.js
88
80
  ```
89
- module.exports = { getAdapter, listAdapters, adapt, outputsToAdapters }
90
- function getAdapter(name) → { name: string, format: F
91
- function listAdapters() → string[]
92
- function adapt(context, adapterName, opts = {}) → string
93
- function outputsToAdapters(outputs) → string[]
81
+ module.exports = { name: 'llm-full', format, outputPath, write }
82
+ function outputPath(cwd)
83
+ function format(context, opts)
84
+ function write(context, cwd, opts)
94
85
  ```
95
86
 
96
87
  ### packages/adapters/openai.js
@@ -109,6 +100,23 @@ function _confidenceMeta(opts)
109
100
  function outputPath(cwd) → string
110
101
  ```
111
102
 
103
+ ### packages/adapters/codex.js
104
+ ```
105
+ module.exports = { name, format, outputPath, write }
106
+ function format(context, opts = {}) → string
107
+ function outputPath(cwd) → string
108
+ function write(context, cwd, opts = {})
109
+ ```
110
+
111
+ ### packages/adapters/index.js
112
+ ```
113
+ module.exports = { getAdapter, listAdapters, adapt, outputsToAdapters }
114
+ function getAdapter(name) → { name: string, format: F
115
+ function listAdapters() → string[]
116
+ function adapt(context, adapterName, opts = {}) → string
117
+ function outputsToAdapters(outputs) → string[]
118
+ ```
119
+
112
120
  ### packages/cli/index.js
113
121
  ```
114
122
  module.exports = { CLI_ENTRY, run }
@@ -149,6 +157,26 @@ function adapt(context, adapterName, opts = {}) → string
149
157
 
150
158
  ## src
151
159
 
160
+ ### src/analysis/coverage-score.js
161
+ ```
162
+ module.exports = { coverageScore }
163
+ function coverageScore(cwd, fileEntries, config) → { * score: number, * grad
164
+ function _walk(dir, excludeSet, out)
165
+ ```
166
+
167
+ ### src/eval/analyzer.js
168
+ ```
169
+ module.exports = { analyzeFiles, formatAnalysisTable, formatAnalysisJSON }
170
+ function isDockerfile(name)
171
+ function getExtractorName(filePath)
172
+ function tokenCount(sigs)
173
+ function hasCoverage(filePath, cwd)
174
+ function loadExtractor(name, cwd)
175
+ function analyzeFiles(files, cwd, opts) → object[]
176
+ function formatAnalysisTable(stats, showSlow) → string
177
+ function formatAnalysisJSON(stats) → object
178
+ ```
179
+
152
180
  ### src/extractors/generic.js
153
181
  ```
154
182
  module.exports = { extract }
@@ -171,11 +199,13 @@ function detectVersion(cwd)
171
199
  function format(context, cwd, writtenFiles, sigmapVersion)
172
200
  ```
173
201
 
174
- ### src/analysis/coverage-score.js
202
+ ### src/mcp/server.js
175
203
  ```
176
- module.exports = { coverageScore }
177
- function coverageScore(cwd, fileEntries, config) → { * score: number, * grad
178
- function _walk(dir, excludeSet, out)
204
+ module.exports = { start }
205
+ function respond(id, result)
206
+ function respondError(id, code, message)
207
+ function dispatch(msg, cwd)
208
+ function start(cwd)
179
209
  ```
180
210
 
181
211
  ### src/config/defaults.js
@@ -191,19 +221,6 @@ function loadConfig(cwd) → object
191
221
  function deepClone(obj)
192
222
  ```
193
223
 
194
- ### src/eval/analyzer.js
195
- ```
196
- module.exports = { analyzeFiles, formatAnalysisTable, formatAnalysisJSON }
197
- function isDockerfile(name)
198
- function getExtractorName(filePath)
199
- function tokenCount(sigs)
200
- function hasCoverage(filePath, cwd)
201
- function loadExtractor(name, cwd)
202
- function analyzeFiles(files, cwd, opts) → object[]
203
- function formatAnalysisTable(stats, showSlow) → string
204
- function formatAnalysisJSON(stats) → object
205
- ```
206
-
207
224
  ### src/eval/runner.js
208
225
  ```
209
226
  module.exports = { run, rank, loadTasks, buildSigIndex, formatTable, formatMetrics, tokenize }
@@ -609,15 +626,6 @@ function queryContext(args, cwd)
609
626
  function getImpact(args, cwd)
610
627
  ```
611
628
 
612
- ### src/mcp/server.js
613
- ```
614
- module.exports = { start }
615
- function respond(id, result)
616
- function respondError(id, code, message)
617
- function dispatch(msg, cwd)
618
- function start(cwd)
619
- ```
620
-
621
629
  ### src/mcp/tools.js
622
630
  ```
623
631
  module.exports = { TOOLS }
package/CHANGELOG.md CHANGED
@@ -10,6 +10,62 @@ Format: [Semantic Versioning](https://semver.org/)
10
10
 
11
11
  ---
12
12
 
13
+ ## [4.0.1] — 2026-04-15 — Config auto-detection fix
14
+
15
+ ### Fixed
16
+ - **Bundled `loadConfig` lacked `detectAutoSrcDirs`**: the inline `__factories["./src/config/loader"]` copy inside `gen-context.js` was a stripped-down version that returned raw `DEFAULTS` without filesystem auto-detection. After `--init` wrote a config with 6 hardcoded `srcDirs`, auto-detection was bypassed and custom project directories were missed — causing coverage to drop for any project whose source lives outside those 6 dirs. The bundled loader is now fully in sync with `src/config/loader.js`.
17
+ - **`--init` config hardcoded `srcDirs`**: `gen-context.config.json.example` had `"srcDirs": ["src","app","lib","packages","services","api"]` as a plain value. Any project that ran `--init` would lock into those 6 dirs and lose auto-detection. The example now omits `srcDirs` entirely and uses `_comment` keys to explain that auto-detection runs automatically. Users who need custom dirs can add `srcDirs` manually.
18
+ - **`gen-context.config.json` (SigMap repo)**: restored explicit `"srcDirs": ["src","packages"]` so the repo's own context generation is not affected by auto-detection picking up `docs-vp/`, `scripts/`, `test/`, and `vscode-extension/`.
19
+ - **Example `outputs` updated**: `gen-context.config.json.example` now lists all four standard adapters — `["copilot","codex","claude","gemini"]` — matching the recommended setup.
20
+
21
+ ### Benchmarks (v4.0.1)
22
+ - Token reduction: **97.6% average** across 18 repos ✅
23
+ - Retrieval hit@5: **84.4%** (up from 83.3% in v4.0.0)
24
+
25
+ ---
26
+
27
+ ## [4.0.0] — 2026-04-15 — Intelligence Layer
28
+
29
+ ### Added
30
+ - **Coverage score** (`src/analysis/coverage-score.js`): measures what fraction of source files made it into context after token-budget application.
31
+ - Grade scale: A ≥ 90% · B ≥ 75% · C ≥ 50% · D < 50%
32
+ - Confidence indicator: HIGH / MEDIUM / LOW
33
+ - Per-module breakdown per srcDir via `perModule` Map
34
+ - **Confidence indicators in all output writers**: every generated file now includes a metadata comment:
35
+ ```
36
+ <!-- sigmap: version=4.0.0 confidence=HIGH coverage=94% dropped=9 commit=abc1234 -->
37
+ ```
38
+ Applies to: `copilot`, `claude`, `cursor`, `windsurf`, `openai`, `gemini` adapters.
39
+ - **`--report` module heatmap**: ASCII bar chart per srcDir showing coverage percentage:
40
+ ```
41
+ Module Coverage:
42
+ src ████████████████ 100% (64/64 files)
43
+ packages ██████████████░░ 86% (12/14 files)
44
+ ```
45
+ `--report --json` gains a `coverage` object with `score`, `grade`, `confidence`, `totalFiles`, `includedFiles`, `droppedFiles`, and `perModule`.
46
+ - **`--diff` risk score**: each changed file is now classified LOW / MEDIUM / HIGH based on reverse-dependency BFS, public API exports, route status, and config-file status:
47
+ ```
48
+ [sigmap] Risk: Changed files (3):
49
+ src/auth/service.ts [HIGH] — exports public API, 5 downstream dependents
50
+ src/config/database.ts [MEDIUM] — config file
51
+ src/utils/format.ts [LOW] — no dependents, internal utility
52
+ ```
53
+ - **Coverage in post-run summary**: every normal run now prints a `Coverage` line:
54
+ ```
55
+ Coverage : A (97%) — 76 of 78 source files included
56
+ ```
57
+ - **Coverage in `--health` and `--health --json`**: coverage grade, score, and file counts are included in both text and JSON health output. `--health --json` adds `coverage`, `coverageGrade`, `coverageConfidence`, `coverageTotalFiles`, `coverageIncludedFiles`.
58
+
59
+ ### Changed
60
+ - **Token budget drop order step 5**: now uses `signalQuality = sigs / linesOfCode` (least-informative files dropped first) instead of the previous "fewest sigs" heuristic.
61
+ - **`src/eval/analyzer.js` `analyzeFiles()` output**: each file stat now includes `linesOfCode` and `signalQuality` fields.
62
+
63
+ ### Benchmark (v4.0.0)
64
+ - Token reduction: **97.6% average** across 18 repos (target ≥ 97.0%) ✅
65
+ - Retrieval hit@5: 83.3% (retrieval improvement targeted in v4.5 with adaptive query)
66
+
67
+ ---
68
+
13
69
  ## [3.5.0] — 2026-04-14 — Phase C/D Intelligence Expansion
14
70
 
15
71
  ### Added
package/README.md CHANGED
@@ -19,7 +19,7 @@
19
19
  npx sigmap # 10 seconds. zero config. your AI never reads the wrong file again.
20
20
  ```
21
21
 
22
- > Latest: **v3.5.0** adds Phase C/D intelligence expansion with framework-specialized extractors and cross-module pattern detection.
22
+ > Latest: **v4.0.0** Intelligence Layer. Coverage score, confidence indicators in every output file, `--report` module heatmap, `--diff` risk scoring, and extractor quality-based drop order.
23
23
 
24
24
  <div align="center">
25
25
  <img src="demo.gif" alt="SigMap demo — reducing 80K tokens to 4K in under 10 seconds" width="760" />
@@ -145,7 +145,7 @@ Reproduced with `node scripts/run-benchmark.mjs` on public repos:
145
145
  | fastify | JavaScript | 54.4K | 2.6K | **95.3%** |
146
146
  | fastapi | Python | 178.4K | 5.2K | **97.1%** |
147
147
 
148
- **Average: 97.5% reduction across 18 repos (16 languages).** See [`benchmarks/reports/token-reduction.md`](benchmarks/reports/token-reduction.md) or reproduce with `node scripts/run-benchmark.mjs`.
148
+ **Average: 97.6% reduction across 18 repos (16 languages).** See [`benchmarks/reports/token-reduction.md`](benchmarks/reports/token-reduction.md) or reproduce with `node scripts/run-benchmark.mjs`.
149
149
 
150
150
  ---
151
151
 
@@ -746,17 +746,89 @@ If `output` is omitted, the default `.github/copilot-instructions.md` is used.
746
746
 
747
747
  ## 📊 Observability
748
748
 
749
+ ### Coverage score (v4.0)
750
+
751
+ Every run now prints a coverage line alongside token reduction:
752
+
753
+ ```
754
+ ───────────────────────────────────────────
755
+ SigMap v4.0.0
756
+ Files scanned : 76
757
+ Symbols found : 332
758
+ Token reduction: 94% (65,227 → 4,103)
759
+ Coverage : A (97%) — 76 of 78 source files included
760
+ Output : .github/copilot-instructions.md
761
+ ───────────────────────────────────────────
762
+ ```
763
+
764
+ The **coverage score** answers _how much of your codebase is represented in context_ after the token budget is applied. Grade scale: A ≥ 90% · B ≥ 75% · C ≥ 50% · D < 50%.
765
+
766
+ ### Module heatmap in `--report`
767
+
749
768
  ```bash
750
- # Append run metrics to .context/usage.ndjson
751
- sigmap --track
769
+ sigmap --report
770
+ ```
752
771
 
753
- # Structured JSON report for CI (exits 1 if over budget)
772
+ ```
773
+ [sigmap] report:
774
+ version : 4.0.0
775
+ files processed : 76
776
+ reduction : 93.7%
777
+ coverage : A (97%) — 76 of 78 source files included
778
+ confidence : HIGH
779
+
780
+ Module Coverage:
781
+ src ████████████████ 100% (64/64 files)
782
+ packages ██████████████░░ 86% (12/14 files)
783
+ ```
784
+
785
+ Machine-readable JSON (suitable for CI dashboards):
786
+
787
+ ```bash
754
788
  sigmap --report --json
755
- # { "version": "2.0.0", "finalTokens": 3200, "reductionPct": 92.4, "overBudget": false }
789
+ # { "version": "4.0.0", "finalTokens": 4103, "reductionPct": 93.7,
790
+ # "coverage": { "score": 97, "grade": "A", "confidence": "HIGH", ... } }
791
+ ```
792
+
793
+ ### Composite health score
756
794
 
757
- # Composite health score
795
+ ```bash
758
796
  sigmap --health
759
- # score: 95/100 (grade A) | reduction: 91.2% | 1 day since regen | 47 runs
797
+ ```
798
+
799
+ ```
800
+ [sigmap] health:
801
+ score : 80/100 (grade B)
802
+ coverage : A (97%) — 76 of 78 source files
803
+ strategy : full
804
+ ...
805
+ ```
806
+
807
+ ```bash
808
+ sigmap --health --json
809
+ # { "score": 80, "grade": "B", "coverage": 97, "coverageGrade": "A",
810
+ # "tokens": 4103, "reduction": 93.7, ... }
811
+ ```
812
+
813
+ ### Confidence indicators in generated files
814
+
815
+ Every output file now carries a metadata line so you can inspect freshness at a glance:
816
+
817
+ ```
818
+ <!-- sigmap: version=4.0.0 confidence=HIGH coverage=97% dropped=2 commit=8540612 -->
819
+ ```
820
+
821
+ ### Diff risk score
822
+
823
+ ```bash
824
+ sigmap --diff HEAD~3
825
+ ```
826
+
827
+ ```
828
+ [sigmap] Risk: Changed files (4):
829
+ src/auth/service.ts [HIGH] — exports public API, 5 downstream dependents
830
+ src/config/database.ts [MEDIUM] — config file
831
+ src/utils/format.ts [LOW] — no dependents, internal utility
760
832
  ```
761
833
 
762
834
  ### Self-healing CI
@@ -1,11 +1,14 @@
1
1
  {
2
- "_comment": "SigMap configuration — all keys are optional (defaults shown)",
2
+ "_comment": "SigMap configuration — all keys are optional (defaults shown). Copy to gen-context.config.json.",
3
3
 
4
4
  "output": ".github/copilot-instructions.md",
5
5
 
6
- "outputs": ["copilot", "codex"],
6
+ "outputs": ["copilot", "codex", "claude", "gemini"],
7
7
 
8
- "srcDirs": ["src", "app", "lib", "packages", "services", "api"],
8
+ "_srcDirs_comment": "Source directories to scan. OMIT this key to use auto-detection (recommended).",
9
+ "_srcDirs_comment2": "Auto-detection reads package.json/go.mod/Cargo.toml and scans top-level dirs automatically.",
10
+ "_srcDirs_comment3": "Only set this explicitly if auto-detection misses part of your project layout.",
11
+ "_srcDirs_example": ["src", "app", "lib", "packages", "services", "api"],
9
12
 
10
13
  "exclude": [
11
14
  "node_modules", ".git", "dist", "build", "out",
package/gen-context.js CHANGED
@@ -116,14 +116,98 @@ __factories["./src/config/defaults"] = function(module, exports) {
116
116
 
117
117
  // ── ./src/config/loader ──
118
118
  __factories["./src/config/loader"] = function(module, exports) {
119
-
119
+
120
120
  const fs = require('fs');
121
121
  const path = require('path');
122
122
  const { DEFAULTS } = __require('./src/config/defaults');
123
-
123
+
124
124
  // Keys that are valid in gen-context.config.json
125
125
  const KNOWN_KEYS = new Set(Object.keys(DEFAULTS));
126
-
126
+
127
+ // Common top-level folder names that reliably hold source code
128
+ const COMMON_CODE_DIRS = new Set([
129
+ 'src', 'app', 'lib', 'packages', 'services', 'api', 'core', 'cmd',
130
+ 'internal', 'pkg', 'handlers', 'controllers', 'models', 'views',
131
+ 'components', 'pages', 'routes', 'middleware', 'utils', 'helpers',
132
+ 'modules', 'plugins', 'extensions', 'adapters', 'drivers',
133
+ 'hooks', 'composables', 'stores', 'features', 'domain', 'infra',
134
+ 'infrastructure', 'application', 'data', 'Sources', 'Tests',
135
+ ]);
136
+
137
+ const SUPPORTED_CODE_EXTS = new Set([
138
+ '.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs',
139
+ '.py', '.pyw', '.java', '.kt', '.kts', '.go', '.rs', '.cs',
140
+ '.cpp', '.c', '.h', '.hpp', '.cc', '.rb', '.rake', '.php',
141
+ '.swift', '.dart', '.scala', '.sc', '.vue', '.svelte',
142
+ '.html', '.htm', '.css', '.scss', '.sass', '.less',
143
+ '.yml', '.yaml', '.sh', '.bash', '.zsh', '.fish',
144
+ '.sql', '.graphql', '.gql', '.tf', '.tfvars', '.proto',
145
+ '.toml', '.properties', '.xml', '.md',
146
+ ]);
147
+
148
+ function detectAutoSrcDirs(cwd, excludeList) {
149
+ const excludeSet = new Set(excludeList || []);
150
+ const candidates = new Set(DEFAULTS.srcDirs);
151
+
152
+ // Manifest-based detection
153
+ const pkgPath = path.join(cwd, 'package.json');
154
+ if (fs.existsSync(pkgPath)) {
155
+ try {
156
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
157
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies, ...pkg.peerDependencies };
158
+ if (allDeps.react || allDeps.next)
159
+ for (const d of ['src', 'app', 'pages', 'components', 'hooks', 'lib', 'utils']) candidates.add(d);
160
+ if (allDeps['@angular/core'])
161
+ for (const d of ['src', 'projects', 'apps', 'libs']) candidates.add(d);
162
+ if (allDeps['@nestjs/core'])
163
+ for (const d of ['src', 'libs', 'apps']) candidates.add(d);
164
+ if (allDeps.vue)
165
+ for (const d of ['src', 'components', 'views', 'stores', 'composables', 'plugins']) candidates.add(d);
166
+ if (allDeps.svelte || allDeps['@sveltejs/kit'])
167
+ for (const d of ['src', 'lib', 'routes']) candidates.add(d);
168
+ if (allDeps.nx || allDeps.turbo || allDeps.lerna || pkg.workspaces)
169
+ for (const d of ['packages', 'apps', 'libs', 'services']) candidates.add(d);
170
+ } catch (_) {}
171
+ }
172
+ if (fs.existsSync(path.join(cwd, 'pyproject.toml')) || fs.existsSync(path.join(cwd, 'requirements.txt')) || fs.existsSync(path.join(cwd, 'setup.py')))
173
+ for (const d of ['src', 'app', 'apps', 'tests', 'examples', 'instance', 'blueprints']) candidates.add(d);
174
+ if (fs.existsSync(path.join(cwd, 'Gemfile')))
175
+ for (const d of ['app', 'lib', 'config', 'db', 'spec', 'test']) candidates.add(d);
176
+ if (fs.existsSync(path.join(cwd, 'composer.json')))
177
+ for (const d of ['app', 'resources', 'routes', 'database', 'tests']) candidates.add(d);
178
+ if (fs.existsSync(path.join(cwd, 'go.mod')))
179
+ for (const d of ['cmd', 'internal', 'pkg', 'api', 'handler', 'handlers', 'middleware', 'service']) candidates.add(d);
180
+ if (fs.existsSync(path.join(cwd, 'Cargo.toml')))
181
+ for (const d of ['src', 'crates', 'examples', 'tests', 'benches']) candidates.add(d);
182
+ if (fs.existsSync(path.join(cwd, 'pubspec.yaml')))
183
+ for (const d of ['lib', 'test', 'integration_test', 'example', 'bin']) candidates.add(d);
184
+ if (fs.existsSync(path.join(cwd, 'Package.swift')))
185
+ for (const d of ['Sources', 'Tests']) candidates.add(d);
186
+
187
+ // Top-level directory scan
188
+ try {
189
+ const entries = fs.readdirSync(cwd, { withFileTypes: true });
190
+ for (const entry of entries) {
191
+ if (!entry.isDirectory() || entry.name.startsWith('.') || excludeSet.has(entry.name)) continue;
192
+ const lname = entry.name.toLowerCase();
193
+ if (COMMON_CODE_DIRS.has(entry.name) || COMMON_CODE_DIRS.has(lname)) { candidates.add(entry.name); continue; }
194
+ const dirPath = path.join(cwd, entry.name);
195
+ try {
196
+ const subs = fs.readdirSync(dirPath, { withFileTypes: true });
197
+ if (subs.some(s => s.isFile() && (SUPPORTED_CODE_EXTS.has(path.extname(s.name).toLowerCase()) || s.name === 'Dockerfile'))) {
198
+ candidates.add(entry.name); continue;
199
+ }
200
+ if (subs.some(s => s.isDirectory() && ['src', 'lib', 'main', 'java', 'kotlin', 'scala', 'python'].includes(s.name)))
201
+ candidates.add(entry.name);
202
+ } catch (_) {}
203
+ }
204
+ } catch (_) {}
205
+
206
+ return Array.from(candidates).filter(d => {
207
+ try { return fs.statSync(path.join(cwd, d)).isDirectory(); } catch (_) { return false; }
208
+ });
209
+ }
210
+
127
211
  /**
128
212
  * Load and merge configuration for a given working directory.
129
213
  *
@@ -133,18 +217,24 @@ __factories["./src/config/loader"] = function(module, exports) {
133
217
  function loadConfig(cwd) {
134
218
  const configPath = path.join(cwd, 'gen-context.config.json');
135
219
  if (!fs.existsSync(configPath)) {
136
- return deepClone(DEFAULTS);
220
+ const cfg = deepClone(DEFAULTS);
221
+ const detected = detectAutoSrcDirs(cwd, cfg.exclude);
222
+ if (detected.length > 0) cfg.srcDirs = detected;
223
+ return cfg;
137
224
  }
138
-
225
+
139
226
  let userConfig;
140
227
  try {
141
228
  const raw = fs.readFileSync(configPath, 'utf8');
142
229
  userConfig = JSON.parse(raw);
143
230
  } catch (err) {
144
231
  console.warn(`[sigmap] config parse error in ${configPath}: ${err.message}`);
145
- return deepClone(DEFAULTS);
232
+ const cfg = deepClone(DEFAULTS);
233
+ const detected = detectAutoSrcDirs(cwd, cfg.exclude);
234
+ if (detected.length > 0) cfg.srcDirs = detected;
235
+ return cfg;
146
236
  }
147
-
237
+
148
238
  // Warn on unknown keys (helps catch typos)
149
239
  for (const key of Object.keys(userConfig)) {
150
240
  if (key.startsWith('_')) continue; // allow _comment etc.
@@ -152,7 +242,7 @@ __factories["./src/config/loader"] = function(module, exports) {
152
242
  console.warn(`[sigmap] unknown config key: "${key}" (ignored)`);
153
243
  }
154
244
  }
155
-
245
+
156
246
  // Deep merge: top-level known keys from user override defaults
157
247
  // For object values (e.g. mcp), merge one level deep
158
248
  const merged = deepClone(DEFAULTS);
@@ -167,6 +257,13 @@ __factories["./src/config/loader"] = function(module, exports) {
167
257
  merged[key] = val;
168
258
  }
169
259
  }
260
+
261
+ // If user didn't specify srcDirs, auto-detect; fall back to DEFAULTS if nothing found
262
+ if (!Array.isArray(userConfig.srcDirs)) {
263
+ const detected = detectAutoSrcDirs(cwd, merged.exclude);
264
+ merged.srcDirs = detected.length > 0 ? detected : deepClone(DEFAULTS.srcDirs);
265
+ }
266
+
170
267
  // Backward compat (v3.0+): if user specified 'adapters', use it as 'outputs' too.
171
268
  // If user specified only 'outputs' (old configs), mirror to 'adapters'.
172
269
  if (merged.adapters && !Array.isArray(merged.adapters)) merged.adapters = null;
@@ -177,13 +274,13 @@ __factories["./src/config/loader"] = function(module, exports) {
177
274
  }
178
275
  return merged;
179
276
  }
180
-
277
+
181
278
  function deepClone(obj) {
182
279
  return JSON.parse(JSON.stringify(obj));
183
280
  }
184
-
185
- module.exports = { loadConfig };
186
-
281
+
282
+ module.exports = { loadConfig, detectAutoSrcDirs };
283
+
187
284
  };
188
285
 
189
286
  // ── ./src/extractors/cpp ──
@@ -4478,7 +4575,7 @@ __factories["./src/mcp/server"] = function(module, exports) {
4478
4575
 
4479
4576
  const SERVER_INFO = {
4480
4577
  name: 'sigmap',
4481
- version: '4.0.0',
4578
+ version: '4.0.1',
4482
4579
  description: 'SigMap MCP server — code signatures on demand',
4483
4580
  };
4484
4581
 
@@ -6040,7 +6137,7 @@ const path = require('path');
6040
6137
  const os = require('os');
6041
6138
  const { execSync } = require('child_process');
6042
6139
 
6043
- const VERSION = '4.0.0';
6140
+ const VERSION = '4.0.1';
6044
6141
  const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
6045
6142
 
6046
6143
  function requireSourceOrBundled(key) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigmap",
3
- "version": "4.0.0",
3
+ "version": "4.0.1",
4
4
  "description": "Zero-dependency AI context engine — 97% token reduction. No npm install. Runs on Node 18+.",
5
5
  "main": "gen-context.js",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigmap-cli",
3
- "version": "4.0.0",
3
+ "version": "4.0.1",
4
4
  "description": "SigMap CLI wrapper — thin adapter for programmatic CLI invocation",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigmap-core",
3
- "version": "4.0.0",
3
+ "version": "4.0.1",
4
4
  "description": "SigMap core library — zero-dependency code signature extraction, retrieval, and security scanning",
5
5
  "main": "index.js",
6
6
  "keywords": [
package/src/mcp/server.js CHANGED
@@ -18,7 +18,7 @@ const { readContext, searchSignatures, getMap, createCheckpoint, getRouting, exp
18
18
 
19
19
  const SERVER_INFO = {
20
20
  name: 'sigmap',
21
- version: '4.0.0',
21
+ version: '4.0.1',
22
22
  description: 'SigMap MCP server — code signatures on demand',
23
23
  };
24
24