sigmap 4.1.1 → 4.1.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 +40 -0
- package/README.md +24 -0
- package/gen-context.js +99 -14
- package/package.json +1 -1
- package/src/retrieval/ranker.js +21 -8
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,46 @@ Format: [Semantic Versioning](https://semver.org/)
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
+
## [4.1.2] — 2026-04-16 — Feat: --output <file> flag for custom context path
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **`--output <file>` flag** — write signatures to any custom path, not just
|
|
18
|
+
an adapter's fixed location:
|
|
19
|
+
```bash
|
|
20
|
+
sigmap --output .context/ai-context.md # default generation
|
|
21
|
+
sigmap --adapter claude --output shared/sigs.md # adapter + custom path
|
|
22
|
+
```
|
|
23
|
+
The custom file is written **in addition to** the adapter's default output so
|
|
24
|
+
existing tooling is unaffected.
|
|
25
|
+
|
|
26
|
+
- **Automatic discovery for `--query`** — the resolved path is persisted to
|
|
27
|
+
`gen-context.config.json` as `customOutput` so subsequent `--query` runs
|
|
28
|
+
find it automatically without needing to pass `--output` again:
|
|
29
|
+
```bash
|
|
30
|
+
sigmap --output .context/ai-context.md # generates + persists path
|
|
31
|
+
sigmap --query "add a new extractor" # auto-finds .context/ai-context.md
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
- **Priority order for `--query` context resolution** (most specific first):
|
|
35
|
+
1. `--output <file>` flag — explicit path
|
|
36
|
+
2. `--adapter <name>` flag — adapter's fixed output path
|
|
37
|
+
3. `customOutput` in `gen-context.config.json` — persisted from last `--output` run
|
|
38
|
+
4. Probe all known adapter output paths — existing fallback behaviour
|
|
39
|
+
|
|
40
|
+
- **Nested directories created automatically** — `--output a/b/c/file.md`
|
|
41
|
+
creates any missing parent directories.
|
|
42
|
+
|
|
43
|
+
### Tests
|
|
44
|
+
|
|
45
|
+
- Added `test/integration/output-flag.test.js` (13 tests) covering: custom
|
|
46
|
+
file creation, parseable headers, config persistence, nested dirs, missing
|
|
47
|
+
arg error, `--adapter` + `--output` combo, explicit `--query` with `--output`,
|
|
48
|
+
auto-discovery via persisted config, missing-file error, `--output` overrides
|
|
49
|
+
`--adapter` during `--query`.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
13
53
|
## [4.1.1] — 2026-04-16 — Fix: --query works with any adapter output
|
|
14
54
|
|
|
15
55
|
### Fixed
|
package/README.md
CHANGED
|
@@ -353,6 +353,24 @@ Configure multiple adapters at once in `gen-context.config.json`:
|
|
|
353
353
|
|
|
354
354
|
Use SigMap as a Node.js library without spawning a subprocess. See the [full API reference](#-programmatic-api) below.
|
|
355
355
|
|
|
356
|
+
### Custom output path
|
|
357
|
+
|
|
358
|
+
Write signatures to any file location — useful for shared docs folders, monorepos,
|
|
359
|
+
or tooling that expects context at a non-standard path:
|
|
360
|
+
|
|
361
|
+
```bash
|
|
362
|
+
sigmap --output .context/ai-context.md # write to custom path
|
|
363
|
+
sigmap --adapter claude --output shared/sigs.md # adapter + custom path
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
The path is persisted to `gen-context.config.json`, so `--query` finds it
|
|
367
|
+
automatically on subsequent runs — no need to pass `--output` again:
|
|
368
|
+
|
|
369
|
+
```bash
|
|
370
|
+
sigmap --output .context/ai-context.md # generates and saves the path
|
|
371
|
+
sigmap --query "add an extractor" # auto-discovers .context/ai-context.md
|
|
372
|
+
```
|
|
373
|
+
|
|
356
374
|
### Query-aware retrieval
|
|
357
375
|
|
|
358
376
|
Find the most relevant files for any task without reading the whole codebase:
|
|
@@ -361,6 +379,7 @@ Find the most relevant files for any task without reading the whole codebase:
|
|
|
361
379
|
sigmap --query "authentication middleware" # ranked file list
|
|
362
380
|
sigmap --query "auth" --json # machine-readable output
|
|
363
381
|
sigmap --query "auth" --top 5 # top 5 results only
|
|
382
|
+
sigmap --query "auth" --adapter claude # query against CLAUDE.md specifically
|
|
364
383
|
```
|
|
365
384
|
|
|
366
385
|
### Diagnostic and evaluation tools
|
|
@@ -616,9 +635,14 @@ sigmap --diff Generate context for git-changed f
|
|
|
616
635
|
sigmap --diff --staged Staged files only (pre-commit check)
|
|
617
636
|
sigmap --mcp Start MCP server on stdio
|
|
618
637
|
|
|
638
|
+
sigmap --output <file> Write signatures to a custom path (persists for --query)
|
|
639
|
+
sigmap --output <file> --adapter <name> Adapter output + custom copy
|
|
640
|
+
|
|
619
641
|
sigmap --query "<text>" Rank files by relevance to a query
|
|
620
642
|
sigmap --query "<text>" --json Ranked results as JSON
|
|
621
643
|
sigmap --query "<text>" --top <n> Limit results to top N files (default 10)
|
|
644
|
+
sigmap --query "<text>" --adapter <name> Query against a specific adapter's output file
|
|
645
|
+
sigmap --query "<text>" --output <file> Query against a specific custom file
|
|
622
646
|
|
|
623
647
|
sigmap --analyze Per-file breakdown (sigs / tokens / extractor / coverage)
|
|
624
648
|
sigmap --analyze --json Analysis as JSON
|
package/gen-context.js
CHANGED
|
@@ -5479,8 +5479,19 @@ __factories["./src/retrieval/ranker"] = function(module, exports) {
|
|
|
5479
5479
|
return index;
|
|
5480
5480
|
}
|
|
5481
5481
|
function buildSigIndex(cwd, opts) {
|
|
5482
|
-
const path = require('path');
|
|
5482
|
+
const fs = require('fs'); const path = require('path');
|
|
5483
5483
|
if (opts && opts.contextPath) return _parseContextFile(opts.contextPath);
|
|
5484
|
+
// Check gen-context.config.json for a persisted customOutput path.
|
|
5485
|
+
try {
|
|
5486
|
+
const cfgPath = path.join(cwd, 'gen-context.config.json');
|
|
5487
|
+
if (fs.existsSync(cfgPath)) {
|
|
5488
|
+
const cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf8'));
|
|
5489
|
+
if (cfg.customOutput) {
|
|
5490
|
+
const idx = _parseContextFile(path.resolve(cwd, cfg.customOutput));
|
|
5491
|
+
if (idx.size > 0) return idx;
|
|
5492
|
+
}
|
|
5493
|
+
}
|
|
5494
|
+
} catch (_) {}
|
|
5484
5495
|
for (const parts of ADAPTER_OUTPUT_PATHS) {
|
|
5485
5496
|
const contextPath = path.join(cwd, ...parts);
|
|
5486
5497
|
const index = _parseContextFile(contextPath);
|
|
@@ -6238,7 +6249,7 @@ const path = require('path');
|
|
|
6238
6249
|
const os = require('os');
|
|
6239
6250
|
const { execSync } = require('child_process');
|
|
6240
6251
|
|
|
6241
|
-
const VERSION = '4.1.
|
|
6252
|
+
const VERSION = '4.1.2';
|
|
6242
6253
|
const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
|
|
6243
6254
|
|
|
6244
6255
|
function requireSourceOrBundled(key) {
|
|
@@ -6926,6 +6937,19 @@ function writeOutputs(content, targets, cwd, config) {
|
|
|
6926
6937
|
if (ADAPTER_TARGETS.has(target)) {
|
|
6927
6938
|
try {
|
|
6928
6939
|
const adapterMod = __require('./packages/adapters/' + target);
|
|
6940
|
+
// copilot: honour config.output custom path (redirects away from default .github/copilot-instructions.md)
|
|
6941
|
+
if (target === 'copilot') {
|
|
6942
|
+
const outPath = resolveAdapterPath('copilot', cwd, config);
|
|
6943
|
+
const defaultPath = path.join(cwd, '.github', 'copilot-instructions.md');
|
|
6944
|
+
if (outPath !== defaultPath) {
|
|
6945
|
+
// custom path: format and write directly (no append logic)
|
|
6946
|
+
const formatted = adapterMod.format(content, { version: VERSION });
|
|
6947
|
+
ensureDir(outPath);
|
|
6948
|
+
fs.writeFileSync(outPath, formatted, 'utf8');
|
|
6949
|
+
console.warn(`[sigmap] wrote ${path.relative(cwd, outPath)}`);
|
|
6950
|
+
continue;
|
|
6951
|
+
}
|
|
6952
|
+
}
|
|
6929
6953
|
if (typeof adapterMod.write === 'function') {
|
|
6930
6954
|
adapterMod.write(content, cwd, { version: VERSION });
|
|
6931
6955
|
const outPath = adapterMod.outputPath(cwd);
|
|
@@ -7507,6 +7531,25 @@ function runGenerate(cwd, config, reportMode, reportJson = false) {
|
|
|
7507
7531
|
writeOutputs(content, config.outputs, cwd, config);
|
|
7508
7532
|
}
|
|
7509
7533
|
if (formatValue === 'cache') writeCacheOutput(content, cwd);
|
|
7534
|
+
|
|
7535
|
+
// --output <file>: write a copy of the formatted context to the custom path.
|
|
7536
|
+
if (config.customOutput) {
|
|
7537
|
+
try {
|
|
7538
|
+
const absCustom = path.resolve(cwd, config.customOutput);
|
|
7539
|
+
const SIGMAP_HEADER = [
|
|
7540
|
+
`<!-- Generated by SigMap v${VERSION} -->`,
|
|
7541
|
+
`<!-- Updated: ${new Date().toISOString()} -->`,
|
|
7542
|
+
`<!-- Regenerate: node gen-context.js --output ${config.customOutput} -->`,
|
|
7543
|
+
'',
|
|
7544
|
+
].join('\n');
|
|
7545
|
+
fs.mkdirSync(path.dirname(absCustom), { recursive: true });
|
|
7546
|
+
fs.writeFileSync(absCustom, SIGMAP_HEADER + content, 'utf8');
|
|
7547
|
+
console.warn(`[sigmap] wrote ${path.relative(cwd, absCustom)} (custom output)`);
|
|
7548
|
+
} catch (err) {
|
|
7549
|
+
console.warn(`[sigmap] --output write failed: ${err.message}`);
|
|
7550
|
+
}
|
|
7551
|
+
}
|
|
7552
|
+
|
|
7510
7553
|
result = { inputTokenTotal, finalTokens, fileCount: beforeCount, droppedCount };
|
|
7511
7554
|
}
|
|
7512
7555
|
} else {
|
|
@@ -7931,6 +7974,39 @@ function main() {
|
|
|
7931
7974
|
|
|
7932
7975
|
const config = loadConfig(cwd);
|
|
7933
7976
|
|
|
7977
|
+
// ── --output <file> — parse early so every subsequent block can use it ─────
|
|
7978
|
+
// Resolves the custom output path and merges it into config.customOutput.
|
|
7979
|
+
// Also persists the resolved relative path to gen-context.config.json so
|
|
7980
|
+
// future --query calls (without --output) find the file automatically.
|
|
7981
|
+
(function resolveOutputFlag() {
|
|
7982
|
+
const outIdx = args.indexOf('--output');
|
|
7983
|
+
if (outIdx < 0) return;
|
|
7984
|
+
const raw = (args[outIdx + 1] || '').trim();
|
|
7985
|
+
if (!raw || raw.startsWith('--')) {
|
|
7986
|
+
console.error('[sigmap] --output requires a file path');
|
|
7987
|
+
console.error(' Example: node gen-context.js --output .context/ai-context.md');
|
|
7988
|
+
process.exit(1);
|
|
7989
|
+
}
|
|
7990
|
+
const abs = path.resolve(cwd, raw);
|
|
7991
|
+
const rel = path.relative(cwd, abs);
|
|
7992
|
+
config.customOutput = rel; // consumed by runGenerate
|
|
7993
|
+
|
|
7994
|
+
// Persist to gen-context.config.json for future --query calls
|
|
7995
|
+
const cfgPath = path.join(cwd, 'gen-context.config.json');
|
|
7996
|
+
try {
|
|
7997
|
+
let savedCfg = {};
|
|
7998
|
+
if (fs.existsSync(cfgPath)) {
|
|
7999
|
+
try { savedCfg = JSON.parse(fs.readFileSync(cfgPath, 'utf8')); } catch (_) {}
|
|
8000
|
+
}
|
|
8001
|
+
if (savedCfg.customOutput !== rel) {
|
|
8002
|
+
savedCfg.customOutput = rel;
|
|
8003
|
+
fs.writeFileSync(cfgPath, JSON.stringify(savedCfg, null, 2) + '\n', 'utf8');
|
|
8004
|
+
}
|
|
8005
|
+
} catch (err) {
|
|
8006
|
+
console.warn(`[sigmap] could not persist customOutput to config: ${err.message}`);
|
|
8007
|
+
}
|
|
8008
|
+
})();
|
|
8009
|
+
|
|
7934
8010
|
// Feature 2: `--mode fast|full|both`
|
|
7935
8011
|
const modeIdx = args.indexOf('--mode');
|
|
7936
8012
|
const mode = modeIdx !== -1
|
|
@@ -8355,19 +8431,28 @@ function main() {
|
|
|
8355
8431
|
}
|
|
8356
8432
|
const { rank, buildSigIndex, formatRankTable, formatRankJSON } = requireSourceOrBundled('./src/retrieval/ranker');
|
|
8357
8433
|
|
|
8358
|
-
// Resolve
|
|
8359
|
-
//
|
|
8360
|
-
//
|
|
8434
|
+
// Resolve the context file path to query against.
|
|
8435
|
+
// Priority: --output flag > --adapter flag > buildSigIndex probe order
|
|
8436
|
+
// (customOutput from config is handled inside buildSigIndex itself)
|
|
8361
8437
|
let queryOpts;
|
|
8362
|
-
|
|
8363
|
-
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
|
|
8367
|
-
|
|
8368
|
-
|
|
8369
|
-
|
|
8370
|
-
|
|
8438
|
+
|
|
8439
|
+
// 1. --output <file> pins to an explicit path
|
|
8440
|
+
if (config.customOutput) {
|
|
8441
|
+
queryOpts = { contextPath: path.resolve(cwd, config.customOutput) };
|
|
8442
|
+
}
|
|
8443
|
+
|
|
8444
|
+
// 2. --adapter <name> pins to that adapter's output path (if --output not given)
|
|
8445
|
+
if (!queryOpts) {
|
|
8446
|
+
const adpIdx = args.indexOf('--adapter');
|
|
8447
|
+
if (adpIdx >= 0) {
|
|
8448
|
+
const adapterName = (args[adpIdx + 1] || '').trim().toLowerCase();
|
|
8449
|
+
const VALID_ADAPTERS = ['copilot', 'claude', 'cursor', 'windsurf', 'openai', 'gemini', 'codex'];
|
|
8450
|
+
if (VALID_ADAPTERS.includes(adapterName)) {
|
|
8451
|
+
try {
|
|
8452
|
+
const adapterMod = __require('./packages/adapters/' + adapterName);
|
|
8453
|
+
queryOpts = { contextPath: adapterMod.outputPath(cwd) };
|
|
8454
|
+
} catch (_) {}
|
|
8455
|
+
}
|
|
8371
8456
|
}
|
|
8372
8457
|
}
|
|
8373
8458
|
|
package/package.json
CHANGED
package/src/retrieval/ranker.js
CHANGED
|
@@ -206,26 +206,39 @@ function _parseContextFile(contextPath) {
|
|
|
206
206
|
* Returns Map<filePath, string[]> where filePath is the relative path
|
|
207
207
|
* as it appears in the ### headers of the context file.
|
|
208
208
|
*
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
* first file that produces a non-empty index is returned.
|
|
209
|
+
* Resolution priority:
|
|
210
|
+
* 1. `opts.contextPath` — explicit path from --output or --adapter flag
|
|
211
|
+
* 2. `customOutput` key in gen-context.config.json — persisted from a
|
|
212
|
+
* previous `--output <file>` generation run
|
|
213
|
+
* 3. All known adapter output paths probed in order (first non-empty wins)
|
|
215
214
|
*
|
|
216
215
|
* @param {string} cwd
|
|
217
216
|
* @param {{ contextPath?: string }} [opts]
|
|
218
217
|
* @returns {Map<string, string[]>}
|
|
219
218
|
*/
|
|
220
219
|
function buildSigIndex(cwd, opts) {
|
|
220
|
+
const fs = require('fs');
|
|
221
221
|
const path = require('path');
|
|
222
222
|
|
|
223
|
-
// Caller supplied an explicit path — use it directly.
|
|
223
|
+
// 1. Caller supplied an explicit path — use it directly.
|
|
224
224
|
if (opts && opts.contextPath) {
|
|
225
225
|
return _parseContextFile(opts.contextPath);
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
-
//
|
|
228
|
+
// 2. Check gen-context.config.json for a persisted customOutput path.
|
|
229
|
+
try {
|
|
230
|
+
const cfgPath = path.join(cwd, 'gen-context.config.json');
|
|
231
|
+
if (fs.existsSync(cfgPath)) {
|
|
232
|
+
const cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf8'));
|
|
233
|
+
if (cfg.customOutput) {
|
|
234
|
+
const customPath = path.resolve(cwd, cfg.customOutput);
|
|
235
|
+
const index = _parseContextFile(customPath);
|
|
236
|
+
if (index.size > 0) return index;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
} catch (_) {}
|
|
240
|
+
|
|
241
|
+
// 3. Probe all known adapter output paths; return first non-empty index.
|
|
229
242
|
for (const parts of ADAPTER_OUTPUT_PATHS) {
|
|
230
243
|
const contextPath = path.join(cwd, ...parts);
|
|
231
244
|
const index = _parseContextFile(contextPath);
|