sigmap 2.10.0 → 3.0.1-alpha.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/CHANGELOG.md CHANGED
@@ -6,7 +6,40 @@ Format: [Semantic Versioning](https://semver.org/)
6
6
 
7
7
  ---
8
8
 
9
- ## [2.10.0] — upcoming · [#25](https://github.com/manojmallick/sigmap/issues/25) · branch: `feat/v2.10-reporting-charts-advanced-metrics`
9
+ ## [Unreleased]
10
+
11
+ ### Fixed
12
+ - **`output` config key not honored for copilot adapter** · [#30](https://github.com/manojmallick/sigmap/issues/30)
13
+ - Custom `output` path in config now correctly used for copilot adapter instead of hard-wired `.github/copilot-instructions.md`
14
+ - Added `resolveAdapterPath()` helper to centralize adapter path resolution
15
+ - Other adapters (claude, cursor, windsurf) continue to use fixed paths as designed
16
+ - 5 new integration tests ensure custom paths work correctly across all config combinations
17
+
18
+ ---
19
+
20
+ ## [3.0.0] — 2026-04-06 — Platform: Multi-Adapter Architecture
21
+
22
+ ### Added
23
+ - **Multi-adapter platform** — `packages/adapters/` with 6 output adapters: `copilot`, `claude`, `cursor`, `windsurf`, `openai`, `gemini`
24
+ - **`--adapter <name>` CLI flag** — generate output for a specific adapter only (e.g. `node gen-context.js --adapter openai`)
25
+ - **`adapt()` in packages/core** — programmatic API: `const { adapt } = require('sigmap'); adapt(context, 'openai')`
26
+ - **New config key `adapters`** — replaces `outputs`; old `outputs` key is silently mapped for full backward compatibility
27
+ - **OpenAI adapter** — formats context as an OpenAI system prompt, writes `.github/openai-context.md`
28
+ - **Gemini adapter** — formats context as a Gemini system instruction, writes `.github/gemini-context.md`
29
+ - **API stability guarantee** — `packages/core` API is now semver-stable; breaking changes require v4.0
30
+ - **20 new integration tests** in `test/integration/adapters.test.js`
31
+
32
+ ### Changed
33
+ - `packages/core/index.js` — adds `adapt()` export alongside existing `extract`, `rank`, `scan`, `score`, `buildSigIndex`
34
+ - `writeOutputs()` in `gen-context.js` — now routes `openai`, `gemini` through adapter pipeline
35
+
36
+ ### Backward compat
37
+ - `outputs: ["copilot","claude"]` config still works — automatically mirrored to `adapters`
38
+ - All existing CLI flags unchanged
39
+
40
+ ---
41
+
42
+ ## [2.10.0] — 2026-04-06 · [#25](https://github.com/manojmallick/sigmap/issues/25)
10
43
 
11
44
  ### Planned additions
12
45
  - **Report charts** — add chart-ready output for token reduction, signatures per file, and budget utilization trends.
package/README.md CHANGED
@@ -26,7 +26,7 @@
26
26
  [![Changelog](https://img.shields.io/badge/changelog-CHANGELOG.md-blue)](CHANGELOG.md)
27
27
  [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md)
28
28
  [![VS Code](https://img.shields.io/badge/VS%20Code-extension-0078d4?logo=visual-studio-code)](https://marketplace.visualstudio.com/items?itemName=manojmallick.sigmap)
29
- [![JetBrains](https://img.shields.io/badge/JetBrains-plugin-000000?logo=jetbrains)](https://plugins.jetbrains.com/plugin/sigmap)
29
+ [![JetBrains](https://img.shields.io/badge/JetBrains-plugin-000000?logo=jetbrains)](https://plugins.jetbrains.com/plugin/31109-sigmap--ai-context-engine/)
30
30
  [![Open VSX](https://img.shields.io/open-vsx/v/manojmallick/sigmap?color=a251e3&label=Open%20VSX&logo=vscodium)](https://open-vsx.org/extension/manojmallick/sigmap)
31
31
 
32
32
  </div>
@@ -124,19 +124,45 @@ AI agent session starts with full context
124
124
  | **CI-friendly metrics export** | Persist machine-readable metrics for release gates and regression tracking |
125
125
  | **Release quality gates** | Add pass/fail thresholds for hit@5 and precision before publish |
126
126
 
127
- ---
128
- | **`get_impact` MCP tool** | 9th MCP tool — `{ file, depth? }` → impacted files + signatures |
129
- | **`src/map/dep-graph.js`** | Reverse-dependency graph built from the import analysis; circular deps handled safely |
130
- | **15 new tests** | `impact.test.js` — direct deps, transitive deps, depth limit, JSON output |
127
+ ## 🔌 v3.0 — Platform: Multi-Adapter Architecture
128
+
129
+ SigMap is now an **adapter platform**. Any AI assistant Copilot, Claude, Cursor, Windsurf, OpenAI, or Gemini — plugs in through a standard interface.
130
+
131
+ ```bash
132
+ # Generate for a specific AI assistant
133
+ node gen-context.js --adapter copilot # → .github/copilot-instructions.md
134
+ node gen-context.js --adapter openai # → .github/openai-context.md
135
+ node gen-context.js --adapter gemini # → .github/gemini-context.md
136
+ node gen-context.js --adapter claude # → CLAUDE.md (append)
137
+ ```
138
+
139
+ ```js
140
+ // Programmatic API — fully semver-stable from v3.0
141
+ const { adapt } = require('sigmap');
142
+ const systemPrompt = adapt(context, 'openai', { version: '3.0.0' });
143
+ ```
144
+
145
+ | Adapter | Output file | AI assistant |
146
+ |---|---|---|
147
+ | `copilot` | `.github/copilot-instructions.md` | GitHub Copilot |
148
+ | `claude` | `CLAUDE.md` (append) | Claude / Claude Code |
149
+ | `cursor` | `.cursorrules` | Cursor |
150
+ | `windsurf` | `.windsurfrules` | Windsurf |
151
+ | `openai` | `.github/openai-context.md` | Any OpenAI model |
152
+ | `gemini` | `.github/gemini-context.md` | Google Gemini |
153
+
154
+ **Backward compat:** existing `outputs` config key silently maps to `adapters` — no migration needed.
155
+
156
+ See full roadmap: [manojmallick.github.io/sigmap/roadmap.html](https://manojmallick.github.io/sigmap/roadmap.html)
131
157
 
132
158
  ---
133
159
 
134
- ## 🚀 Quick start
160
+ ## Quick start
135
161
 
136
- **No install required just Node.js 18+.**
162
+ Download the single-file CLI and generate context immediately:
137
163
 
138
164
  ```bash
139
- # 1. Copy gen-context.js into your project root
165
+ # 1. Download
140
166
  curl -O https://raw.githubusercontent.com/manojmallick/sigmap/main/gen-context.js
141
167
 
142
168
  # 2. Generate your context file
@@ -220,7 +246,7 @@ The `jetbrains-plugin/` directory contains a Kotlin-based plugin for JetBrains I
220
246
 
221
247
  Compatible with **IntelliJ IDEA 2024.1+** (Community & Ultimate), **WebStorm**, **PyCharm**, **GoLand**, **RubyMine**, **PhpStorm**, and all other IntelliJ-based IDEs.
222
248
 
223
- **Install:** [JetBrains Marketplace](https://plugins.jetbrains.com/plugin/sigmap) | [Manual setup guide](docs/JETBRAINS_SETUP.md)
249
+ **Install:** [JetBrains Marketplace](https://plugins.jetbrains.com/plugin/31109-sigmap--ai-context-engine/) | [Manual setup guide](docs/JETBRAINS_SETUP.md)
224
250
 
225
251
  ---
226
252
 
@@ -423,6 +449,7 @@ Copy `gen-context.config.json.example` to `gen-context.config.json`:
423
449
 
424
450
  ```json
425
451
  {
452
+ "output": ".github/copilot-instructions.md",
426
453
  "srcDirs": ["src", "app", "lib"],
427
454
  "maxTokens": 6000,
428
455
  "outputs": ["copilot"],
@@ -433,6 +460,15 @@ Copy `gen-context.config.json.example` to `gen-context.config.json`:
433
460
  }
434
461
  ```
435
462
 
463
+ **Key fields:**
464
+
465
+ - **`output`** — custom path for the primary markdown output file (used by `copilot` adapter). Default: `.github/copilot-instructions.md`
466
+ - **`outputs`** — which adapters to write to: `copilot` | `claude` | `cursor` | `windsurf`
467
+ - **`srcDirs`** — directories to scan (relative to project root)
468
+ - **`maxTokens`** — max tokens in final output before budget enforcement
469
+ - **`secretScan`** — redact secrets (AWS keys, tokens, etc.) from output
470
+ - **`strategy`** — output mode: `full` (default) | `per-module` | `hot-cold`
471
+
436
472
  Exclusions go in `.contextignore` (gitignore syntax). Also reads `.repomixignore` if present.
437
473
 
438
474
  ```
@@ -450,12 +486,25 @@ Run `node gen-context.js --init` to scaffold both files in one step.
450
486
 
451
487
  | Key | Output file | Read by |
452
488
  |---|---|---|
453
- | `"copilot"` | `.github/copilot-instructions.md` | GitHub Copilot |
489
+ | `"copilot"` | `.github/copilot-instructions.md` *(or custom path via `output`)* | GitHub Copilot |
454
490
  | `"claude"` | `CLAUDE.md` (appends below marker) | Claude Code |
455
491
  | `"cursor"` | `.cursorrules` | Cursor |
456
492
  | `"windsurf"` | `.windsurfrules` | Windsurf |
457
493
 
458
- ---
494
+ The **`output`** config key sets the primary output file path. It is used by the `copilot` adapter when enabled. Other adapters always write to their fixed paths.
495
+
496
+ **Example:**
497
+
498
+ ```json
499
+ {
500
+ "output": ".context/ai-context.md",
501
+ "outputs": ["copilot"]
502
+ }
503
+ ```
504
+
505
+ This writes to `.context/ai-context.md` instead of `.github/copilot-instructions.md`.
506
+
507
+ If `output` is omitted, the default `.github/copilot-instructions.md` is used.
459
508
 
460
509
  ## 📊 Observability
461
510
 
package/gen-context.js CHANGED
@@ -33,6 +33,10 @@ __factories["./src/config/defaults"] = function(module, exports) {
33
33
 
34
34
  // Output targets: 'copilot' | 'claude' | 'cursor' | 'windsurf'
35
35
  outputs: ['copilot'],
36
+
37
+ // Adapter targets (v3.0+): replaces 'outputs'. Same names, adds 'openai' | 'gemini'.
38
+ // Old 'outputs' config key is still accepted and silently maps to 'adapters'.
39
+ adapters: null, // null means: use 'outputs' for backward compat
36
40
 
37
41
  // Directories to scan (relative to project root)
38
42
  srcDirs: [
@@ -163,6 +167,14 @@ __factories["./src/config/loader"] = function(module, exports) {
163
167
  merged[key] = val;
164
168
  }
165
169
  }
170
+ // Backward compat (v3.0+): if user specified 'adapters', use it as 'outputs' too.
171
+ // If user specified only 'outputs' (old configs), mirror to 'adapters'.
172
+ if (merged.adapters && !Array.isArray(merged.adapters)) merged.adapters = null;
173
+ if (!merged.adapters && Array.isArray(merged.outputs)) {
174
+ merged.adapters = merged.outputs.slice();
175
+ } else if (Array.isArray(merged.adapters) && !userConfig.outputs) {
176
+ merged.outputs = merged.adapters.filter((a) => ['copilot','claude','cursor','windsurf'].includes(a));
177
+ }
166
178
  return merged;
167
179
  }
168
180
 
@@ -4727,6 +4739,158 @@ __factories["./src/eval/runner"] = function(module, exports) {
4727
4739
  };
4728
4740
 
4729
4741
 
4742
+ // ── ./packages/adapters/copilot (bundled) ──
4743
+ __factories["./packages/adapters/copilot"] = function(module, exports) {
4744
+ const path = require('path');
4745
+ const name = 'copilot';
4746
+ function format(context, opts = {}) {
4747
+ if (!context || typeof context !== 'string') return '';
4748
+ const version = (opts && opts.version) || 'unknown';
4749
+ const timestamp = new Date().toISOString();
4750
+ return [
4751
+ `<!-- Generated by SigMap gen-context.js v${version} -->`,
4752
+ `<!-- Updated: ${timestamp} -->`,
4753
+ `<!-- Do not edit below — regenerate with: node gen-context.js -->`,
4754
+ '',
4755
+ '# Code signatures',
4756
+ '',
4757
+ context,
4758
+ ].join('\n');
4759
+ }
4760
+ function outputPath(cwd) { return path.join(cwd, '.github', 'copilot-instructions.md'); }
4761
+ module.exports = { name, format, outputPath };
4762
+ };
4763
+
4764
+ // ── ./packages/adapters/claude (bundled) ──
4765
+ __factories["./packages/adapters/claude"] = function(module, exports) {
4766
+ const path = require('path');
4767
+ const fs = require('fs');
4768
+ const name = 'claude';
4769
+ const CLAUDE_MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
4770
+ function format(context, opts = {}) {
4771
+ if (!context || typeof context !== 'string') return '';
4772
+ const version = (opts && opts.version) || 'unknown';
4773
+ const timestamp = new Date().toISOString();
4774
+ return [`<!-- Generated by SigMap v${version} — ${timestamp} -->`, '', context].join('\n');
4775
+ }
4776
+ function outputPath(cwd) { return path.join(cwd, 'CLAUDE.md'); }
4777
+ function write(context, cwd, opts = {}) {
4778
+ const filePath = outputPath(cwd);
4779
+ let existing = '';
4780
+ if (fs.existsSync(filePath)) existing = fs.readFileSync(filePath, 'utf8');
4781
+ const formatted = format(context, opts);
4782
+ const markerIdx = existing.indexOf('## Auto-generated signatures');
4783
+ const newContent = markerIdx !== -1
4784
+ ? existing.slice(0, markerIdx) + CLAUDE_MARKER.trimStart() + formatted
4785
+ : existing + CLAUDE_MARKER + formatted;
4786
+ fs.writeFileSync(filePath, newContent, 'utf8');
4787
+ }
4788
+ module.exports = { name, format, outputPath, write };
4789
+ };
4790
+
4791
+ // ── ./packages/adapters/cursor (bundled) ──
4792
+ __factories["./packages/adapters/cursor"] = function(module, exports) {
4793
+ const path = require('path');
4794
+ const name = 'cursor';
4795
+ function format(context, opts = {}) {
4796
+ if (!context || typeof context !== 'string') return '';
4797
+ const version = (opts && opts.version) || 'unknown';
4798
+ const timestamp = new Date().toISOString();
4799
+ return [`# Code signatures — generated by SigMap v${version}`, `# Updated: ${timestamp}`, `# Regenerate: node gen-context.js`, '', context].join('\n');
4800
+ }
4801
+ function outputPath(cwd) { return path.join(cwd, '.cursorrules'); }
4802
+ module.exports = { name, format, outputPath };
4803
+ };
4804
+
4805
+ // ── ./packages/adapters/windsurf (bundled) ──
4806
+ __factories["./packages/adapters/windsurf"] = function(module, exports) {
4807
+ const path = require('path');
4808
+ const name = 'windsurf';
4809
+ function format(context, opts = {}) {
4810
+ if (!context || typeof context !== 'string') return '';
4811
+ const version = (opts && opts.version) || 'unknown';
4812
+ const timestamp = new Date().toISOString();
4813
+ return [`# Code signatures — generated by SigMap v${version}`, `# Updated: ${timestamp}`, `# Regenerate: node gen-context.js`, '', context].join('\n');
4814
+ }
4815
+ function outputPath(cwd) { return path.join(cwd, '.windsurfrules'); }
4816
+ module.exports = { name, format, outputPath };
4817
+ };
4818
+
4819
+ // ── ./packages/adapters/openai (bundled) ──
4820
+ __factories["./packages/adapters/openai"] = function(module, exports) {
4821
+ const path = require('path');
4822
+ const name = 'openai';
4823
+ function format(context, opts = {}) {
4824
+ if (!context || typeof context !== 'string') return '';
4825
+ const version = (opts && opts.version) || 'unknown';
4826
+ const timestamp = new Date().toISOString();
4827
+ const projectLine = (opts && opts.projectName) ? `Project: ${opts.projectName}\n` : '';
4828
+ return [
4829
+ `You are a coding assistant with full knowledge of this codebase.`,
4830
+ `Below are the code signatures extracted by SigMap v${version} on ${timestamp}.`,
4831
+ projectLine,
4832
+ `Use these signatures to answer questions about the code accurately.`,
4833
+ ``,
4834
+ `## Code Signatures`,
4835
+ ``,
4836
+ context,
4837
+ ].join('\n');
4838
+ }
4839
+ function outputPath(cwd) { return path.join(cwd, '.github', 'openai-context.md'); }
4840
+ module.exports = { name, format, outputPath };
4841
+ };
4842
+
4843
+ // ── ./packages/adapters/gemini (bundled) ──
4844
+ __factories["./packages/adapters/gemini"] = function(module, exports) {
4845
+ const path = require('path');
4846
+ const name = 'gemini';
4847
+ function format(context, opts = {}) {
4848
+ if (!context || typeof context !== 'string') return '';
4849
+ const version = (opts && opts.version) || 'unknown';
4850
+ const timestamp = new Date().toISOString();
4851
+ const projectLine = (opts && opts.projectName) ? `Project: ${opts.projectName}\n` : '';
4852
+ return [
4853
+ `You are a coding assistant with complete knowledge of this codebase.`,
4854
+ `The following code signatures were extracted by SigMap v${version} on ${timestamp}.`,
4855
+ projectLine,
4856
+ `These signatures represent every public function, class, and type in the project.`,
4857
+ ``,
4858
+ `## Code Signatures`,
4859
+ ``,
4860
+ context,
4861
+ ].join('\n');
4862
+ }
4863
+ function outputPath(cwd) { return path.join(cwd, '.github', 'gemini-context.md'); }
4864
+ module.exports = { name, format, outputPath };
4865
+ };
4866
+
4867
+ // ── ./packages/adapters/index (bundled) ──
4868
+ __factories["./packages/adapters/index"] = function(module, exports) {
4869
+ const ADAPTER_NAMES = ['copilot', 'claude', 'cursor', 'windsurf', 'openai', 'gemini'];
4870
+ const _adapterCache = {};
4871
+ function getAdapter(name) {
4872
+ if (!name || typeof name !== 'string') return null;
4873
+ const key = name.toLowerCase();
4874
+ if (!ADAPTER_NAMES.includes(key)) return null;
4875
+ if (!_adapterCache[key]) {
4876
+ try { _adapterCache[key] = __require('./packages/adapters/' + key); } catch (_) { return null; }
4877
+ }
4878
+ return _adapterCache[key];
4879
+ }
4880
+ function listAdapters() { return ADAPTER_NAMES.slice(); }
4881
+ function adapt(context, adapterName, opts = {}) {
4882
+ if (!context || typeof context !== 'string') return '';
4883
+ const adapter = getAdapter(adapterName);
4884
+ if (!adapter || typeof adapter.format !== 'function') return '';
4885
+ try { return adapter.format(context, opts); } catch (_) { return ''; }
4886
+ }
4887
+ function outputsToAdapters(outputs) {
4888
+ if (!Array.isArray(outputs)) return ['copilot'];
4889
+ return outputs.map((o) => ADAPTER_NAMES.includes(o) ? o : o);
4890
+ }
4891
+ module.exports = { getAdapter, listAdapters, adapt, outputsToAdapters };
4892
+ };
4893
+
4730
4894
  /**
4731
4895
  * SigMap — gen-context.js v1.2.0
4732
4896
  * Zero-dependency AI context engine.
@@ -4739,7 +4903,7 @@ const path = require('path');
4739
4903
  const os = require('os');
4740
4904
  const { execSync } = require('child_process');
4741
4905
 
4742
- const VERSION = '2.10.0';
4906
+ const VERSION = '3.0.1';
4743
4907
  const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
4744
4908
 
4745
4909
  function requireSourceOrBundled(key) {
@@ -5281,6 +5445,29 @@ function ensureDir(filePath) {
5281
5445
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
5282
5446
  }
5283
5447
 
5448
+ // ---------------------------------------------------------------------------
5449
+ // Resolve output path for a given adapter
5450
+ // ---------------------------------------------------------------------------
5451
+ // Takes config.output into account for the copilot adapter.
5452
+ // Returns the full path where output should be written.
5453
+ function resolveAdapterPath(adapter, cwd, config) {
5454
+ config = config || {};
5455
+
5456
+ // Copilot uses config.output if available, otherwise default
5457
+ if (adapter === 'copilot') {
5458
+ return config.output
5459
+ ? path.join(cwd, config.output)
5460
+ : path.join(cwd, '.github', 'copilot-instructions.md');
5461
+ }
5462
+
5463
+ // Fixed paths for other adapters
5464
+ if (adapter === 'claude') return path.join(cwd, 'CLAUDE.md');
5465
+ if (adapter === 'cursor') return path.join(cwd, '.cursorrules');
5466
+ if (adapter === 'windsurf') return path.join(cwd, '.windsurfrules');
5467
+
5468
+ return null;
5469
+ }
5470
+
5284
5471
  // ---------------------------------------------------------------------------
5285
5472
  // Cache output writer (v0.8)
5286
5473
  // ---------------------------------------------------------------------------
@@ -5296,11 +5483,15 @@ function writeCacheOutput(content, cwd) {
5296
5483
  }
5297
5484
  }
5298
5485
 
5299
- function writeOutputs(content, targets, cwd) {
5486
+ function writeOutputs(content, targets, cwd, config) {
5487
+ config = config || {};
5488
+ // v3.0+: adapter-aware output targets
5489
+ const ADAPTER_TARGETS = new Set(['openai', 'gemini']);
5490
+
5300
5491
  const targetMap = {
5301
- copilot: path.join(cwd, '.github', 'copilot-instructions.md'),
5302
- cursor: path.join(cwd, '.cursorrules'),
5303
- windsurf: path.join(cwd, '.windsurfrules'),
5492
+ copilot: resolveAdapterPath('copilot', cwd, config),
5493
+ cursor: resolveAdapterPath('cursor', cwd, config),
5494
+ windsurf: resolveAdapterPath('windsurf', cwd, config),
5304
5495
  };
5305
5496
 
5306
5497
  for (const target of targets) {
@@ -5308,6 +5499,20 @@ function writeOutputs(content, targets, cwd) {
5308
5499
  writeClaude(content, cwd);
5309
5500
  continue;
5310
5501
  }
5502
+ // v3.0+ adapter targets (openai, gemini) — use bundled adapter to format + write
5503
+ if (ADAPTER_TARGETS.has(target)) {
5504
+ try {
5505
+ const adapterMod = __require('./packages/adapters/' + target);
5506
+ const formatted = adapterMod.format(content, { version: VERSION });
5507
+ const outPath = adapterMod.outputPath(cwd);
5508
+ ensureDir(outPath);
5509
+ fs.writeFileSync(outPath, formatted, 'utf8');
5510
+ console.warn(`[sigmap] wrote ${path.relative(cwd, outPath)}`);
5511
+ } catch (err) {
5512
+ console.warn(`[sigmap] adapter "${target}" failed: ${err.message}`);
5513
+ }
5514
+ continue;
5515
+ }
5311
5516
  const outPath = targetMap[target];
5312
5517
  if (!outPath) {
5313
5518
  console.warn(`[sigmap] unknown output target: ${target}`);
@@ -5528,7 +5733,7 @@ function runPerModuleStrategy(cwd, config, fileEntries, inputTokenTotal) {
5528
5733
  overviewLines.push('> For cross-module questions load both files.');
5529
5734
  const overviewContent = overviewLines.join('\n') + '\n';
5530
5735
  const primaryTargets = (config.outputs || ['copilot']).filter((t) => t !== 'claude');
5531
- writeOutputs(overviewContent, primaryTargets, cwd);
5736
+ writeOutputs(overviewContent, primaryTargets, cwd, config);
5532
5737
 
5533
5738
  const overviewTokens = estimateTokens(overviewContent);
5534
5739
  console.warn(`[sigmap] per-module: overview ~${overviewTokens} tokens (always-on), modules total ~${totalOut} tokens (on-demand)`);
@@ -5547,7 +5752,7 @@ function runHotColdStrategy(cwd, config, fileEntries, recentFiles, inputTokenTot
5547
5752
  ? formatOutput(hotEntries, cwd, false, config, null)
5548
5753
  : '<!-- Generated by SigMap — no recently changed files -->\n';
5549
5754
  const primaryTargets = (config.outputs || ['copilot']).filter((t) => t !== 'claude');
5550
- writeOutputs(hotContent, primaryTargets, cwd);
5755
+ writeOutputs(hotContent, primaryTargets, cwd, config);
5551
5756
  const hotTokens = estimateTokens(hotContent);
5552
5757
 
5553
5758
  // Cold → .github/context-cold.md (MCP reads this on demand)
@@ -5650,7 +5855,7 @@ function runDiff(cwd, config, stagedOnly, baseRef) {
5650
5855
  const diffSection = baseRef ? buildDiffSectionFromBase(cwd, baseRef, fileEntries, config) : [];
5651
5856
  const content = formatOutput(fileEntries, cwd, routingEnabled, config, { diffSection });
5652
5857
  const finalTokens = estimateTokens(content);
5653
- writeOutputs(content, config.outputs, cwd);
5858
+ writeOutputs(content, config.outputs, cwd, config);
5654
5859
 
5655
5860
  const scope = baseRef ? `diff-vs-${baseRef}` : (stagedOnly ? 'staged' : 'diff');
5656
5861
  console.warn(`[sigmap] ${scope} files: ${fileEntries.length}, diff tokens: ~${finalTokens}`);
@@ -5759,7 +5964,7 @@ function runGenerate(cwd, config, reportMode, reportJson = false) {
5759
5964
  const finalTokens = estimateTokens(content);
5760
5965
  const formatIdx = process.argv.indexOf('--format');
5761
5966
  const formatValue = formatIdx >= 0 ? process.argv[formatIdx + 1] : (config.format || 'default');
5762
- writeOutputs(content, config.outputs, cwd);
5967
+ writeOutputs(content, config.outputs, cwd, config);
5763
5968
  if (formatValue === 'cache') writeCacheOutput(content, cwd);
5764
5969
  result = { inputTokenTotal, finalTokens, fileCount: beforeCount, droppedCount };
5765
5970
  }
@@ -5948,6 +6153,8 @@ Usage:
5948
6153
  node gen-context.js --diff <base-ref> Generate context + structural diff vs base ref (e.g. main)
5949
6154
  node gen-context.js --diff --staged Generate context for staged files only
5950
6155
  node gen-context.js --benchmark Run retrieval benchmark (benchmarks/tasks/retrieval.jsonl)
6156
+ node gen-context.js --adapter <name> Generate for a specific adapter only (v3.0+)
6157
+ node gen-context.js --adapter <name> --json Show adapter output path as JSON
5951
6158
  node gen-context.js --benchmark --json Benchmark results as JSON
5952
6159
  node gen-context.js --eval Alias for --benchmark
5953
6160
  node gen-context.js --analyze Per-file breakdown: sigs, tokens, extractor, coverage
@@ -5972,6 +6179,10 @@ Strategies (set via config "strategy" key):
5972
6179
  ~90% fewer tokens. Best with MCP (Claude Code, Cursor).
5973
6180
  Set "hotCommits": N to control how many commits count as hot (default 10).
5974
6181
 
6182
+ Adapters (v3.0+): copilot | claude | cursor | windsurf | openai | gemini
6183
+ Set "adapters": ["copilot","openai"] in config to write multiple adapter outputs.
6184
+ Old "outputs" config key is still accepted (maps to adapters automatically).
6185
+
5975
6186
  Config: gen-context.config.json
5976
6187
  Ignore: .contextignore, .repomixignore
5977
6188
  Output: .github/copilot-instructions.md (default)
@@ -6303,6 +6514,44 @@ function main() {
6303
6514
  process.exit(0);
6304
6515
  }
6305
6516
 
6517
+ // ── --adapter <name> ───────────────────────────────────────────────────────
6518
+ if (args.includes('--adapter')) {
6519
+ try {
6520
+ const adpIdx = args.indexOf('--adapter');
6521
+ const adapterName = (args[adpIdx + 1] || '').trim().toLowerCase();
6522
+ const VALID_ADAPTERS = ['copilot', 'claude', 'cursor', 'windsurf', 'openai', 'gemini'];
6523
+ if (!adapterName || adapterName.startsWith('--')) {
6524
+ console.error('[sigmap] --adapter requires an adapter name');
6525
+ console.error(` Valid adapters: ${VALID_ADAPTERS.join(', ')}`);
6526
+ console.error(' Example: node gen-context.js --adapter copilot');
6527
+ process.exit(1);
6528
+ }
6529
+ if (!VALID_ADAPTERS.includes(adapterName)) {
6530
+ console.error(`[sigmap] unknown adapter: "${adapterName}"`);
6531
+ console.error(` Valid adapters: ${VALID_ADAPTERS.join(', ')}`);
6532
+ process.exit(1);
6533
+ }
6534
+ if (args.includes('--json')) {
6535
+ const adapterMod = __require('./packages/adapters/' + adapterName);
6536
+ const outPath = adapterMod.outputPath(cwd);
6537
+ process.stdout.write(JSON.stringify({ adapter: adapterName, outputPath: outPath, version: VERSION }) + '\n');
6538
+ process.exit(0);
6539
+ }
6540
+ // Run generate with this adapter's output targets
6541
+ const singleAdapterConfig = Object.assign({}, config, {
6542
+ outputs: adapterName === 'claude' ? ['claude'] : [adapterName],
6543
+ adapters: [adapterName],
6544
+ });
6545
+ runGenerate(cwd, singleAdapterConfig, false);
6546
+ const adapterMod = __require('./packages/adapters/' + adapterName);
6547
+ console.warn(`[sigmap] adapter "${adapterName}" → ${path.relative(cwd, adapterMod.outputPath(cwd))}`);
6548
+ } catch (err) {
6549
+ console.error(`[sigmap] adapter error: ${err.message}`);
6550
+ process.exit(1);
6551
+ }
6552
+ process.exit(0);
6553
+ }
6554
+
6306
6555
  // ── --impact <file> ────────────────────────────────────────────────────────
6307
6556
  if (args.includes('--impact')) {
6308
6557
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigmap",
3
- "version": "2.10.0",
3
+ "version": "3.0.1-alpha.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": {
@@ -0,0 +1,71 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Claude adapter — appends to CLAUDE.md under a marker line.
5
+ * Never overwrites human-written content above the marker.
6
+ *
7
+ * Contract:
8
+ * format(context, opts?) → string
9
+ * outputPath(cwd) → string
10
+ * write(context, cwd, opts?) → void (handles append logic)
11
+ */
12
+
13
+ const path = require('path');
14
+ const fs = require('fs');
15
+
16
+ const name = 'claude';
17
+
18
+ const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
19
+
20
+ /**
21
+ * Format context suited for CLAUDE.md.
22
+ * @param {string} context - Raw signature context string
23
+ * @param {object} [opts]
24
+ * @param {string} [opts.version] - SigMap version string
25
+ * @returns {string}
26
+ */
27
+ function format(context, opts = {}) {
28
+ if (!context || typeof context !== 'string') return '';
29
+ const version = opts.version || 'unknown';
30
+ const timestamp = new Date().toISOString();
31
+ return [
32
+ `<!-- Generated by SigMap v${version} — ${timestamp} -->`,
33
+ '',
34
+ context,
35
+ ].join('\n');
36
+ }
37
+
38
+ /**
39
+ * Return the output file path for this adapter.
40
+ * @param {string} cwd - Project root
41
+ * @returns {string}
42
+ */
43
+ function outputPath(cwd) {
44
+ return path.join(cwd, 'CLAUDE.md');
45
+ }
46
+
47
+ /**
48
+ * Write signatures into CLAUDE.md using the append-under-marker strategy.
49
+ * Human content above the marker is never touched.
50
+ * @param {string} context - Raw signature context string
51
+ * @param {string} cwd - Project root
52
+ * @param {object} [opts]
53
+ */
54
+ function write(context, cwd, opts = {}) {
55
+ const filePath = outputPath(cwd);
56
+ let existing = '';
57
+ if (fs.existsSync(filePath)) {
58
+ existing = fs.readFileSync(filePath, 'utf8');
59
+ }
60
+ const formatted = format(context, opts);
61
+ const markerIdx = existing.indexOf('## Auto-generated signatures');
62
+ let newContent;
63
+ if (markerIdx !== -1) {
64
+ newContent = existing.slice(0, markerIdx) + MARKER.trimStart() + formatted;
65
+ } else {
66
+ newContent = existing + MARKER + formatted;
67
+ }
68
+ fs.writeFileSync(filePath, newContent, 'utf8');
69
+ }
70
+
71
+ module.exports = { name, format, outputPath, write };
@@ -0,0 +1,47 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Copilot adapter — writes to .github/copilot-instructions.md
5
+ * GitHub Copilot reads this file automatically in every workspace.
6
+ *
7
+ * Contract:
8
+ * format(context, opts?) → string
9
+ * outputPath(cwd) → string
10
+ */
11
+
12
+ const path = require('path');
13
+
14
+ const name = 'copilot';
15
+
16
+ /**
17
+ * Format context for GitHub Copilot instructions.
18
+ * @param {string} context - Raw signature context string
19
+ * @param {object} [opts]
20
+ * @param {string} [opts.version] - SigMap version string
21
+ * @returns {string}
22
+ */
23
+ function format(context, opts = {}) {
24
+ if (!context || typeof context !== 'string') return '';
25
+ const version = opts.version || 'unknown';
26
+ const timestamp = new Date().toISOString();
27
+ const header = [
28
+ `<!-- Generated by SigMap gen-context.js v${version} -->`,
29
+ `<!-- Updated: ${timestamp} -->`,
30
+ `<!-- Do not edit below — regenerate with: node gen-context.js -->`,
31
+ '',
32
+ '# Code signatures',
33
+ '',
34
+ ].join('\n');
35
+ return header + context;
36
+ }
37
+
38
+ /**
39
+ * Return the output file path for this adapter.
40
+ * @param {string} cwd - Project root
41
+ * @returns {string}
42
+ */
43
+ function outputPath(cwd) {
44
+ return path.join(cwd, '.github', 'copilot-instructions.md');
45
+ }
46
+
47
+ module.exports = { name, format, outputPath };
@@ -0,0 +1,45 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Cursor adapter — writes to .cursorrules
5
+ * Cursor reads .cursorrules automatically in every workspace.
6
+ *
7
+ * Contract:
8
+ * format(context, opts?) → string
9
+ * outputPath(cwd) → string
10
+ */
11
+
12
+ const path = require('path');
13
+
14
+ const name = 'cursor';
15
+
16
+ /**
17
+ * Format context for Cursor rules file.
18
+ * @param {string} context - Raw signature context string
19
+ * @param {object} [opts]
20
+ * @param {string} [opts.version] - SigMap version string
21
+ * @returns {string}
22
+ */
23
+ function format(context, opts = {}) {
24
+ if (!context || typeof context !== 'string') return '';
25
+ const version = opts.version || 'unknown';
26
+ const timestamp = new Date().toISOString();
27
+ const header = [
28
+ `# Code signatures — generated by SigMap v${version}`,
29
+ `# Updated: ${timestamp}`,
30
+ `# Regenerate: node gen-context.js`,
31
+ '',
32
+ ].join('\n');
33
+ return header + context;
34
+ }
35
+
36
+ /**
37
+ * Return the output file path for this adapter.
38
+ * @param {string} cwd - Project root
39
+ * @returns {string}
40
+ */
41
+ function outputPath(cwd) {
42
+ return path.join(cwd, '.cursorrules');
43
+ }
44
+
45
+ module.exports = { name, format, outputPath };
@@ -0,0 +1,59 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Gemini adapter — formats context as a Gemini system instruction.
5
+ * Use the output as the `system_instruction` field in a Gemini API request.
6
+ *
7
+ * Example usage:
8
+ * const { format } = require('sigmap/adapters/gemini');
9
+ * const instruction = format(context);
10
+ * // Pass to: genAI.getGenerativeModel({ model: 'gemini-pro', systemInstruction: instruction })
11
+ *
12
+ * Contract:
13
+ * format(context, opts?) → string
14
+ * outputPath(cwd) → string
15
+ */
16
+
17
+ const path = require('path');
18
+
19
+ const name = 'gemini';
20
+
21
+ /**
22
+ * Format context as a Gemini system instruction.
23
+ * @param {string} context - Raw signature context string
24
+ * @param {object} [opts]
25
+ * @param {string} [opts.version] - SigMap version string
26
+ * @param {string} [opts.projectName] - Optional project name
27
+ * @returns {string}
28
+ */
29
+ function format(context, opts = {}) {
30
+ if (!context || typeof context !== 'string') return '';
31
+ const version = opts.version || 'unknown';
32
+ const timestamp = new Date().toISOString();
33
+ const projectLine = opts.projectName
34
+ ? `Project: ${opts.projectName}\n`
35
+ : '';
36
+
37
+ return [
38
+ `You are a coding assistant with complete knowledge of this codebase.`,
39
+ `The following code signatures were extracted by SigMap v${version} on ${timestamp}.`,
40
+ projectLine,
41
+ `These signatures represent every public function, class, and type in the project.`,
42
+ `Refer to them when answering questions about code structure, APIs, and implementation.`,
43
+ ``,
44
+ `## Code Signatures`,
45
+ ``,
46
+ context,
47
+ ].join('\n');
48
+ }
49
+
50
+ /**
51
+ * Return the output file path for this adapter.
52
+ * @param {string} cwd - Project root
53
+ * @returns {string}
54
+ */
55
+ function outputPath(cwd) {
56
+ return path.join(cwd, '.github', 'gemini-context.md');
57
+ }
58
+
59
+ module.exports = { name, format, outputPath };
@@ -0,0 +1,79 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * packages/adapters/index.js
5
+ * Central registry for all SigMap output adapters.
6
+ *
7
+ * Usage:
8
+ * const { getAdapter, listAdapters, adapt } = require('sigmap/adapters');
9
+ * const output = adapt(context, 'copilot', { version: '3.0.0' });
10
+ */
11
+
12
+ const path = require('path');
13
+
14
+ const ADAPTER_NAMES = ['copilot', 'claude', 'cursor', 'windsurf', 'openai', 'gemini'];
15
+
16
+ // Lazy-load adapters so unused ones don't pay any require() cost
17
+ const _cache = {};
18
+
19
+ /**
20
+ * Load and return an adapter module by name.
21
+ * @param {string} name - Adapter name (copilot|claude|cursor|windsurf|openai|gemini)
22
+ * @returns {{ name: string, format: Function, outputPath: Function }|null}
23
+ */
24
+ function getAdapter(name) {
25
+ if (!name || typeof name !== 'string') return null;
26
+ const key = name.toLowerCase();
27
+ if (!ADAPTER_NAMES.includes(key)) return null;
28
+ if (!_cache[key]) {
29
+ try {
30
+ _cache[key] = require(path.join(__dirname, key + '.js'));
31
+ } catch (_) {
32
+ return null;
33
+ }
34
+ }
35
+ return _cache[key];
36
+ }
37
+
38
+ /**
39
+ * List all available adapter names.
40
+ * @returns {string[]}
41
+ */
42
+ function listAdapters() {
43
+ return ADAPTER_NAMES.slice();
44
+ }
45
+
46
+ /**
47
+ * Format context using the named adapter.
48
+ * @param {string} context - Raw signature context string
49
+ * @param {string} adapterName - Adapter name
50
+ * @param {object} [opts] - Options passed to adapter.format()
51
+ * @returns {string} Formatted output string (empty string if adapter not found or context empty)
52
+ */
53
+ function adapt(context, adapterName, opts = {}) {
54
+ if (!context || typeof context !== 'string') return '';
55
+ const adapter = getAdapter(adapterName);
56
+ if (!adapter || typeof adapter.format !== 'function') return '';
57
+ try {
58
+ return adapter.format(context, opts);
59
+ } catch (_) {
60
+ return '';
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Map old `outputs` config values to new `adapters` names.
66
+ * Provides backward compatibility for existing configurations.
67
+ * @param {string[]} outputs - Legacy outputs array
68
+ * @returns {string[]} Equivalent adapters array
69
+ */
70
+ function outputsToAdapters(outputs) {
71
+ if (!Array.isArray(outputs)) return ['copilot'];
72
+ return outputs.map((o) => {
73
+ // All current output names already match adapter names
74
+ if (ADAPTER_NAMES.includes(o)) return o;
75
+ return o; // pass through unknowns — getAdapter() will handle gracefully
76
+ });
77
+ }
78
+
79
+ module.exports = { getAdapter, listAdapters, adapt, outputsToAdapters };
@@ -0,0 +1,60 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * OpenAI adapter — formats context as an OpenAI system message.
5
+ * Use the output as the `content` field of a system role message.
6
+ *
7
+ * Example usage in code:
8
+ * const { format } = require('sigmap/adapters/openai');
9
+ * const systemPrompt = format(context);
10
+ * // Pass to: openai.chat.completions.create({ messages: [{ role: 'system', content: systemPrompt }] })
11
+ *
12
+ * Contract:
13
+ * format(context, opts?) → string
14
+ * outputPath(cwd) → string
15
+ */
16
+
17
+ const path = require('path');
18
+
19
+ const name = 'openai';
20
+
21
+ /**
22
+ * Format context as an OpenAI system prompt.
23
+ * @param {string} context - Raw signature context string
24
+ * @param {object} [opts]
25
+ * @param {string} [opts.version] - SigMap version string
26
+ * @param {string} [opts.projectName] - Optional project name
27
+ * @returns {string}
28
+ */
29
+ function format(context, opts = {}) {
30
+ if (!context || typeof context !== 'string') return '';
31
+ const version = opts.version || 'unknown';
32
+ const timestamp = new Date().toISOString();
33
+ const projectLine = opts.projectName
34
+ ? `Project: ${opts.projectName}\n`
35
+ : '';
36
+
37
+ return [
38
+ `You are a coding assistant with full knowledge of this codebase.`,
39
+ `Below are the code signatures extracted by SigMap v${version} on ${timestamp}.`,
40
+ projectLine,
41
+ `Use these signatures to answer questions about the code accurately.`,
42
+ `When the user asks about a specific file or function, refer to the signatures below.`,
43
+ ``,
44
+ `## Code Signatures`,
45
+ ``,
46
+ context,
47
+ ].join('\n');
48
+ }
49
+
50
+ /**
51
+ * Return the output file path for this adapter.
52
+ * Writes a .openai-context.md file that can be loaded at runtime.
53
+ * @param {string} cwd - Project root
54
+ * @returns {string}
55
+ */
56
+ function outputPath(cwd) {
57
+ return path.join(cwd, '.github', 'openai-context.md');
58
+ }
59
+
60
+ module.exports = { name, format, outputPath };
@@ -0,0 +1,45 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Windsurf adapter — writes to .windsurfrules
5
+ * Windsurf reads .windsurfrules automatically in every workspace.
6
+ *
7
+ * Contract:
8
+ * format(context, opts?) → string
9
+ * outputPath(cwd) → string
10
+ */
11
+
12
+ const path = require('path');
13
+
14
+ const name = 'windsurf';
15
+
16
+ /**
17
+ * Format context for Windsurf rules file.
18
+ * @param {string} context - Raw signature context string
19
+ * @param {object} [opts]
20
+ * @param {string} [opts.version] - SigMap version string
21
+ * @returns {string}
22
+ */
23
+ function format(context, opts = {}) {
24
+ if (!context || typeof context !== 'string') return '';
25
+ const version = opts.version || 'unknown';
26
+ const timestamp = new Date().toISOString();
27
+ const header = [
28
+ `# Code signatures — generated by SigMap v${version}`,
29
+ `# Updated: ${timestamp}`,
30
+ `# Regenerate: node gen-context.js`,
31
+ '',
32
+ ].join('\n');
33
+ return header + context;
34
+ }
35
+
36
+ /**
37
+ * Return the output file path for this adapter.
38
+ * @param {string} cwd - Project root
39
+ * @returns {string}
40
+ */
41
+ function outputPath(cwd) {
42
+ return path.join(cwd, '.windsurfrules');
43
+ }
44
+
45
+ module.exports = { name, format, outputPath };
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigmap-cli",
3
- "version": "2.10.0",
3
+ "version": "3.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
  # sigmap-core
2
2
 
3
- Programmatic API for [SigMap](https://manojmallick.github.io/sigmap/) — zero-dependency code signature extraction, ranked retrieval, secret scanning, and project health scoring.
3
+ Programmatic API for [SigMap](https://manojmallick.github.io/sigmap/) — zero-dependency code signature extraction, ranked retrieval, secret scanning, project health scoring, and multi-adapter output formatting (v3.0+).
4
4
 
5
5
  ## Installation
6
6
 
@@ -13,7 +13,7 @@ npm install sigmap # installs the full package (CLI + core)
13
13
  ## Quick start
14
14
 
15
15
  ```js
16
- const { extract, rank, buildSigIndex, scan, score } = require('sigmap');
16
+ const { extract, rank, buildSigIndex, scan, score, adapt } = require('sigmap');
17
17
 
18
18
  // 1. Extract signatures from any source file
19
19
  const sigs = extract('function hello() { return "world"; }', 'javascript');
@@ -128,9 +128,21 @@ const health = score('/path/to/project');
128
128
 
129
129
  All existing CLI flags (`--generate`, `--watch`, `--mcp`, `--query`, `--analyze`, `--benchmark`, `--health`, …) are unchanged.
130
130
 
131
- ## What's next v2.10
131
+ ## v3.0Multi-Adapter Architecture (released)
132
132
 
133
- v2.10 adds reporting charts and advanced metrics for benchmark visibility. This milestone focuses on chart-ready report output, precision@K/recall@K/MRR trends, and CI-friendly metrics artifacts. See [issue #25](https://github.com/manojmallick/sigmap/issues/25).
133
+ v3.0 adds the `adapt()` function to `packages/core`, making the API **semver-stable**. Breaking changes now require v4.0.
134
+
135
+ ```js
136
+ const { adapt } = require('sigmap');
137
+
138
+ // Format context as an OpenAI system prompt
139
+ const systemPrompt = adapt(context, 'openai', { version: '3.0.0' });
140
+
141
+ // Format context as a Gemini system instruction
142
+ const geminiInstruction = adapt(context, 'gemini');
143
+
144
+ // All 6 adapters: copilot | claude | cursor | windsurf | openai | gemini
145
+ ```
134
146
 
135
147
  See the full [roadmap](https://manojmallick.github.io/sigmap/roadmap.html).
136
148
 
@@ -198,6 +198,33 @@ function score(cwd) {
198
198
  }
199
199
  }
200
200
 
201
+ // ---------------------------------------------------------------------------
202
+ // adapt(context, adapterName, opts?) → string (v3.0+)
203
+ // ---------------------------------------------------------------------------
204
+ /**
205
+ * Format a context string using the named output adapter.
206
+ *
207
+ * @param {string} context - Raw signature context string
208
+ * @param {string} adapterName - One of: 'copilot'|'claude'|'cursor'|'windsurf'|'openai'|'gemini'
209
+ * @param {object} [opts] - Passed through to adapter.format()
210
+ * @returns {string} Formatted output string (empty string if adapter not found)
211
+ *
212
+ * @example
213
+ * const { adapt } = require('sigmap');
214
+ * const systemPrompt = adapt(context, 'openai', { version: '3.0.0' });
215
+ *
216
+ * const copilotMd = adapt(context, 'copilot');
217
+ */
218
+ function adapt(context, adapterName, opts = {}) {
219
+ try {
220
+ const adaptersPath = path.resolve(__dirname, '..', 'adapters', 'index.js');
221
+ const { adapt: _adapt } = require(adaptersPath);
222
+ return _adapt(context, adapterName, opts);
223
+ } catch (_) {
224
+ return '';
225
+ }
226
+ }
227
+
201
228
  // ---------------------------------------------------------------------------
202
229
  // Exports
203
230
  // ---------------------------------------------------------------------------
@@ -212,4 +239,6 @@ module.exports = {
212
239
  scan,
213
240
  /** Compute project health score */
214
241
  score,
242
+ /** Format context using a named output adapter (v3.0+) */
243
+ adapt,
215
244
  };
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigmap-core",
3
- "version": "2.10.0",
3
+ "version": "3.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": [
@@ -11,6 +11,10 @@ const DEFAULTS = {
11
11
  // Output targets: 'copilot' | 'claude' | 'cursor' | 'windsurf'
12
12
  outputs: ['copilot'],
13
13
 
14
+ // Adapter targets (v3.0+): replaces 'outputs'. Same names, adds 'openai' | 'gemini'.
15
+ // Old 'outputs' config key is still accepted and silently maps to 'adapters'.
16
+ adapters: null,
17
+
14
18
  // Directories to scan (relative to project root)
15
19
  srcDirs: [
16
20
  'src', 'app', 'lib', 'packages', 'services', 'api',
@@ -50,6 +50,13 @@ function loadConfig(cwd) {
50
50
  merged[key] = val;
51
51
  }
52
52
  }
53
+ // Backward compat (v3.0+): mirror outputs ↔ adapters
54
+ if (merged.adapters && !Array.isArray(merged.adapters)) merged.adapters = null;
55
+ if (!merged.adapters && Array.isArray(merged.outputs)) {
56
+ merged.adapters = merged.outputs.slice();
57
+ } else if (Array.isArray(merged.adapters) && !userConfig.outputs) {
58
+ merged.outputs = merged.adapters.filter((a) => ['copilot','claude','cursor','windsurf'].includes(a));
59
+ }
53
60
  return merged;
54
61
  }
55
62
 
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: '2.10.0',
21
+ version: '3.0.1',
22
22
  description: 'SigMap MCP server — code signatures on demand',
23
23
  };
24
24