sigmap 6.10.0 → 6.10.2

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
@@ -10,6 +10,37 @@ Format: [Semantic Versioning](https://semver.org/)
10
10
 
11
11
  ---
12
12
 
13
+ ## [6.10.2] — 2026-05-11
14
+
15
+ ### Added
16
+
17
+ - **Open-source agents documentation** — Comprehensive integration guides for OpenCode, Aider, OpenHands, and Cline with setup examples and context injection patterns. Clear separation of coding agents from inference backends.
18
+ - **Local LLM workflows guide** — Complete setup guide for Ollama, llama.cpp, vLLM with model recommendations, performance tuning, and benchmarking. Emphasizes model-agnostic nature: no API costs, full privacy, offline capability.
19
+ - **Integrations sidebar** — New VitePress navigation section highlighting open-source agents, local LLMs, MCP server, and Repomix integration.
20
+
21
+ ### Changed
22
+
23
+ - **README model-agnostic messaging** — Updated to clarify support for cloud LLMs, open-source agents, and local models with full privacy. Removed proprietary-focused language.
24
+ - **Quick-start guide** — Added links to new agent and local-LLM guides in "Next steps" section.
25
+
26
+ ---
27
+
28
+ ## [6.10.1] — 2026-05-10
29
+
30
+ ### Added
31
+
32
+ - **R language support (Phase 1)** — Extract function signatures from `.r` and `.R` files with support for function definitions (`<-`, `=`, `<<-` forms), multi-line arguments with string-literal protection, S4 patterns (setGeneric, setMethod, setClass), and private function filtering. Shiny framework detection via `app.R`/`ui.R`/`server.R` triplet.
33
+ - **Native Python AST extractor** — Fallback to `python_ast.py` using `ast.parse()` for accurate extraction of complex signatures (multiline args, stacked decorators, complex generics). Preserves regex fallback for Python 2 / no-Python3 environments. Zero breaking changes to output format.
34
+
35
+ ### Fixed
36
+
37
+ - **ReferenceError in `--query`** — Fixed variable scope issue where `adpIdx` was undefined when no context file present. Moved variable declaration to proper scope before conditional block.
38
+ - **Windows path handling** — Normalized path separators in nested path deduplication. Windows backslashes no longer cause false negatives when matching nested source roots.
39
+ - **.contextignore patterns** — Fixed bracket character classes (`[Bb]in/`) being treated as literals. Fixed trailing slashes on directory patterns not matching nested paths. Added error handling for malformed bracket syntax.
40
+ - **Claude adapter in per-module and hot-cold strategies** — Fixed adapter not being written to output in per-module and hot-cold context strategies.
41
+
42
+ ---
43
+
13
44
  ## [6.10.0] — 2026-05-05
14
45
 
15
46
  ### Added
package/README.md CHANGED
@@ -12,7 +12,8 @@
12
12
  [![Zero deps](https://img.shields.io/badge/dependencies-zero-22c55e)](package.json)
13
13
  [![License: MIT](https://img.shields.io/badge/License-MIT-7c6af7.svg)](LICENSE)
14
14
  [![GitHub Stars](https://img.shields.io/github/stars/manojmallick/sigmap?style=flat&color=f59e0b&logo=github)](https://github.com/manojmallick/sigmap/stargazers)
15
- [![Hacker News](https://img.shields.io/badge/Hacker%20News-Discussion-orange?logo=ycombinator)](https://news.ycombinator.com/item?id=47956790)
15
+ [![Star History Chart](https://api.star-history.com/svg?repos=manojmallick/sigmap&type=Date)](https://star-history.com/#manojmallick/sigmap&Date)
16
+ [![Discover on ShyPD](https://img.shields.io/badge/ShyPD-Discover-7c6af7?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDE2IDE2Ij48Y2lyY2xlIGN4PSI4IiBjeT0iOCIgcj0iOCIgZmlsbD0id2hpdGUiLz48L3N2Zz4=&logoColor=7c6af7)](https://shypd.ai/tools/sigmap)
16
17
 
17
18
  </div>
18
19
 
@@ -20,9 +21,11 @@
20
21
 
21
22
  ## Try it now
22
23
 
24
+ **No install required.** Run instantly on any machine:
25
+
23
26
  ```bash
24
27
  npx sigmap
25
- sigmap ask "Where is auth handled?"
28
+ npx sigmap ask "Where is auth handled?"
26
29
  ```
27
30
 
28
31
  Zero config. Zero dependencies. Under 10 seconds.
@@ -33,7 +36,12 @@ Zero config. Zero dependencies. Under 10 seconds.
33
36
 
34
37
  SigMap extracts function and class signatures from your codebase and feeds the right files — not the whole repo — to your AI.
35
38
 
36
- Works with Copilot, Claude, Cursor, Windsurf, and any LLM.
39
+ **Model-agnostic.** Works with:
40
+ - **Cloud LLMs:** Claude, GPT-4, Copilot, Gemini
41
+ - **Open-source agents:** OpenCode, Aider, OpenHands, Cline
42
+ - **Local LLMs:** Ollama, llama.cpp, vLLM (no API keys, full privacy)
43
+ - **Any editor:** VS Code, Cursor, Windsurf, Neovim, JetBrains
44
+ - **Any model:** Use what you want, no vendor lock-in
37
45
 
38
46
  ---
39
47
 
@@ -43,7 +51,9 @@ Works with Copilot, Claude, Cursor, Windsurf, and any LLM.
43
51
  - **40–98% token reduction** — 2K–4K tokens instead of 80K+
44
52
  - **52.2% task success rate** — up from 10% without context
45
53
  - **1.68 prompts per task** — down from 2.84
46
- - **Works with any LLM** — no API key, no cloud, no accounts
54
+ - **No vendor lock-in** — works with any AI assistant or local LLM
55
+ - **No API costs** — use local models (Ollama, llama.cpp, vLLM) with zero token fees
56
+ - **Full privacy** — keep your code and context on your machine
47
57
  - **Zero npm dependencies** — `npx sigmap` on any machine
48
58
 
49
59
  ---
@@ -144,20 +154,26 @@ volta install sigmap
144
154
 
145
155
  | Adapter | Output file | Used by |
146
156
  |---|---|---|
147
- | `copilot` | `.github/copilot-instructions.md` | GitHub Copilot |
157
+ | `copilot` | `.github/copilot-instructions.md` | GitHub Copilot, OpenCode |
148
158
  | `claude` | `CLAUDE.md` | Claude / Claude Code |
149
- | `cursor` | `.cursorrules` | Cursor |
159
+ | `cursor` | `.cursorrules` | Cursor, Cline |
150
160
  | `windsurf` | `.windsurfrules` | Windsurf |
151
- | `openai` | `.github/openai-context.md` | OpenAI models |
161
+ | `openai` | `.github/openai-context.md` | OpenAI API, Aider, local Ollama/llama.cpp |
152
162
  | `gemini` | `.github/gemini-context.md` | Google Gemini |
153
- | `codex` | `AGENTS.md` | OpenAI Codex · OpenCode |
163
+ | `codex` | `AGENTS.md` | OpenAI Codex (legacy) |
154
164
 
155
165
  ```bash
156
- sigmap --adapter copilot # default
157
- sigmap --adapter claude
158
- sigmap --adapter cursor
166
+ sigmap --adapter copilot # default — works with Copilot, OpenCode
167
+ sigmap --adapter openai # works with Ollama, llama.cpp, vLLM, Aider
168
+ sigmap --adapter claude # works with Claude Code
159
169
  ```
160
170
 
171
+ **Open-source agents & local LLMs:**
172
+
173
+ Use SigMap with open-source tools and fully self-hosted setups:
174
+ - **[Open-source agents guide →](https://manojmallick.github.io/sigmap/guide/agents)** — OpenCode, Aider, OpenHands, Cline
175
+ - **[Local LLMs guide →](https://manojmallick.github.io/sigmap/guide/local-llms)** — Ollama, llama.cpp, vLLM (no API keys, full privacy)
176
+
161
177
  **IDE extensions:**
162
178
 
163
179
  | IDE | Install | Source | Features |
@@ -228,6 +244,20 @@ If SigMap saves you context or API spend, a ⭐ on [GitHub](https://github.com/m
228
244
 
229
245
  ---
230
246
 
247
+ ## Contributing
248
+
249
+ SigMap welcomes contributions!
250
+
251
+ **Before submitting a PR:**
252
+ 1. Read [CONTRIBUTING.md](CONTRIBUTING.md)
253
+ 2. Check [Discussions → Announcements](../../discussions) for workflow setup
254
+ 3. Target the `develop` branch (not main)
255
+ 4. Follow the [contributor checklist](.github/CONTRIBUTOR_CHECKLIST.txt)
256
+
257
+ See [.github/PULL_REQUEST_TEMPLATE.md](.github/PULL_REQUEST_TEMPLATE.md) for the PR checklist. All contributors are credited in the CHANGELOG and release notes.
258
+
259
+ ---
260
+
231
261
  ## Why not embeddings?
232
262
 
233
263
  | | Embeddings | SigMap |
package/gen-context.js CHANGED
@@ -2980,6 +2980,148 @@ __factories["./src/extractors/prdiff"] = function(module, exports) {
2980
2980
 
2981
2981
  };
2982
2982
 
2983
+ // ── ./src/extractors/r ──
2984
+ __factories["./src/extractors/r"] = function(module, exports) {
2985
+
2986
+ 'use strict';
2987
+
2988
+ /**
2989
+ * Extract signatures from R source code.
2990
+ * @param {string} src - Raw file content
2991
+ * @returns {string[]} Array of signature strings
2992
+ */
2993
+ function extract(src) {
2994
+ if (!src || typeof src !== 'string') return [];
2995
+ const sigs = [];
2996
+
2997
+ // Strip line comments. R uses # comments. Roxygen2 (#') comments are
2998
+ // stripped along with regular ones; Phase 2 may parse them.
2999
+ const stripped = src.replace(/#.*$/gm, '');
3000
+
3001
+ // Function definitions:
3002
+ // name <- function(args) { ... }
3003
+ // name = function(args) { ... }
3004
+ // name <<- function(args) { ... }
3005
+ // Args may span multiple lines and contain default values, so we need to
3006
+ // match a balanced parenthesis group rather than a single line.
3007
+ const funcRe = /^(?:[ \t]*)([\w.]+)\s*(?:<<-|<-|=)\s*function\s*\(/gm;
3008
+ let m;
3009
+ while ((m = funcRe.exec(stripped)) !== null) {
3010
+ const name = m[1];
3011
+ if (name.startsWith('.')) continue; // private convention
3012
+ const argsStart = funcRe.lastIndex;
3013
+ const args = readBalancedParens(stripped, argsStart - 1);
3014
+ if (args === null) continue;
3015
+ sigs.push(`${name} <- function(${normalizeParams(args)})`);
3016
+ }
3017
+
3018
+ // S4 setMethod / setGeneric:
3019
+ // setGeneric("name", function(args) standardGeneric("name"))
3020
+ // setMethod("name", "ClassName", function(args) { ... })
3021
+ for (const sm of stripped.matchAll(/^[ \t]*setGeneric\s*\(\s*["']([\w.]+)["']/gm)) {
3022
+ sigs.push(`setGeneric("${sm[1]}")`);
3023
+ }
3024
+ for (const sm of stripped.matchAll(/^[ \t]*setMethod\s*\(\s*["']([\w.]+)["']\s*,\s*["']([\w.]+)["']/gm)) {
3025
+ sigs.push(`setMethod("${sm[1]}", "${sm[2]}")`);
3026
+ }
3027
+
3028
+ // S4 class definitions:
3029
+ // setClass("Name", representation(...), ...)
3030
+ for (const sm of stripped.matchAll(/^[ \t]*setClass\s*\(\s*["']([\w.]+)["']/gm)) {
3031
+ sigs.push(`setClass("${sm[1]}")`);
3032
+ }
3033
+
3034
+ return sigs.slice(0, 30);
3035
+ }
3036
+
3037
+ /**
3038
+ * Read a parenthesis-balanced substring starting at the position of the
3039
+ * opening '(' character, returning the inner content (without the outer
3040
+ * parens). Returns null if no matching close paren is found within `cap`
3041
+ * characters, which guards against runaway scans on malformed input.
3042
+ */
3043
+ function readBalancedParens(src, openIdx, cap = 4096) {
3044
+ if (src[openIdx] !== '(') return null;
3045
+ let depth = 1;
3046
+ let i = openIdx + 1;
3047
+ const end = Math.min(src.length, openIdx + cap);
3048
+ let inString = null; // null | '"' | "'"
3049
+ while (i < end) {
3050
+ const ch = src[i];
3051
+ if (inString) {
3052
+ if (ch === '\\') { i += 2; continue; }
3053
+ if (ch === inString) inString = null;
3054
+ i++;
3055
+ continue;
3056
+ }
3057
+ if (ch === '"' || ch === "'") { inString = ch; i++; continue; }
3058
+ if (ch === '(') depth++;
3059
+ else if (ch === ')') {
3060
+ depth--;
3061
+ if (depth === 0) return src.slice(openIdx + 1, i);
3062
+ }
3063
+ i++;
3064
+ }
3065
+ return null;
3066
+ }
3067
+
3068
+ /**
3069
+ * Compress whitespace inside a parameter list, collapse multi-line default
3070
+ * expressions onto a single line, and trim. The goal is one-line readable
3071
+ * signatures, not a faithful AST.
3072
+ *
3073
+ * String literals are protected so that commas/equals inside default values
3074
+ * like sep = "," don't get respaced.
3075
+ */
3076
+ function normalizeParams(raw) {
3077
+ const tokens = [];
3078
+ let buf = '';
3079
+ let inString = null;
3080
+ for (let i = 0; i < raw.length; i++) {
3081
+ const ch = raw[i];
3082
+ if (inString) {
3083
+ buf += ch;
3084
+ if (ch === '\\' && i + 1 < raw.length) { buf += raw[i + 1]; i++; continue; }
3085
+ if (ch === inString) inString = null;
3086
+ continue;
3087
+ }
3088
+ if (ch === '"' || ch === "'") { inString = ch; buf += ch; continue; }
3089
+ buf += ch;
3090
+ }
3091
+ // Now buf === raw with strings preserved character-for-character.
3092
+ // Walk again: collapse non-string runs of whitespace, normalize ', ' and ' = '.
3093
+ let out = '';
3094
+ inString = null;
3095
+ for (let i = 0; i < buf.length; i++) {
3096
+ const ch = buf[i];
3097
+ if (inString) {
3098
+ out += ch;
3099
+ if (ch === '\\' && i + 1 < buf.length) { out += buf[i + 1]; i++; continue; }
3100
+ if (ch === inString) inString = null;
3101
+ continue;
3102
+ }
3103
+ if (ch === '"' || ch === "'") { inString = ch; out += ch; continue; }
3104
+ if (/\s/.test(ch)) {
3105
+ if (out.length && !/\s$/.test(out)) out += ' ';
3106
+ continue;
3107
+ }
3108
+ if (ch === ',') {
3109
+ out = out.replace(/\s+$/, '') + ', ';
3110
+ continue;
3111
+ }
3112
+ if (ch === '=') {
3113
+ out = out.replace(/\s+$/, '') + ' = ';
3114
+ continue;
3115
+ }
3116
+ out += ch;
3117
+ }
3118
+ return out.trim();
3119
+ }
3120
+
3121
+ module.exports = { extract };
3122
+
3123
+ };
3124
+
2983
3125
  // ── ./src/format/cache ──
2984
3126
  __factories["./src/format/cache"] = function(module, exports) {
2985
3127
 
@@ -5387,7 +5529,7 @@ __factories["./src/mcp/server"] = function(module, exports) {
5387
5529
 
5388
5530
  const SERVER_INFO = {
5389
5531
  name: 'sigmap',
5390
- version: '6.10.0',
5532
+ version: '6.10.2',
5391
5533
  description: 'SigMap MCP server — code signatures on demand',
5392
5534
  };
5393
5535
 
@@ -7913,6 +8055,95 @@ __factories["./src/discovery/source-root-resolver"] = function(module, exports)
7913
8055
  module.exports = { resolveSourceRoots };
7914
8056
  };
7915
8057
 
8058
+ // ── ./src/workspace/detector ──
8059
+ __factories["./src/workspace/detector"] = function(module, exports) {
8060
+ 'use strict';
8061
+ const fs = require('fs');
8062
+ const path = require('path');
8063
+ module.exports = { detectWorkspaces, inferPackage, scopeToPackage };
8064
+
8065
+ function detectWorkspaces(cwd) {
8066
+ const pkgPath = path.join(cwd, 'package.json');
8067
+ if (!fs.existsSync(pkgPath)) return [];
8068
+
8069
+ let pkg;
8070
+ try {
8071
+ pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
8072
+ } catch {
8073
+ return [];
8074
+ }
8075
+
8076
+ const patterns = pkg.workspaces || [];
8077
+ const dirs = [];
8078
+
8079
+ // Handle both flat array and object with packages field (Yarn v2 format)
8080
+ const patternArray = Array.isArray(patterns) ? patterns : (patterns.packages || []);
8081
+
8082
+ for (const p of patternArray) {
8083
+ const base = p.replace(/\/\*\*?$/, '');
8084
+ const resolved = path.join(cwd, base);
8085
+ if (fs.existsSync(resolved)) {
8086
+ try {
8087
+ for (const entry of fs.readdirSync(resolved, { withFileTypes: true })) {
8088
+ if (entry.isDirectory()) dirs.push(path.join(resolved, entry.name));
8089
+ }
8090
+ } catch (_) {}
8091
+ }
8092
+ }
8093
+
8094
+ return dirs;
8095
+ }
8096
+
8097
+ // Infer package from query tokens: "add rate limiting to payments" → "packages/payments"
8098
+ function inferPackage(query, workspaceDirs, cwd) {
8099
+ const tokens = query.toLowerCase().split(/\W+/).filter(t => t.length > 2);
8100
+
8101
+ // Find longest matching package name
8102
+ let bestMatch = null;
8103
+ let bestLen = 0;
8104
+ let bestMatchLen = 0;
8105
+
8106
+ for (const dir of workspaceDirs) {
8107
+ const name = path.basename(dir).toLowerCase();
8108
+ for (const token of tokens) {
8109
+ const matchLen = _getMatchLength(name, token);
8110
+ // Only consider matches; use longest match, and break ties by longest package name
8111
+ if (matchLen > 0 && (matchLen > bestLen || (matchLen === bestLen && name.length > bestMatchLen))) {
8112
+ bestMatch = dir;
8113
+ bestLen = matchLen;
8114
+ bestMatchLen = name.length;
8115
+ }
8116
+ }
8117
+ }
8118
+
8119
+ return bestMatch;
8120
+ }
8121
+
8122
+ function _getMatchLength(name, token) {
8123
+ if (name === token) return 1000 + name.length; // Exact match is best
8124
+ if (name.startsWith(token) && token.length >= 3) return 100 + token.length;
8125
+ if (token.startsWith(name) && name.length >= 3) return name.length;
8126
+ return 0;
8127
+ }
8128
+
8129
+ // Return boost multiplier for files inside the inferred package
8130
+ function scopeToPackage(filePath, packageDir) {
8131
+ const normalized = filePath.replace(/\\/g, '/');
8132
+ const normalizedPkg = packageDir.replace(/\\/g, '/');
8133
+
8134
+ // Ensure we match the directory boundary, not just a prefix
8135
+ // e.g., packages/payment should not match packages/payment-old
8136
+ if (normalized.startsWith(normalizedPkg)) {
8137
+ const afterPrefix = normalized.slice(normalizedPkg.length);
8138
+ // Check if next char is / or if it's the exact match
8139
+ if (afterPrefix === '' || afterPrefix[0] === '/') {
8140
+ return 0.30;
8141
+ }
8142
+ }
8143
+ return 0;
8144
+ }
8145
+ };
8146
+
7916
8147
  /**
7917
8148
  * SigMap — gen-context.js v1.2.0
7918
8149
  * Zero-dependency AI context engine.
@@ -7925,7 +8156,7 @@ const path = require('path');
7925
8156
  const os = require('os');
7926
8157
  const { execSync } = require('child_process');
7927
8158
 
7928
- const VERSION = '6.10.0';
8159
+ const VERSION = '6.10.2';
7929
8160
  const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
7930
8161
 
7931
8162
  function requireSourceOrBundled(key) {
@@ -8005,14 +8236,22 @@ function loadIgnorePatterns(cwd) {
8005
8236
  function matchesIgnore(relPath, patterns) {
8006
8237
  for (const pat of patterns) {
8007
8238
  const normalized = pat.replace(/\\/g, '/');
8008
- // Simple glob: support * and ** and trailing /
8009
- const regexStr = normalized
8010
- .replace(/[.+^${}()|[\]\\]/g, '\\$&')
8239
+ // Strip trailing slash (gitignore style directory patterns)
8240
+ const patternToUse = normalized.endsWith('/')
8241
+ ? normalized.slice(0, -1)
8242
+ : normalized;
8243
+ // Escape regex special chars but NOT brackets (keep them for character classes)
8244
+ const regexStr = patternToUse
8245
+ .replace(/[.+^${}()|\\]/g, '\\$&')
8011
8246
  .replace(/\*\*/g, '___DOUBLE___')
8012
8247
  .replace(/\*/g, '[^/]*')
8013
8248
  .replace(/___DOUBLE___/g, '.*');
8014
- const regex = new RegExp(`(^|/)${regexStr}($|/)`);
8015
- if (regex.test(relPath)) return true;
8249
+ try {
8250
+ const regex = new RegExp(`(^|/)${regexStr}($|/)`);
8251
+ if (regex.test(relPath)) return true;
8252
+ } catch (_) {
8253
+ // Malformed bracket syntax or invalid regex — skip this pattern
8254
+ }
8016
8255
  }
8017
8256
  return false;
8018
8257
  }
@@ -8940,7 +9179,7 @@ function runPerModuleStrategy(cwd, config, fileEntries, inputTokenTotal) {
8940
9179
  overviewLines.push('> Inject the relevant module file into your IDE context window.');
8941
9180
  overviewLines.push('> For cross-module questions load both files.');
8942
9181
  const overviewContent = overviewLines.join('\n') + '\n';
8943
- const primaryTargets = (config.outputs || ['copilot']).filter((t) => t !== 'claude');
9182
+ const primaryTargets = config.outputs || ['copilot'];
8944
9183
  writeOutputs(overviewContent, primaryTargets, cwd, config);
8945
9184
 
8946
9185
  const overviewTokens = estimateTokens(overviewContent);
@@ -8959,7 +9198,7 @@ function runHotColdStrategy(cwd, config, fileEntries, recentFiles, inputTokenTot
8959
9198
  const hotContent = hotEntries.length > 0
8960
9199
  ? formatOutput(hotEntries, cwd, false, config, null)
8961
9200
  : '<!-- Generated by SigMap — no recently changed files -->\n';
8962
- const primaryTargets = (config.outputs || ['copilot']).filter((t) => t !== 'claude');
9201
+ const primaryTargets = config.outputs || ['copilot'];
8963
9202
  writeOutputs(hotContent, primaryTargets, cwd, config);
8964
9203
  const hotTokens = estimateTokens(hotContent);
8965
9204
 
@@ -11093,6 +11332,7 @@ function main() {
11093
11332
  // Priority: --output flag > --adapter flag > buildSigIndex probe order
11094
11333
  // (customOutput from config is handled inside buildSigIndex itself)
11095
11334
  let queryOpts;
11335
+ const adpIdx = args.indexOf('--adapter');
11096
11336
 
11097
11337
  // 1. --output <file> pins to an explicit path
11098
11338
  if (config.customOutput) {
@@ -11100,17 +11340,14 @@ function main() {
11100
11340
  }
11101
11341
 
11102
11342
  // 2. --adapter <name> pins to that adapter's output path (if --output not given)
11103
- if (!queryOpts) {
11104
- const adpIdx = args.indexOf('--adapter');
11105
- if (adpIdx >= 0) {
11106
- const adapterName = (args[adpIdx + 1] || '').trim().toLowerCase();
11107
- const VALID_ADAPTERS = ['copilot', 'claude', 'cursor', 'windsurf', 'openai', 'gemini', 'codex'];
11108
- if (VALID_ADAPTERS.includes(adapterName)) {
11109
- try {
11110
- const adapterMod = __require('./packages/adapters/' + adapterName);
11111
- queryOpts = { contextPath: adapterMod.outputPath(cwd) };
11112
- } catch (_) {}
11113
- }
11343
+ if (!queryOpts && adpIdx >= 0) {
11344
+ const adapterName = (args[adpIdx + 1] || '').trim().toLowerCase();
11345
+ const VALID_ADAPTERS = ['copilot', 'claude', 'cursor', 'windsurf', 'openai', 'gemini', 'codex'];
11346
+ if (VALID_ADAPTERS.includes(adapterName)) {
11347
+ try {
11348
+ const adapterMod = __require('./packages/adapters/' + adapterName);
11349
+ queryOpts = { contextPath: adapterMod.outputPath(cwd) };
11350
+ } catch (_) {}
11114
11351
  }
11115
11352
  }
11116
11353
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigmap",
3
- "version": "6.10.0",
3
+ "version": "6.10.2",
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": {
@@ -11,14 +11,16 @@
11
11
 
12
12
  const path = require('path');
13
13
 
14
- const ADAPTER_NAMES = ['copilot', 'claude', 'cursor', 'windsurf', 'openai', 'gemini', 'codex'];
14
+ // Third-party adapters: 'willow' writes atoms to a Willow MCP knowledge store
15
+ // instead of a flat file (see willow.js for configuration).
16
+ const ADAPTER_NAMES = ['copilot', 'claude', 'cursor', 'windsurf', 'openai', 'gemini', 'codex', 'willow'];
15
17
 
16
18
  // Lazy-load adapters so unused ones don't pay any require() cost
17
19
  const _cache = {};
18
20
 
19
21
  /**
20
22
  * Load and return an adapter module by name.
21
- * @param {string} name - Adapter name (copilot|claude|cursor|windsurf|openai|gemini|codex)
23
+ * @param {string} name - Adapter name (copilot|claude|cursor|windsurf|openai|gemini|codex|willow)
22
24
  * @returns {{ name: string, format: Function, outputPath: Function }|null}
23
25
  */
24
26
  function getAdapter(name) {