sigmap 7.0.0 → 7.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/CHANGELOG.md +14 -0
- package/gen-context.js +74 -77
- package/llms-full.txt +1 -1
- package/llms.txt +1 -1
- package/package.json +2 -2
- package/packages/cli/package.json +1 -1
- package/packages/core/package.json +1 -1
- package/src/config/loader.js +6 -6
- package/src/discovery/source-root-scorer.js +2 -2
- package/src/format/llms-txt.js +2 -3
- package/src/mcp/handlers.js +3 -8
- package/src/mcp/server.js +1 -1
- package/src/nudge.js +1 -6
- package/src/session/notes.js +2 -8
- package/src/util/git.js +31 -0
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,20 @@ Format: [Semantic Versioning](https://semver.org/)
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
+
## [7.0.1] — 2026-06-14
|
|
14
|
+
|
|
15
|
+
Patch release — supply-chain hardening and package hygiene, plus a wider star nudge.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- **Eliminated system-shell access (#252):** every `child_process.execSync` call (which runs via `/bin/sh -c`) was converted to shell-free `execFileSync` with an arguments array. Several commands had previously interpolated values into the command string (`git diff ${range}`, `HEAD~${n}`, `printf '%s' … | ${clipCmd}`, `node -e "…http.get…"`) — a real shell-injection surface. New `src/util/git.js` (`git()`/`tryGit()`) centralizes shell-free git; the `extends` config fetch passes the URL as an argv to node; `compare` spawns node by argv; clipboard copy writes via stdin. Net: zero `execSync`/`exec`/`shell:true` in the published surface, which clears Socket's "Shell access" capability alert.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- **Star nudge now counts plain `sigmap` runs (#251):** the one-time GitHub-star nudge previously only counted `ask`/`squeeze`. Users who only run `sigmap` to generate the context file now also reach the 10-run threshold. Counted once per process at the end of generation (monorepo-safe, tracked at the repo root); interactive-only — silent under `--json`/`--report`/`--quiet`/non-TTY.
|
|
22
|
+
- **`main` points at the importable core (#252):** `package.json` `main` changed from the CLI bundle (`gen-context.js`, which runs `main()` + exits on `require`) to `packages/core/index.js`, matching `exports["."]`. Bundlephobia and legacy resolvers now see the real zero-dep API.
|
|
23
|
+
- **Removed unused device fingerprint (#252):** the star nudge no longer records `machineId = sha256(os.hostname())` in `.context/usage.json` — it was never read or transmitted. Dropped the now-unused `os`/`crypto` requires.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
13
27
|
## [7.0.0] — 2026-06-14
|
|
14
28
|
|
|
15
29
|
Major release — **Squeeze** makes `ask` minimize pasted input by default, a behavioral change to the core command.
|
package/gen-context.js
CHANGED
|
@@ -236,12 +236,11 @@ __factories["./src/config/loader"] = function(module, exports) {
|
|
|
236
236
|
}
|
|
237
237
|
}
|
|
238
238
|
try {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
);
|
|
239
|
+
// Shell-free: run the node binary directly with the URL passed as an argv
|
|
240
|
+
// value (never interpolated into a command string, never via /bin/sh).
|
|
241
|
+
const { execFileSync } = require('child_process');
|
|
242
|
+
const SYNC_FETCH = "const u=process.argv[1];const h=require(u.startsWith('https')?'https':'http');let d='';h.get(u,r=>{r.on('data',c=>d+=c);r.on('end',()=>process.stdout.write(d))}).on('error',()=>process.exit(1))";
|
|
243
|
+
const out = execFileSync(process.execPath, ['-e', SYNC_FETCH, extendsVal], { timeout: 10000, encoding: 'utf8' });
|
|
245
244
|
const parsed = JSON.parse(out);
|
|
246
245
|
if (!fs.existsSync(cacheDir)) fs.mkdirSync(cacheDir, { recursive: true });
|
|
247
246
|
fs.writeFileSync(cachePath, JSON.stringify(parsed), 'utf8');
|
|
@@ -5533,7 +5532,6 @@ __factories["./src/mcp/handlers"] = function(module, exports) {
|
|
|
5533
5532
|
|
|
5534
5533
|
const fs = require('fs');
|
|
5535
5534
|
const path = require('path');
|
|
5536
|
-
const { execSync } = require('child_process');
|
|
5537
5535
|
|
|
5538
5536
|
const CONTEXT_FILE = path.join('.github', 'copilot-instructions.md');
|
|
5539
5537
|
const CONTEXT_COLD_FILE = path.join('.github', 'context-cold.md');
|
|
@@ -5680,19 +5678,14 @@ function createCheckpoint(args, cwd) {
|
|
|
5680
5678
|
// ── Git info ────────────────────────────────────────────────────────────
|
|
5681
5679
|
lines.push('## Git state');
|
|
5682
5680
|
try {
|
|
5683
|
-
const branch =
|
|
5684
|
-
cwd, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
|
|
5685
|
-
}).trim();
|
|
5681
|
+
const branch = __git(['rev-parse', '--abbrev-ref', 'HEAD'], { cwd }).trim();
|
|
5686
5682
|
lines.push(`**Branch:** ${branch}`);
|
|
5687
5683
|
} catch (_) {
|
|
5688
5684
|
lines.push('**Branch:** (not a git repo)');
|
|
5689
5685
|
}
|
|
5690
5686
|
|
|
5691
5687
|
try {
|
|
5692
|
-
const log =
|
|
5693
|
-
'git log --oneline -5 --no-decorate 2>/dev/null',
|
|
5694
|
-
{ cwd, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }
|
|
5695
|
-
).trim();
|
|
5688
|
+
const log = __git(['log', '--oneline', '-5', '--no-decorate'], { cwd }).trim();
|
|
5696
5689
|
if (log) {
|
|
5697
5690
|
lines.push('');
|
|
5698
5691
|
lines.push('**Recent commits:**');
|
|
@@ -6261,7 +6254,7 @@ const { readContext, searchSignatures, getMap, createCheckpoint, getRouting, exp
|
|
|
6261
6254
|
|
|
6262
6255
|
const SERVER_INFO = {
|
|
6263
6256
|
name: 'sigmap',
|
|
6264
|
-
version: '7.0.
|
|
6257
|
+
version: '7.0.1',
|
|
6265
6258
|
description: 'SigMap MCP server — code signatures on demand',
|
|
6266
6259
|
};
|
|
6267
6260
|
|
|
@@ -7152,8 +7145,6 @@ __factories["./src/session/notes"] = function(module, exports) {
|
|
|
7152
7145
|
|
|
7153
7146
|
const fs = require('fs');
|
|
7154
7147
|
const path = require('path');
|
|
7155
|
-
const { execSync } = require('child_process');
|
|
7156
|
-
|
|
7157
7148
|
const NOTES_FILE = path.join('.context', 'notes.ndjson');
|
|
7158
7149
|
const MAX_TEXT = 2000;
|
|
7159
7150
|
|
|
@@ -7162,13 +7153,7 @@ function notesPath(cwd) {
|
|
|
7162
7153
|
}
|
|
7163
7154
|
|
|
7164
7155
|
function _currentBranch(cwd) {
|
|
7165
|
-
|
|
7166
|
-
return execSync('git rev-parse --abbrev-ref HEAD', {
|
|
7167
|
-
cwd, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
|
|
7168
|
-
}).trim() || null;
|
|
7169
|
-
} catch (_) {
|
|
7170
|
-
return null;
|
|
7171
|
-
}
|
|
7156
|
+
return __tryGit(['rev-parse', '--abbrev-ref', 'HEAD'], { cwd }) || null;
|
|
7172
7157
|
}
|
|
7173
7158
|
|
|
7174
7159
|
/**
|
|
@@ -8703,10 +8688,9 @@ __factories["./src/format/llms-txt"] = function(module, exports) {
|
|
|
8703
8688
|
'use strict';
|
|
8704
8689
|
const path = require('path');
|
|
8705
8690
|
const fs = require('fs');
|
|
8706
|
-
const { execSync } = require('child_process');
|
|
8707
8691
|
function outputPath(cwd) { return path.join(cwd, 'llms.txt'); }
|
|
8708
8692
|
function getShortCommit(cwd) {
|
|
8709
|
-
try { return
|
|
8693
|
+
try { return __tryGit(['rev-parse', '--short', 'HEAD'], { cwd, timeout: 2000 }); }
|
|
8710
8694
|
catch (_) { return ''; }
|
|
8711
8695
|
}
|
|
8712
8696
|
function detectVersion(cwd) {
|
|
@@ -9031,14 +9015,13 @@ __factories["./src/discovery/source-root-scorer"] = function(module, exports) {
|
|
|
9031
9015
|
'use strict';
|
|
9032
9016
|
const fs = require('fs');
|
|
9033
9017
|
const path = require('path');
|
|
9034
|
-
const { execSync } = require('child_process');
|
|
9035
9018
|
const CODE_EXTS = new Set(['.js','.mjs','.cjs','.ts','.tsx','.jsx','.py','.rb','.go','.rs','.java','.kt','.cs','.cpp','.c','.h','.swift','.dart','.scala','.php']);
|
|
9036
9019
|
const AUTO_SKIP = new Set(['node_modules','dist','build','.git','.next','.nuxt','vendor','DerivedData','Pods','target','coverage','__pycache__','.venv','venv','.build','Carthage','storybook-static','.gradle','bin','obj','.vs']);
|
|
9037
9020
|
const PENALTY_DIRS = new Set(['test','tests','spec','__tests__','e2e','docs','doc','docs-vp','examples','example','fixtures','mocks','__mocks__','demo','samples','migrations','benchmarks','scripts']);
|
|
9038
9021
|
const ROOT_ENTRYPOINTS = { go: ['main.go'], python: ['app.py','main.py','wsgi.py','asgi.py'], javascript: ['index.js','server.js','app.js'], typescript: ['index.ts','main.ts'], rust: [], php: ['index.php'] };
|
|
9039
9022
|
function getRecentlyChangedDirs(cwd) {
|
|
9040
9023
|
try {
|
|
9041
|
-
const out =
|
|
9024
|
+
const out = __git(['log', '--name-only', '--format=', 'HEAD~10'], { cwd, timeout: 3000 }).toString();
|
|
9042
9025
|
return new Set(out.split('\n').filter(Boolean).map(f => f.split('/')[0]));
|
|
9043
9026
|
} catch { return new Set(); }
|
|
9044
9027
|
}
|
|
@@ -10027,8 +10010,6 @@ __factories["./src/nudge"] = function(module, exports) {
|
|
|
10027
10010
|
|
|
10028
10011
|
const fs = require('fs');
|
|
10029
10012
|
const path = require('path');
|
|
10030
|
-
const os = require('os');
|
|
10031
|
-
const crypto = require('crypto');
|
|
10032
10013
|
|
|
10033
10014
|
const RUN_THRESHOLD = 10;
|
|
10034
10015
|
const SUCCESS_THRESHOLD = 8;
|
|
@@ -10038,7 +10019,7 @@ function usagePath(cwd) { return path.join(cwd, '.context', 'usage.json'); }
|
|
|
10038
10019
|
function defaultUsage() {
|
|
10039
10020
|
return {
|
|
10040
10021
|
totalRuns: 0, successfulRuns: 0, squeezeOffered: 0, squeezeAccepted: 0,
|
|
10041
|
-
starNudgeShown: false,
|
|
10022
|
+
starNudgeShown: false, firstRunDate: null, lastRunDate: null,
|
|
10042
10023
|
};
|
|
10043
10024
|
}
|
|
10044
10025
|
|
|
@@ -10091,9 +10072,6 @@ function checkStarNudge(cwd, runSuccess, opts = {}) {
|
|
|
10091
10072
|
const today = opts.today || new Date().toISOString().slice(0, 10);
|
|
10092
10073
|
if (!usage.firstRunDate) usage.firstRunDate = today;
|
|
10093
10074
|
usage.lastRunDate = today;
|
|
10094
|
-
if (!usage.machineId) {
|
|
10095
|
-
try { usage.machineId = 'sha256-' + crypto.createHash('sha256').update(os.hostname()).digest('hex').slice(0, 16); } catch (_) {}
|
|
10096
|
-
}
|
|
10097
10075
|
|
|
10098
10076
|
let nudged = false;
|
|
10099
10077
|
if (!usage.starNudgeShown && usage.totalRuns >= RUN_THRESHOLD && usage.successfulRuns >= SUCCESS_THRESHOLD) {
|
|
@@ -10956,9 +10934,20 @@ module.exports = { verify, buildSymbolSet, loadDeps, loadScripts, isTestPath };
|
|
|
10956
10934
|
const fs = require('fs');
|
|
10957
10935
|
const path = require('path');
|
|
10958
10936
|
const os = require('os');
|
|
10959
|
-
const { execSync } = require('child_process');
|
|
10960
10937
|
|
|
10961
|
-
|
|
10938
|
+
// Shell-free subprocess helpers. execFileSync runs the binary directly (never
|
|
10939
|
+
// /bin/sh), so there is no shell-injection surface and no "Shell access"
|
|
10940
|
+
// capability for supply-chain scanners. stderr is discarded by default.
|
|
10941
|
+
function __git(args, opts = {}) {
|
|
10942
|
+
const { execFileSync } = require('child_process');
|
|
10943
|
+
return execFileSync('git', args, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], ...opts });
|
|
10944
|
+
}
|
|
10945
|
+
function __tryGit(args, opts = {}) {
|
|
10946
|
+
try { return __git(args, opts).toString().trim(); }
|
|
10947
|
+
catch (_) { return ''; }
|
|
10948
|
+
}
|
|
10949
|
+
|
|
10950
|
+
const VERSION = '7.0.1';
|
|
10962
10951
|
const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
|
|
10963
10952
|
|
|
10964
10953
|
function requireSourceOrBundled(key) {
|
|
@@ -11340,9 +11329,7 @@ function applyTokenBudget(fileEntries, maxTokens) {
|
|
|
11340
11329
|
function getRecentlyCommittedFiles(cwd, count) {
|
|
11341
11330
|
const n = count || 10;
|
|
11342
11331
|
try {
|
|
11343
|
-
const out =
|
|
11344
|
-
cwd, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
|
|
11345
|
-
});
|
|
11332
|
+
const out = __git(['log', '--name-only', '--format=', '-n', String(n)], { cwd });
|
|
11346
11333
|
return new Set(out.split('\n').map((f) => f.trim()).filter(Boolean).map((f) => path.resolve(cwd, f)));
|
|
11347
11334
|
} catch (_) {
|
|
11348
11335
|
return new Set();
|
|
@@ -11354,8 +11341,8 @@ function getRecentlyCommittedFiles(cwd, count) {
|
|
|
11354
11341
|
// ---------------------------------------------------------------------------
|
|
11355
11342
|
function getDiffFiles(cwd, stagedOnly) {
|
|
11356
11343
|
try {
|
|
11357
|
-
const
|
|
11358
|
-
const out =
|
|
11344
|
+
const gitArgs = stagedOnly ? ['diff', '--cached', '--name-only'] : ['diff', 'HEAD', '--name-only'];
|
|
11345
|
+
const out = __git(gitArgs, { cwd });
|
|
11359
11346
|
return new Set(
|
|
11360
11347
|
out.split('\n').map((f) => f.trim()).filter(Boolean).map((f) => path.resolve(cwd, f))
|
|
11361
11348
|
);
|
|
@@ -11453,17 +11440,13 @@ function buildChangesSection(cwd, config, fileEntries) {
|
|
|
11453
11440
|
try {
|
|
11454
11441
|
let range = `HEAD~${n}..HEAD`;
|
|
11455
11442
|
try {
|
|
11456
|
-
|
|
11457
|
-
cwd, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
|
|
11458
|
-
});
|
|
11443
|
+
__git(['rev-parse', '--verify', `HEAD~${n}`], { cwd });
|
|
11459
11444
|
} catch (_) {
|
|
11460
11445
|
// HEAD~n doesn't exist (shallow repo) — clamp to deepest valid ancestor
|
|
11461
11446
|
let best = 1;
|
|
11462
11447
|
for (let k = n - 1; k >= 2; k--) {
|
|
11463
11448
|
try {
|
|
11464
|
-
|
|
11465
|
-
cwd, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
|
|
11466
|
-
});
|
|
11449
|
+
__git(['rev-parse', '--verify', `HEAD~${k}`], { cwd });
|
|
11467
11450
|
best = k;
|
|
11468
11451
|
break;
|
|
11469
11452
|
} catch (_) {}
|
|
@@ -11471,15 +11454,11 @@ function buildChangesSection(cwd, config, fileEntries) {
|
|
|
11471
11454
|
range = `HEAD~${best}..HEAD`;
|
|
11472
11455
|
}
|
|
11473
11456
|
|
|
11474
|
-
const namesOnly =
|
|
11475
|
-
cwd, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
|
|
11476
|
-
});
|
|
11457
|
+
const namesOnly = __git(['diff', range, '--name-only'], { cwd });
|
|
11477
11458
|
const changed = new Set(namesOnly.split('\n').map((l) => l.trim()).filter(Boolean).map((f) => path.resolve(cwd, f)));
|
|
11478
11459
|
if (changed.size === 0) return [];
|
|
11479
11460
|
|
|
11480
|
-
const timeAgo =
|
|
11481
|
-
cwd, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
|
|
11482
|
-
}).trim();
|
|
11461
|
+
const timeAgo = __git(['log', '-1', '--format=%cr'], { cwd }).trim();
|
|
11483
11462
|
|
|
11484
11463
|
const lines = [`## changes (last ${n} commits — ${timeAgo})`, '```'];
|
|
11485
11464
|
let hasDelta = false;
|
|
@@ -11488,9 +11467,7 @@ function buildChangesSection(cwd, config, fileEntries) {
|
|
|
11488
11467
|
const rel = path.relative(cwd, entry.filePath);
|
|
11489
11468
|
let diff = '';
|
|
11490
11469
|
try {
|
|
11491
|
-
diff =
|
|
11492
|
-
cwd, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
|
|
11493
|
-
});
|
|
11470
|
+
diff = __git(['diff', range, '--', rel], { cwd });
|
|
11494
11471
|
} catch (_) {
|
|
11495
11472
|
continue;
|
|
11496
11473
|
}
|
|
@@ -12470,6 +12447,23 @@ function runGenerate(cwd, config, reportMode, reportJson = false) {
|
|
|
12470
12447
|
}
|
|
12471
12448
|
}
|
|
12472
12449
|
|
|
12450
|
+
// v7.0.1: count a plain context-generation run for the one-time star nudge.
|
|
12451
|
+
// Most users only ever run `sigmap` to build the context file — count that too,
|
|
12452
|
+
// not just ask/squeeze. Guard so a monorepo (many runGenerate calls per process)
|
|
12453
|
+
// counts as a single run. Interactive only — silent under --json/--report or non-TTY.
|
|
12454
|
+
if (!global.__sigmapGenCounted) {
|
|
12455
|
+
global.__sigmapGenCounted = true;
|
|
12456
|
+
try {
|
|
12457
|
+
const { checkStarNudge } = requireSourceOrBundled('./src/nudge');
|
|
12458
|
+
const showNudge = !!process.stderr.isTTY
|
|
12459
|
+
&& !reportJson
|
|
12460
|
+
&& !process.argv.includes('--json')
|
|
12461
|
+
&& !process.argv.includes('--report')
|
|
12462
|
+
&& !process.argv.includes('--quiet');
|
|
12463
|
+
checkStarNudge(global.__sigmapRootCwd || cwd, true, { silent: !showNudge });
|
|
12464
|
+
} catch (_) {}
|
|
12465
|
+
}
|
|
12466
|
+
|
|
12473
12467
|
return result;
|
|
12474
12468
|
}
|
|
12475
12469
|
|
|
@@ -12651,11 +12645,7 @@ function suggestTool(description) {
|
|
|
12651
12645
|
|
|
12652
12646
|
function resolveProjectRoot(startDir) {
|
|
12653
12647
|
try {
|
|
12654
|
-
const gitRoot =
|
|
12655
|
-
cwd: startDir,
|
|
12656
|
-
encoding: 'utf8',
|
|
12657
|
-
stdio: ['ignore', 'pipe', 'ignore'],
|
|
12658
|
-
}).trim();
|
|
12648
|
+
const gitRoot = __git(['rev-parse', '--show-toplevel'], { cwd: startDir }).trim();
|
|
12659
12649
|
if (gitRoot) return gitRoot;
|
|
12660
12650
|
} catch (_) {}
|
|
12661
12651
|
return startDir;
|
|
@@ -12926,8 +12916,7 @@ function buildIndexContext(ranked, cwd) {
|
|
|
12926
12916
|
|
|
12927
12917
|
function computeCurrentRisk(cwd) {
|
|
12928
12918
|
try {
|
|
12929
|
-
const
|
|
12930
|
-
const out = execSync('git diff --name-only HEAD', { cwd, timeout: 3000, encoding: 'utf8' });
|
|
12919
|
+
const out = __git(['diff', '--name-only', 'HEAD'], { cwd, timeout: 3000 });
|
|
12931
12920
|
const count = out.trim().split('\n').filter(Boolean).length;
|
|
12932
12921
|
if (count === 0) return 'NONE';
|
|
12933
12922
|
if (count <= 3) return 'LOW';
|
|
@@ -13011,6 +13000,9 @@ function main() {
|
|
|
13011
13000
|
const cwd = cwdFlag
|
|
13012
13001
|
? path.resolve(invokedFrom, cwdFlag)
|
|
13013
13002
|
: resolveProjectRoot(invokedFrom);
|
|
13003
|
+
// v7.0.1: remember the invocation root so the generation star-nudge always tracks
|
|
13004
|
+
// usage at the repo root, even when runGenerate is called per-package (monorepo).
|
|
13005
|
+
global.__sigmapRootCwd = cwd;
|
|
13014
13006
|
const scriptPath = process.argv[1] || path.join(invokedFrom, 'gen-context.js');
|
|
13015
13007
|
|
|
13016
13008
|
if (cwdFlag) {
|
|
@@ -13286,9 +13278,8 @@ function main() {
|
|
|
13286
13278
|
const short = args.includes('--short');
|
|
13287
13279
|
let msg = '', diff = '';
|
|
13288
13280
|
try {
|
|
13289
|
-
|
|
13290
|
-
|
|
13291
|
-
diff = execSync('git diff --cached --name-only', { cwd, timeout: 3000, encoding: 'utf8' });
|
|
13281
|
+
msg = __git(['log', '-1', '--format=%s'], { cwd, timeout: 3000 }).trim();
|
|
13282
|
+
diff = __git(['diff', '--cached', '--name-only'], { cwd, timeout: 3000 });
|
|
13292
13283
|
} catch (_) {}
|
|
13293
13284
|
|
|
13294
13285
|
let profile = 'default';
|
|
@@ -13309,13 +13300,15 @@ function main() {
|
|
|
13309
13300
|
|
|
13310
13301
|
// v4.2: `sigmap compare` — human-readable benchmark CLI
|
|
13311
13302
|
if (args[0] === 'compare') {
|
|
13312
|
-
const {
|
|
13303
|
+
const { execFileSync } = require('child_process');
|
|
13313
13304
|
console.log('[sigmap] Running comparison benchmark (this may take ~30s)...\n');
|
|
13314
13305
|
|
|
13315
13306
|
let raw = '';
|
|
13316
13307
|
try {
|
|
13317
|
-
|
|
13318
|
-
|
|
13308
|
+
// Shell-free: run the node binary directly with the script path as an argv.
|
|
13309
|
+
raw = execFileSync(
|
|
13310
|
+
process.execPath,
|
|
13311
|
+
[path.join(__dirname, 'scripts', 'run-retrieval-benchmark.mjs'), '--compare'],
|
|
13319
13312
|
{ cwd, timeout: 90_000, encoding: 'utf8' }
|
|
13320
13313
|
);
|
|
13321
13314
|
} catch (e) { raw = (e && e.stdout) ? e.stdout : ''; }
|
|
@@ -13372,9 +13365,13 @@ function main() {
|
|
|
13372
13365
|
console.log(shareText);
|
|
13373
13366
|
|
|
13374
13367
|
try {
|
|
13375
|
-
|
|
13376
|
-
|
|
13377
|
-
|
|
13368
|
+
// Shell-free: spawn the clipboard binary directly and write the text to its
|
|
13369
|
+
// stdin — no shell, no pipe, no string-built command.
|
|
13370
|
+
const { execFileSync } = require('child_process');
|
|
13371
|
+
const [clipBin, clipArgs] = process.platform === 'darwin'
|
|
13372
|
+
? ['pbcopy', []]
|
|
13373
|
+
: ['xclip', ['-selection', 'clipboard']];
|
|
13374
|
+
execFileSync(clipBin, clipArgs, { input: shareText, timeout: 2000 });
|
|
13378
13375
|
console.log('\n[sigmap] Copied to clipboard.');
|
|
13379
13376
|
} catch (_) {}
|
|
13380
13377
|
|
|
@@ -13955,16 +13952,16 @@ function main() {
|
|
|
13955
13952
|
// `sigmap status` — environment / repo state at a glance.
|
|
13956
13953
|
if (args[0] === 'status') {
|
|
13957
13954
|
const jsonOut = args.includes('--json');
|
|
13958
|
-
const gitOpts = { cwd
|
|
13955
|
+
const gitOpts = { cwd };
|
|
13959
13956
|
const st = { branch: null, dirty: 0, lastIndex: null, indexVersion: null, indexFiles: null, changedSinceIndex: null, notes: 0, lastNote: null };
|
|
13960
13957
|
|
|
13961
|
-
|
|
13958
|
+
st.branch = __tryGit(['rev-parse', '--abbrev-ref', 'HEAD'], gitOpts) || null;
|
|
13962
13959
|
// Fallback for an unborn branch (fresh repo, no commits yet).
|
|
13963
13960
|
if (!st.branch || st.branch === 'HEAD') {
|
|
13964
|
-
|
|
13961
|
+
st.branch = __tryGit(['symbolic-ref', '--short', 'HEAD'], gitOpts) || st.branch;
|
|
13965
13962
|
}
|
|
13966
13963
|
try {
|
|
13967
|
-
const porcelain =
|
|
13964
|
+
const porcelain = __git(['status', '--porcelain'], gitOpts).trim();
|
|
13968
13965
|
st.dirty = porcelain ? porcelain.split('\n').filter(Boolean).length : 0;
|
|
13969
13966
|
} catch (_) {}
|
|
13970
13967
|
|
|
@@ -13983,7 +13980,7 @@ function main() {
|
|
|
13983
13980
|
if (st.lastIndex) {
|
|
13984
13981
|
try {
|
|
13985
13982
|
const since = Date.parse(st.lastIndex);
|
|
13986
|
-
const tracked =
|
|
13983
|
+
const tracked = __git(['ls-files'], gitOpts).split('\n').filter(Boolean);
|
|
13987
13984
|
let changed = 0;
|
|
13988
13985
|
for (const f of tracked.slice(0, 5000)) {
|
|
13989
13986
|
try { if (fs.statSync(path.join(cwd, f)).mtimeMs > since) changed++; } catch (_) {}
|
package/llms-full.txt
CHANGED
|
@@ -9,7 +9,7 @@ the files relevant to the task — cutting tokens ~97% while keeping answers
|
|
|
9
9
|
grounded. Deterministic, offline, no embeddings or vector database. Works with
|
|
10
10
|
Claude, Cursor, GitHub Copilot, Aider, Windsurf, local LLMs, and MCP.
|
|
11
11
|
|
|
12
|
-
# Version: 7.0.
|
|
12
|
+
# Version: 7.0.1 | Benchmark: sigmap-v7.0-main (2026-06-14)
|
|
13
13
|
# Source: auto-generated from package.json, version.json, src/mcp/tools.js, src/config/defaults.js
|
|
14
14
|
# Regenerate: npm run generate:llms | Validate: npm run validate:llms
|
|
15
15
|
|
package/llms.txt
CHANGED
|
@@ -9,7 +9,7 @@ the files relevant to the task — cutting tokens ~97% while keeping answers
|
|
|
9
9
|
grounded. Deterministic, offline, no embeddings or vector database. Works with
|
|
10
10
|
Claude, Cursor, GitHub Copilot, Aider, Windsurf, local LLMs, and MCP.
|
|
11
11
|
|
|
12
|
-
# Version: 7.0.
|
|
12
|
+
# Version: 7.0.1 | Benchmark: sigmap-v7.0-main (2026-06-14)
|
|
13
13
|
# Source: auto-generated from package.json, version.json, src/mcp/tools.js, src/config/defaults.js
|
|
14
14
|
# Regenerate: npm run generate:llms | Validate: npm run validate:llms
|
|
15
15
|
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sigmap",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.1",
|
|
4
4
|
"description": "97% token reduction for AI coding. Extracts function & class signatures with TF-IDF ranking to feed only the right files to Claude, Cursor, Copilot, Aider, Windsurf, local LLMs & MCP. Zero dependencies, runs offline via npx.",
|
|
5
|
-
"main": "
|
|
5
|
+
"main": "packages/core/index.js",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": "./packages/core/index.js",
|
|
8
8
|
"./cli": "./packages/cli/index.js",
|
package/src/config/loader.js
CHANGED
|
@@ -34,12 +34,12 @@ function loadBaseConfig(extendsVal, cwd) {
|
|
|
34
34
|
}).on('error', reject);
|
|
35
35
|
});
|
|
36
36
|
})();
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
);
|
|
37
|
+
// Sync fallback: run a tiny fetch in a child node process. Shell-free —
|
|
38
|
+
// execFileSync runs the node binary directly (no /bin/sh) and the URL is
|
|
39
|
+
// passed as an argv value, never interpolated into a command string.
|
|
40
|
+
const { execFileSync } = require('child_process');
|
|
41
|
+
const SYNC_FETCH = "const u=process.argv[1];const h=require(u.startsWith('https')?'https':'http');let d='';h.get(u,r=>{r.on('data',c=>d+=c);r.on('end',()=>process.stdout.write(d))}).on('error',()=>process.exit(1))";
|
|
42
|
+
const out = execFileSync(process.execPath, ['-e', SYNC_FETCH, extendsVal], { timeout: 10000, encoding: 'utf8' });
|
|
43
43
|
const parsed = JSON.parse(out);
|
|
44
44
|
if (!fs.existsSync(cacheDir)) fs.mkdirSync(cacheDir, { recursive: true });
|
|
45
45
|
fs.writeFileSync(cachePath, JSON.stringify(parsed), 'utf8');
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
|
-
const {
|
|
5
|
+
const { git } = require('../util/git');
|
|
6
6
|
|
|
7
7
|
const CODE_EXTS = new Set([
|
|
8
8
|
'.js','.mjs','.cjs','.ts','.tsx','.jsx',
|
|
@@ -35,7 +35,7 @@ const ROOT_ENTRYPOINTS = {
|
|
|
35
35
|
|
|
36
36
|
function getRecentlyChangedDirs(cwd) {
|
|
37
37
|
try {
|
|
38
|
-
const out =
|
|
38
|
+
const out = git(['log', '--name-only', '--format=', 'HEAD~10'], { cwd, timeout: 3000 }).toString();
|
|
39
39
|
return new Set(out.split('\n').filter(Boolean).map(f => f.split('/')[0]));
|
|
40
40
|
} catch { return new Set(); }
|
|
41
41
|
}
|
package/src/format/llms-txt.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const fs = require('fs');
|
|
4
|
-
const {
|
|
4
|
+
const { tryGit } = require('../util/git');
|
|
5
5
|
module.exports = { format, outputPath };
|
|
6
6
|
|
|
7
7
|
function outputPath(cwd) { return path.join(cwd, 'llms.txt'); }
|
|
8
8
|
|
|
9
9
|
function getShortCommit(cwd) {
|
|
10
|
-
|
|
11
|
-
catch (_) { return ''; }
|
|
10
|
+
return tryGit(['rev-parse', '--short', 'HEAD'], { cwd, timeout: 2000 });
|
|
12
11
|
}
|
|
13
12
|
|
|
14
13
|
function detectVersion(cwd) {
|
package/src/mcp/handlers.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
|
-
const {
|
|
5
|
+
const { git } = require('../util/git');
|
|
6
6
|
|
|
7
7
|
const CONTEXT_FILE = path.join('.github', 'copilot-instructions.md');
|
|
8
8
|
const CONTEXT_COLD_FILE = path.join('.github', 'context-cold.md');
|
|
@@ -149,19 +149,14 @@ function createCheckpoint(args, cwd) {
|
|
|
149
149
|
// ── Git info ────────────────────────────────────────────────────────────
|
|
150
150
|
lines.push('## Git state');
|
|
151
151
|
try {
|
|
152
|
-
const branch =
|
|
153
|
-
cwd, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
|
|
154
|
-
}).trim();
|
|
152
|
+
const branch = git(['rev-parse', '--abbrev-ref', 'HEAD'], { cwd }).trim();
|
|
155
153
|
lines.push(`**Branch:** ${branch}`);
|
|
156
154
|
} catch (_) {
|
|
157
155
|
lines.push('**Branch:** (not a git repo)');
|
|
158
156
|
}
|
|
159
157
|
|
|
160
158
|
try {
|
|
161
|
-
const log =
|
|
162
|
-
'git log --oneline -5 --no-decorate 2>/dev/null',
|
|
163
|
-
{ cwd, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }
|
|
164
|
-
).trim();
|
|
159
|
+
const log = git(['log', '--oneline', '-5', '--no-decorate'], { cwd }).trim();
|
|
165
160
|
if (log) {
|
|
166
161
|
lines.push('');
|
|
167
162
|
lines.push('**Recent commits:**');
|
package/src/mcp/server.js
CHANGED
package/src/nudge.js
CHANGED
|
@@ -11,8 +11,6 @@
|
|
|
11
11
|
|
|
12
12
|
const fs = require('fs');
|
|
13
13
|
const path = require('path');
|
|
14
|
-
const os = require('os');
|
|
15
|
-
const crypto = require('crypto');
|
|
16
14
|
|
|
17
15
|
const RUN_THRESHOLD = 10;
|
|
18
16
|
const SUCCESS_THRESHOLD = 8;
|
|
@@ -22,7 +20,7 @@ function usagePath(cwd) { return path.join(cwd, '.context', 'usage.json'); }
|
|
|
22
20
|
function defaultUsage() {
|
|
23
21
|
return {
|
|
24
22
|
totalRuns: 0, successfulRuns: 0, squeezeOffered: 0, squeezeAccepted: 0,
|
|
25
|
-
starNudgeShown: false,
|
|
23
|
+
starNudgeShown: false, firstRunDate: null, lastRunDate: null,
|
|
26
24
|
};
|
|
27
25
|
}
|
|
28
26
|
|
|
@@ -75,9 +73,6 @@ function checkStarNudge(cwd, runSuccess, opts = {}) {
|
|
|
75
73
|
const today = opts.today || new Date().toISOString().slice(0, 10);
|
|
76
74
|
if (!usage.firstRunDate) usage.firstRunDate = today;
|
|
77
75
|
usage.lastRunDate = today;
|
|
78
|
-
if (!usage.machineId) {
|
|
79
|
-
try { usage.machineId = 'sha256-' + crypto.createHash('sha256').update(os.hostname()).digest('hex').slice(0, 16); } catch (_) {}
|
|
80
|
-
}
|
|
81
76
|
|
|
82
77
|
let nudged = false;
|
|
83
78
|
if (!usage.starNudgeShown && usage.totalRuns >= RUN_THRESHOLD && usage.successfulRuns >= SUCCESS_THRESHOLD) {
|
package/src/session/notes.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
const fs = require('fs');
|
|
17
17
|
const path = require('path');
|
|
18
|
-
const {
|
|
18
|
+
const { tryGit } = require('../util/git');
|
|
19
19
|
|
|
20
20
|
const NOTES_FILE = path.join('.context', 'notes.ndjson');
|
|
21
21
|
const MAX_TEXT = 2000;
|
|
@@ -25,13 +25,7 @@ function notesPath(cwd) {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
function _currentBranch(cwd) {
|
|
28
|
-
|
|
29
|
-
return execSync('git rev-parse --abbrev-ref HEAD', {
|
|
30
|
-
cwd, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
|
|
31
|
-
}).trim() || null;
|
|
32
|
-
} catch (_) {
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
28
|
+
return tryGit(['rev-parse', '--abbrev-ref', 'HEAD'], { cwd }) || null;
|
|
35
29
|
}
|
|
36
30
|
|
|
37
31
|
/**
|
package/src/util/git.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shell-free git invocation.
|
|
5
|
+
*
|
|
6
|
+
* Uses `execFileSync('git', [...])`, which executes the git binary directly —
|
|
7
|
+
* it never spawns a system shell (`/bin/sh -c`). That means:
|
|
8
|
+
* - no shell-injection surface (arguments are passed as an array, never
|
|
9
|
+
* interpolated into a command string), and
|
|
10
|
+
* - supply-chain scanners (e.g. Socket) do not flag a "Shell access" capability.
|
|
11
|
+
*
|
|
12
|
+
* stderr is discarded by default (replaces the old `2>/dev/null` redirects).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const { execFileSync } = require('child_process');
|
|
16
|
+
|
|
17
|
+
function git(args, opts = {}) {
|
|
18
|
+
return execFileSync('git', args, {
|
|
19
|
+
encoding: 'utf8',
|
|
20
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
21
|
+
...opts,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Convenience: run git and return trimmed stdout, or '' on any failure.
|
|
26
|
+
function tryGit(args, opts = {}) {
|
|
27
|
+
try { return git(args, opts).toString().trim(); }
|
|
28
|
+
catch (_) { return ''; }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = { git, tryGit };
|