sigmap 3.2.0 → 3.3.0
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 +63 -2
- package/README.md +87 -1
- package/gen-context.js +144 -51
- package/gen-project-map.js +17 -4
- package/package.json +3 -2
- package/packages/cli/package.json +1 -1
- package/packages/core/package.json +1 -1
- package/src/config/defaults.js +7 -1
- package/src/extractors/coverage.js +27 -5
- package/src/extractors/css.js +24 -6
- package/src/mcp/server.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -10,7 +10,68 @@ Format: [Semantic Versioning](https://semver.org/)
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
## [3.
|
|
13
|
+
## [3.3.0] — 2026-04-08 — Context-Aware CLI & Command Switcher
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- **Context-aware `--help` output** — `gen-context.js` and `gen-project-map.js` now detect how they were invoked and show the matching command in every usage example:
|
|
17
|
+
- `npx sigmap --help` shows `npx sigmap <flag>`
|
|
18
|
+
- `sigmap --help` shows `sigmap <flag>`
|
|
19
|
+
- `gen-context --help` shows `gen-context <flag>`
|
|
20
|
+
- `node gen-context.js --help` shows `node gen-context.js <flag>` (unchanged for local users)
|
|
21
|
+
- Detection uses `process.argv[1]` path analysis (npx cache path, basename without `.js`, fallback)
|
|
22
|
+
- **`docs/cli.html` command picker** — four-tab switcher ("How you run it:") above the flags reference terminal updates every code block on the page (all `.tw` spans and `.term-title` bars) to the selected invocation style. Applies equally to `gen-project-map` references. Selection is saved in `localStorage` and restored on next visit.
|
|
23
|
+
- **`docs/readmes/`** — `vscode-extension.md` and `jetbrains-plugin.md` added for docs site cross-linking
|
|
24
|
+
- **`gen-context.config.json`** — example config committed alongside the repo for reference
|
|
25
|
+
- **Gemini adapter context file** — `.github/gemini-context.md` now generated alongside the copilot instructions file
|
|
26
|
+
- **SEO improvements across all docs pages** — structured data, canonical tags, improved meta descriptions, and `sitemap.xml` updated to v3.3.0
|
|
27
|
+
|
|
28
|
+
### Added (from `fix/defaults-css-coverage-budget-36` · #38)
|
|
29
|
+
- **`--each` flag — multi-repo parent directory support** · [#37](https://github.com/manojmallick/sigmap/issues/37)
|
|
30
|
+
- Running `node gen-context.js --each` (or `sigmap --each`) from a parent directory that contains multiple independent git repos now processes each repo in one shot.
|
|
31
|
+
- Scans immediate subdirectories; a subdirectory qualifies when it contains `.git` or a recognised project manifest (`package.json`, `pyproject.toml`, `Cargo.toml`, `go.mod`, `build.gradle`, `pom.xml`, `requirements.txt`).
|
|
32
|
+
- Each sub-repo is processed independently: it loads its own `gen-context.config.json` when present, uses its own `srcDirs`, and writes its own context files (`.github/copilot-instructions.md` etc.) inside itself.
|
|
33
|
+
- Summary printed at the end: `[sigmap] --each: done — 3 succeeded`.
|
|
34
|
+
- Distinct from `--monorepo` (which processes workspace packages inside a single repo); `--each` targets sibling repos under a shared parent directory.
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
- **Default excludes expanded + `changesCommits` corrected** · [#36](https://github.com/manojmallick/sigmap/issues/36)
|
|
38
|
+
- `changesCommits` default raised from `5` to `10` to match the documented recommended value.
|
|
39
|
+
- Added `playwright-tmp`, `playwright-report`, `test-results`, `.turbo`, `storybook-static`, `.docusaurus` to the default `exclude` list so they are skipped on modern JS/TS projects without requiring manual config.
|
|
40
|
+
- **CSS extractor: utility-class noise elimination** · [#36](https://github.com/manojmallick/sigmap/issues/36)
|
|
41
|
+
- Files where ≥70% of top-level selectors are single-word (e.g. Tailwind / compiled utility CSS) are now detected automatically and class extraction is skipped entirely, preventing the output from being flooded with low-signal entries like `.p-4`, `.flex`, `.text-sm`.
|
|
42
|
+
- For semantic CSS, BEM/hyphenated class names (e.g. `.modal-header`, `.btn-primary`) fill output slots first; single-word names only fill remaining slots up to 8.
|
|
43
|
+
- **`testCoverage` false-positive coverage markers eliminated** · [#36](https://github.com/manojmallick/sigmap/issues/36)
|
|
44
|
+
- Removed the "all word tokens" pass from `buildTestIndex` that caused common words appearing anywhere in a test file (comments, variable names) to mark unrelated functions as `✓` tested.
|
|
45
|
+
- Index now only includes tokens extracted from test name strings (`it('...')`, `test('...')`, `describe('...')`) and identifiers directly invoked inside `expect(fn())` / `assert(fn())` calls.
|
|
46
|
+
- **Token budget: mock/fixture files drop before test files** · [#36](https://github.com/manojmallick/sigmap/issues/36)
|
|
47
|
+
- Added `isMockFile()` helper and priority-9 drop tier in `applyTokenBudget`. Paths matching `mock`, `mocks`, `stub`, `stubs`, `fake`, `fakes`, `demo`, `__mocks__`, `fixtures` or file suffixes like `.mock.ts` now drop before test files (priority 8) and after generated files (priority 10), keeping real production code in context longer.
|
|
48
|
+
- Fixed `applyTokenBudget` loop direction: generated/mock/test files now drop first (as intended) rather than source files being dropped first.
|
|
49
|
+
- **`--monorepo` now respects configured output adapter** · [#39](https://github.com/manojmallick/sigmap/issues/39)
|
|
50
|
+
- Removed hardcoded `outputs: ['claude']` override — `--monorepo` now inherits `outputs` from the root config, defaulting to `copilot` (writes `copilot-instructions.md` per package).
|
|
51
|
+
- **IDE command resolution parity (VS Code/Open VSX/JetBrains)** · [#34](https://github.com/manojmallick/sigmap/issues/34)
|
|
52
|
+
- Unified resolver now checks both `sigmap` and `gen-context` executables with consistent fallback order.
|
|
53
|
+
- Improved cross-platform probing for local workspace bins, Volta/nvm/npm-global installs, and OS-specific command lookup (`where` on Windows, shell lookup on macOS/Linux).
|
|
54
|
+
- JetBrains plugin now resolves commands more reliably outside Node-only projects and provides OS-aware install guidance when command lookup fails.
|
|
55
|
+
|
|
56
|
+
### Changed
|
|
57
|
+
- **Installation guidance for plugin users**
|
|
58
|
+
- Updated VS Code/Open VSX and JetBrains setup docs to include all supported install paths: npm global, npm local, npx, standalone binaries in PATH, and project-local `gen-context.js`.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## [3.2.1] — 2026-04-07 — Patch: IDE Command Resolution & Plugin Parity
|
|
63
|
+
|
|
64
|
+
### Added
|
|
65
|
+
- **IDE command resolution parity (VS Code / Open VSX / JetBrains)** · [#34](https://github.com/manojmallick/sigmap/issues/34)
|
|
66
|
+
- Unified resolver checks both `sigmap` and `gen-context` executables with consistent fallback order
|
|
67
|
+
- Improved cross-platform probing for local workspace bins, Volta/nvm/npm-global installs, and OS-specific command lookup (`where` on Windows, shell lookup on macOS/Linux)
|
|
68
|
+
- JetBrains plugin resolves commands more reliably outside Node-only projects and provides OS-aware install guidance when command lookup fails
|
|
69
|
+
- **`scripts/sync-versions.mjs`** — one-shot script to bump version across all package manifests and `gen-context.js` in sync
|
|
70
|
+
- **Updated plugin docs** — VS Code/Open VSX and JetBrains setup docs updated with all supported install paths (npm global, npm local, npx, standalone binaries, project-local `gen-context.js`)
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## [3.2.0] — 2026-04-07 — Cross-Platform Standalone Binaries
|
|
14
75
|
|
|
15
76
|
### Added
|
|
16
77
|
- **Standalone binaries** — macOS (arm64 + x64), Linux x64, Windows x64 built via Node.js SEA
|
|
@@ -25,7 +86,7 @@ Format: [Semantic Versioning](https://semver.org/)
|
|
|
25
86
|
|
|
26
87
|
### Technical
|
|
27
88
|
- Uses [Node.js SEA](https://nodejs.org/api/single-executable-applications.html) (Node 20 `--experimental-sea-config` + `postject`)
|
|
28
|
-
- `gen-context.js`
|
|
89
|
+
- `gen-context.js` updated to include previously-missing `src/` modules (`todos`, `coverage`, `prdiff`) in the SEA bundle; `requireSourceOrBundled()` fallback remains SEA-compatible
|
|
29
90
|
- Binary builds run natively per OS in GHA (no cross-compilation)
|
|
30
91
|
- `release-attach` job waits for the npm-publish Release to exist before uploading binary assets
|
|
31
92
|
|
package/README.md
CHANGED
|
@@ -117,6 +117,91 @@ Available from any directory on your machine.
|
|
|
117
117
|
|
|
118
118
|
</details>
|
|
119
119
|
|
|
120
|
+
<details>
|
|
121
|
+
<summary><strong>Standalone binaries — no Node.js, no npm</strong></summary>
|
|
122
|
+
|
|
123
|
+
Download from the latest release:
|
|
124
|
+
|
|
125
|
+
- <https://github.com/manojmallick/sigmap/releases/latest>
|
|
126
|
+
|
|
127
|
+
Available assets:
|
|
128
|
+
|
|
129
|
+
- `sigmap-darwin-arm64` (macOS Apple Silicon)
|
|
130
|
+
- `sigmap-linux-x64` (Linux x64)
|
|
131
|
+
- `sigmap-win32-x64.exe` (Windows x64)
|
|
132
|
+
- `sigmap-checksums.txt` (SHA-256 checksums)
|
|
133
|
+
|
|
134
|
+
<details>
|
|
135
|
+
<summary><strong>macOS / Linux</strong></summary>
|
|
136
|
+
|
|
137
|
+
Run directly:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
chmod +x ./sigmap-darwin-arm64 # or ./sigmap-linux-x64
|
|
141
|
+
./sigmap-darwin-arm64 --help
|
|
142
|
+
./sigmap-darwin-arm64
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Make it globally available in Bash/Zsh (no `./` needed):
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
# 1) Pick a user bin dir and move/rename the binary
|
|
149
|
+
mkdir -p "$HOME/.local/bin"
|
|
150
|
+
mv ./sigmap-darwin-arm64 "$HOME/.local/bin/sigmap" # or sigmap-linux-x64
|
|
151
|
+
chmod +x "$HOME/.local/bin/sigmap"
|
|
152
|
+
|
|
153
|
+
# 2) Add to PATH in your shell profile
|
|
154
|
+
echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$HOME/.zshrc" # zsh
|
|
155
|
+
# echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$HOME/.bashrc" # bash
|
|
156
|
+
|
|
157
|
+
# 3) Reload shell and verify
|
|
158
|
+
source "$HOME/.zshrc" # or: source "$HOME/.bashrc"
|
|
159
|
+
sigmap --version
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
</details>
|
|
163
|
+
|
|
164
|
+
<details>
|
|
165
|
+
<summary><strong>Windows (PowerShell)</strong></summary>
|
|
166
|
+
|
|
167
|
+
Run directly:
|
|
168
|
+
|
|
169
|
+
```powershell
|
|
170
|
+
.\sigmap-win32-x64.exe --help
|
|
171
|
+
.\sigmap-win32-x64.exe
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Make it globally available:
|
|
175
|
+
|
|
176
|
+
```powershell
|
|
177
|
+
# 1) Create a user bin directory and rename the binary
|
|
178
|
+
New-Item -ItemType Directory -Force "$HOME\bin" | Out-Null
|
|
179
|
+
Move-Item .\sigmap-win32-x64.exe "$HOME\bin\sigmap.exe"
|
|
180
|
+
|
|
181
|
+
# 2) Add user bin to PATH (current user)
|
|
182
|
+
[Environment]::SetEnvironmentVariable(
|
|
183
|
+
"Path",
|
|
184
|
+
$env:Path + ";$HOME\bin",
|
|
185
|
+
"User"
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
# 3) Restart PowerShell and verify
|
|
189
|
+
sigmap --version
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
</details>
|
|
193
|
+
|
|
194
|
+
Optional checksum verification:
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
shasum -a 256 sigmap-darwin-arm64
|
|
198
|
+
# Compare with sigmap-checksums.txt
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Full guide: [docs/binaries.md](docs/binaries.md)
|
|
202
|
+
|
|
203
|
+
</details>
|
|
204
|
+
|
|
120
205
|
<details>
|
|
121
206
|
<summary><strong>npm local — per-project, version-pinned</strong></summary>
|
|
122
207
|
|
|
@@ -466,7 +551,8 @@ node gen-context.js --health --json Machine-readable health JSON
|
|
|
466
551
|
node gen-context.js --suggest-tool "<task>" Recommend model tier for a task
|
|
467
552
|
node gen-context.js --suggest-tool "<task>" --json Machine-readable tier recommendation
|
|
468
553
|
|
|
469
|
-
node gen-context.js --monorepo Per-package context for monorepos
|
|
554
|
+
node gen-context.js --monorepo Per-package context for monorepos (packages/, apps/, services/)
|
|
555
|
+
node gen-context.js --each Process each sub-repo under a parent directory
|
|
470
556
|
node gen-context.js --routing Include model routing hints in output
|
|
471
557
|
node gen-context.js --format cache Write Anthropic prompt-cache JSON
|
|
472
558
|
node gen-context.js --track Append run metrics to .context/usage.ndjson
|
package/gen-context.js
CHANGED
|
@@ -3700,7 +3700,7 @@ __factories["./src/mcp/server"] = function(module, exports) {
|
|
|
3700
3700
|
|
|
3701
3701
|
const SERVER_INFO = {
|
|
3702
3702
|
name: 'sigmap',
|
|
3703
|
-
version: '3.2.
|
|
3703
|
+
version: '3.2.1',
|
|
3704
3704
|
description: 'SigMap MCP server — code signatures on demand',
|
|
3705
3705
|
};
|
|
3706
3706
|
|
|
@@ -5049,7 +5049,7 @@ const path = require('path');
|
|
|
5049
5049
|
const os = require('os');
|
|
5050
5050
|
const { execSync } = require('child_process');
|
|
5051
5051
|
|
|
5052
|
-
const VERSION = '3.
|
|
5052
|
+
const VERSION = '3.3.0';
|
|
5053
5053
|
const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
|
|
5054
5054
|
|
|
5055
5055
|
function requireSourceOrBundled(key) {
|
|
@@ -5251,6 +5251,13 @@ function isGeneratedFile(filePath) {
|
|
|
5251
5251
|
return /(\.generated\.|\.pb\.|_pb\.)/.test(filePath);
|
|
5252
5252
|
}
|
|
5253
5253
|
|
|
5254
|
+
function isMockFile(filePath) {
|
|
5255
|
+
const p = filePath.replace(/\\/g, '/');
|
|
5256
|
+
return /\/(mock|mocks|stub|stubs|fake|fakes|demo|demos|__mocks__|fixtures)\//i.test(p) ||
|
|
5257
|
+
/\.(mock|stub|fake)\.[jt]sx?$/.test(p) ||
|
|
5258
|
+
/mock\.(ts|js|tsx|jsx)$/.test(p);
|
|
5259
|
+
}
|
|
5260
|
+
|
|
5254
5261
|
function applyTokenBudget(fileEntries, maxTokens) {
|
|
5255
5262
|
// fileEntries: [{ filePath, sigs, mtime }]
|
|
5256
5263
|
// Reserve ~10% for formatting overhead (section headers, code fences, top-level header)
|
|
@@ -5262,6 +5269,7 @@ function applyTokenBudget(fileEntries, maxTokens) {
|
|
|
5262
5269
|
const withPriority = fileEntries.map((e) => {
|
|
5263
5270
|
let priority = 0;
|
|
5264
5271
|
if (isGeneratedFile(e.filePath)) priority = 10;
|
|
5272
|
+
else if (isMockFile(e.filePath)) priority = 9;
|
|
5265
5273
|
else if (isTestFile(e.filePath)) priority = 8;
|
|
5266
5274
|
else if (isConfigFile(e.filePath)) priority = 6;
|
|
5267
5275
|
else priority = 4;
|
|
@@ -5276,14 +5284,15 @@ function applyTokenBudget(fileEntries, maxTokens) {
|
|
|
5276
5284
|
|
|
5277
5285
|
const kept = [];
|
|
5278
5286
|
let dropped = 0;
|
|
5279
|
-
|
|
5280
|
-
|
|
5287
|
+
// Iterate forward: highest drop-priority files (generated=10, mock=9, test=8) are at index 0
|
|
5288
|
+
// Drop those first until we're under budget, then keep everything else
|
|
5289
|
+
for (const entry of withPriority) {
|
|
5281
5290
|
const entryTokens = estimateTokens(entry.sigs.join('\n'));
|
|
5282
|
-
if (total
|
|
5283
|
-
kept.unshift(entry);
|
|
5284
|
-
} else {
|
|
5291
|
+
if (total > effectiveBudget) {
|
|
5285
5292
|
total -= entryTokens;
|
|
5286
5293
|
dropped++;
|
|
5294
|
+
} else {
|
|
5295
|
+
kept.push(entry);
|
|
5287
5296
|
}
|
|
5288
5297
|
}
|
|
5289
5298
|
if (dropped > 0) {
|
|
@@ -6206,11 +6215,10 @@ function runMonorepo(cwd, config) {
|
|
|
6206
6215
|
for (const pkgPath of packages) {
|
|
6207
6216
|
const pkgName = path.relative(cwd, pkgPath);
|
|
6208
6217
|
console.warn(`[sigmap] monorepo: processing ${pkgName}`);
|
|
6209
|
-
// Per-package config: scan src/ and package root,
|
|
6218
|
+
// Per-package config: scan src/ and package root, inherit outputs from parent config
|
|
6210
6219
|
const pkgConfig = {
|
|
6211
6220
|
...config,
|
|
6212
6221
|
srcDirs: ['src', 'lib', 'app', '.'],
|
|
6213
|
-
outputs: ['claude'],
|
|
6214
6222
|
};
|
|
6215
6223
|
try {
|
|
6216
6224
|
runGenerate(pkgPath, pkgConfig, false);
|
|
@@ -6218,7 +6226,66 @@ function runMonorepo(cwd, config) {
|
|
|
6218
6226
|
console.warn(`[sigmap] monorepo: failed for ${pkgName}: ${err.message}`);
|
|
6219
6227
|
}
|
|
6220
6228
|
}
|
|
6221
|
-
console.warn(`[sigmap] monorepo: wrote
|
|
6229
|
+
console.warn(`[sigmap] monorepo: done — wrote context files for ${packages.length} packages`);
|
|
6230
|
+
}
|
|
6231
|
+
|
|
6232
|
+
// ---------------------------------------------------------------------------
|
|
6233
|
+
// Multi-repo support (--each)
|
|
6234
|
+
// Run sigmap independently for every immediate subdirectory that looks like
|
|
6235
|
+
// an independent git repo or project root. Each sub-repo gets its own
|
|
6236
|
+
// context files written inside it, using its own gen-context.config.json
|
|
6237
|
+
// when present.
|
|
6238
|
+
// ---------------------------------------------------------------------------
|
|
6239
|
+
function detectRepoDirs(cwd) {
|
|
6240
|
+
let entries;
|
|
6241
|
+
try {
|
|
6242
|
+
entries = fs.readdirSync(cwd, { withFileTypes: true });
|
|
6243
|
+
} catch (_) {
|
|
6244
|
+
return [];
|
|
6245
|
+
}
|
|
6246
|
+
const repos = [];
|
|
6247
|
+
for (const entry of entries) {
|
|
6248
|
+
if (!entry.isDirectory()) continue;
|
|
6249
|
+
if (entry.name.startsWith('.')) continue;
|
|
6250
|
+
const abs = path.join(cwd, entry.name);
|
|
6251
|
+
// A directory qualifies if it has its own .git folder
|
|
6252
|
+
// OR contains a recognised project manifest (fallback for un-initialised repos)
|
|
6253
|
+
const hasGit = fs.existsSync(path.join(abs, '.git'));
|
|
6254
|
+
const hasManifest = PKG_MANIFESTS.some((m) => fs.existsSync(path.join(abs, m)));
|
|
6255
|
+
if (hasGit || hasManifest) repos.push(abs);
|
|
6256
|
+
}
|
|
6257
|
+
return repos;
|
|
6258
|
+
}
|
|
6259
|
+
|
|
6260
|
+
function runEach(cwd, baseConfig) {
|
|
6261
|
+
const repos = detectRepoDirs(cwd);
|
|
6262
|
+
if (repos.length === 0) {
|
|
6263
|
+
console.warn('[sigmap] --each: no project subdirectories found');
|
|
6264
|
+
console.warn('[sigmap] A subdirectory qualifies when it contains .git or a package manifest');
|
|
6265
|
+
console.warn(`[sigmap] (package.json / pyproject.toml / Cargo.toml / go.mod / etc.)`);
|
|
6266
|
+
return;
|
|
6267
|
+
}
|
|
6268
|
+
console.warn(`[sigmap] --each: found ${repos.length} repos — ${repos.map((r) => path.basename(r)).join(', ')}`);
|
|
6269
|
+
let ok = 0, failed = 0;
|
|
6270
|
+
for (const repoDir of repos) {
|
|
6271
|
+
const name = path.basename(repoDir);
|
|
6272
|
+
// Load each repo's own config independently, fall back to base config
|
|
6273
|
+
let repoConfig;
|
|
6274
|
+
try {
|
|
6275
|
+
repoConfig = loadConfig(repoDir);
|
|
6276
|
+
} catch (_) {
|
|
6277
|
+
repoConfig = { ...baseConfig };
|
|
6278
|
+
}
|
|
6279
|
+
console.warn(`[sigmap] --each: processing ${name} …`);
|
|
6280
|
+
try {
|
|
6281
|
+
runGenerate(repoDir, repoConfig, false);
|
|
6282
|
+
ok++;
|
|
6283
|
+
} catch (err) {
|
|
6284
|
+
console.warn(`[sigmap] --each: FAILED for ${name}: ${err.message}`);
|
|
6285
|
+
failed++;
|
|
6286
|
+
}
|
|
6287
|
+
}
|
|
6288
|
+
console.warn(`[sigmap] --each: done — ${ok} succeeded${failed > 0 ? `, ${failed} failed` : ''}`);
|
|
6222
6289
|
}
|
|
6223
6290
|
|
|
6224
6291
|
// ---------------------------------------------------------------------------
|
|
@@ -6272,50 +6339,71 @@ function resolveProjectRoot(startDir) {
|
|
|
6272
6339
|
return startDir;
|
|
6273
6340
|
}
|
|
6274
6341
|
|
|
6275
|
-
function
|
|
6342
|
+
function detectInvokedAs() {
|
|
6343
|
+
const argv1 = process.argv[1] || '';
|
|
6344
|
+
const base = path.basename(argv1);
|
|
6345
|
+
const baseNoExt = base.endsWith('.js') ? base.slice(0, -3) : base;
|
|
6346
|
+
// npx: the script path goes through an _npx cache directory
|
|
6347
|
+
if (argv1.includes('/_npx/') || argv1.includes('\\_npx\\') ||
|
|
6348
|
+
argv1.includes('/.npm/_') || argv1.includes('\\.npm\\_')) {
|
|
6349
|
+
return 'npx sigmap';
|
|
6350
|
+
}
|
|
6351
|
+
// globally-installed bin aliases (no .js extension)
|
|
6352
|
+
if (baseNoExt === 'sigmap') return 'sigmap';
|
|
6353
|
+
if (baseNoExt === 'gen-context') return 'gen-context';
|
|
6354
|
+
// default: local file invocation
|
|
6355
|
+
return 'node gen-context.js';
|
|
6356
|
+
}
|
|
6357
|
+
|
|
6358
|
+
function printHelp(cmd) {
|
|
6359
|
+
cmd = cmd || 'node gen-context.js';
|
|
6360
|
+
const header = cmd === 'node gen-context.js'
|
|
6361
|
+
? `SigMap — gen-context.js v${VERSION}`
|
|
6362
|
+
: `SigMap v${VERSION} (${cmd})`;
|
|
6276
6363
|
console.log(`
|
|
6277
|
-
|
|
6364
|
+
${header}
|
|
6278
6365
|
Zero-dependency AI context engine
|
|
6279
6366
|
|
|
6280
6367
|
Usage:
|
|
6281
|
-
|
|
6282
|
-
|
|
6283
|
-
|
|
6284
|
-
|
|
6285
|
-
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
-
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
|
|
6317
|
-
|
|
6318
|
-
|
|
6368
|
+
${cmd} Generate context once and exit
|
|
6369
|
+
${cmd} --monorepo Generate per-package context (monorepo)
|
|
6370
|
+
${cmd} --each Run for every repo in the current directory
|
|
6371
|
+
${cmd} --routing Include model routing hints in output
|
|
6372
|
+
${cmd} --format cache Also write Anthropic prompt-cache JSON
|
|
6373
|
+
${cmd} --track Append run metrics to .context/usage.ndjson
|
|
6374
|
+
${cmd} --watch Generate + watch for file changes
|
|
6375
|
+
${cmd} --setup Generate + install git hook + watch
|
|
6376
|
+
${cmd} --mcp Start MCP server on stdio
|
|
6377
|
+
${cmd} --report Token reduction stats to stdout
|
|
6378
|
+
${cmd} --report --json Token report as JSON (for CI; exits 1 if over budget)
|
|
6379
|
+
${cmd} --report --history Print usage log summary from .context/usage.ndjson
|
|
6380
|
+
${cmd} --report --history --chart Include inline SVG charts + Unicode sparklines
|
|
6381
|
+
${cmd} --dashboard Write benchmarks/reports/dashboard.html
|
|
6382
|
+
${cmd} --suggest-tool "<task>" Recommend model tier for a task description
|
|
6383
|
+
${cmd} --suggest-tool "<task>" --json Machine-readable tier recommendation
|
|
6384
|
+
${cmd} --health Print composite health score
|
|
6385
|
+
${cmd} --health --json Machine-readable health score
|
|
6386
|
+
${cmd} --diff Generate context for git-changed files only
|
|
6387
|
+
${cmd} --diff <base-ref> Generate context + structural diff vs base ref (e.g. main)
|
|
6388
|
+
${cmd} --diff --staged Generate context for staged files only
|
|
6389
|
+
${cmd} --benchmark Run retrieval benchmark (benchmarks/tasks/retrieval.jsonl)
|
|
6390
|
+
${cmd} --adapter <name> Generate for a specific adapter only (v3.0+)
|
|
6391
|
+
${cmd} --adapter <name> --json Show adapter output path as JSON
|
|
6392
|
+
${cmd} --benchmark --json Benchmark results as JSON
|
|
6393
|
+
${cmd} --eval Alias for --benchmark
|
|
6394
|
+
${cmd} --analyze Per-file breakdown: sigs, tokens, extractor, coverage
|
|
6395
|
+
${cmd} --analyze --json Breakdown as JSON
|
|
6396
|
+
${cmd} --analyze --slow Re-time each extractor; flag files >50ms
|
|
6397
|
+
${cmd} --diagnose-extractors Run all 21 extractors vs fixtures; show pass/fail + diff
|
|
6398
|
+
${cmd} --query "<text>" Rank files by relevance to a query
|
|
6399
|
+
${cmd} --query "<text>" --json Ranked results as JSON
|
|
6400
|
+
${cmd} --query "<text>" --top <n> Limit results to top N files (default 10)
|
|
6401
|
+
${cmd} --impact <file> Show every file impacted by changing <file>
|
|
6402
|
+
${cmd} --impact <file> --json Impact as JSON {changed, direct, transitive, tests, routes}
|
|
6403
|
+
${cmd} --impact <file> --depth <n> BFS depth limit (default 3, 0=unlimited)
|
|
6404
|
+
${cmd} --init Write example config + .contextignore scaffold
|
|
6405
|
+
${cmd} --help Show this message
|
|
6406
|
+
${cmd} --version Show version
|
|
6319
6407
|
|
|
6320
6408
|
Strategies (set via config "strategy" key):
|
|
6321
6409
|
"full" Single file, all signatures. Works everywhere. (default)
|
|
@@ -6380,7 +6468,7 @@ function main() {
|
|
|
6380
6468
|
}
|
|
6381
6469
|
|
|
6382
6470
|
if (args.includes('--help') || args.includes('-h')) {
|
|
6383
|
-
printHelp();
|
|
6471
|
+
printHelp(detectInvokedAs());
|
|
6384
6472
|
process.exit(0);
|
|
6385
6473
|
}
|
|
6386
6474
|
|
|
@@ -6779,6 +6867,11 @@ function main() {
|
|
|
6779
6867
|
process.exit(0);
|
|
6780
6868
|
}
|
|
6781
6869
|
|
|
6870
|
+
if (args.includes('--each')) {
|
|
6871
|
+
runEach(cwd, config);
|
|
6872
|
+
process.exit(0);
|
|
6873
|
+
}
|
|
6874
|
+
|
|
6782
6875
|
if (args.includes('--setup')) {
|
|
6783
6876
|
runGenerate(cwd, config, false);
|
|
6784
6877
|
installHook(cwd, scriptPath);
|
package/gen-project-map.js
CHANGED
|
@@ -24,19 +24,32 @@ const OUTPUT_FILE = 'PROJECT_MAP.md';
|
|
|
24
24
|
// ---------------------------------------------------------------------------
|
|
25
25
|
const args = process.argv.slice(2);
|
|
26
26
|
|
|
27
|
+
function detectInvokedAs() {
|
|
28
|
+
const argv1 = process.argv[1] || '';
|
|
29
|
+
const base = path.basename(argv1);
|
|
30
|
+
const baseNoExt = base.endsWith('.js') ? base.slice(0, -3) : base;
|
|
31
|
+
if (argv1.includes('/_npx/') || argv1.includes('\\_npx\\') ||
|
|
32
|
+
argv1.includes('/.npm/_') || argv1.includes('\\.npm\\_')) {
|
|
33
|
+
return 'npx gen-project-map';
|
|
34
|
+
}
|
|
35
|
+
if (baseNoExt === 'gen-project-map') return 'gen-project-map';
|
|
36
|
+
return 'node gen-project-map.js';
|
|
37
|
+
}
|
|
38
|
+
|
|
27
39
|
if (args.includes('--version')) {
|
|
28
40
|
console.log(`gen-project-map.js v${VERSION}`);
|
|
29
41
|
process.exit(0);
|
|
30
42
|
}
|
|
31
43
|
|
|
32
44
|
if (args.includes('--help')) {
|
|
45
|
+
const cmd = detectInvokedAs();
|
|
33
46
|
console.log([
|
|
34
|
-
`
|
|
47
|
+
`SigMap project map generator v${VERSION} (${cmd})`,
|
|
35
48
|
'',
|
|
36
49
|
'Usage:',
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
50
|
+
` ${cmd} Generate PROJECT_MAP.md`,
|
|
51
|
+
` ${cmd} --version Print version and exit`,
|
|
52
|
+
` ${cmd} --help Print this message`,
|
|
40
53
|
'',
|
|
41
54
|
'Configuration: gen-context.config.json (same file as gen-context.js)',
|
|
42
55
|
' srcDirs — directories to scan (default: ["src","app","lib",...])',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sigmap",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
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": {
|
|
@@ -27,7 +27,8 @@
|
|
|
27
27
|
"map": "node gen-project-map.js",
|
|
28
28
|
"mcp": "node gen-context.js --mcp",
|
|
29
29
|
"build:binary": "node scripts/build-binary.mjs",
|
|
30
|
-
"verify:binary": "node scripts/verify-binary.mjs"
|
|
30
|
+
"verify:binary": "node scripts/verify-binary.mjs",
|
|
31
|
+
"version:sync": "node scripts/sync-versions.mjs"
|
|
31
32
|
},
|
|
32
33
|
"files": [
|
|
33
34
|
"gen-context.js",
|
package/src/config/defaults.js
CHANGED
|
@@ -29,6 +29,12 @@ const DEFAULTS = {
|
|
|
29
29
|
'node_modules', '.git', 'dist', 'build', 'out',
|
|
30
30
|
'__pycache__', '.next', 'coverage', 'target', 'vendor',
|
|
31
31
|
'.context',
|
|
32
|
+
// CI/test artifacts
|
|
33
|
+
'playwright-tmp', 'playwright-report', 'test-results',
|
|
34
|
+
// build/monorepo caches
|
|
35
|
+
'.turbo',
|
|
36
|
+
// documentation build output
|
|
37
|
+
'storybook-static', '.docusaurus',
|
|
32
38
|
],
|
|
33
39
|
|
|
34
40
|
// Maximum directory depth to recurse
|
|
@@ -86,7 +92,7 @@ const DEFAULTS = {
|
|
|
86
92
|
changes: true,
|
|
87
93
|
|
|
88
94
|
// Number of commits used for changes section
|
|
89
|
-
changesCommits:
|
|
95
|
+
changesCommits: 10,
|
|
90
96
|
|
|
91
97
|
// Add test coverage markers to extracted function signatures (opt-in)
|
|
92
98
|
testCoverage: false,
|
|
@@ -34,12 +34,26 @@ function buildTestIndex(cwd, testDirs) {
|
|
|
34
34
|
continue;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
// Extract tokens from JS/TS test name strings (it/test/describe calls)
|
|
38
|
+
for (const m of src.matchAll(/\b(?:it|test|describe)\s*\(\s*['"\`]([\w_ ]+)['"\`]/g)) {
|
|
39
|
+
if (!m[1]) continue;
|
|
40
|
+
const full = m[1].replace(/[\s_]+/g, '_').toLowerCase();
|
|
41
|
+
names.add(full); // also keep whole phrase: "validate_user"
|
|
42
|
+
for (const word of m[1].split(/[\s_]+/)) {
|
|
43
|
+
if (word.length >= 3) names.add(word.toLowerCase());
|
|
44
|
+
}
|
|
39
45
|
}
|
|
40
|
-
|
|
41
|
-
for (const m of src.matchAll(/\
|
|
42
|
-
if (m[1]
|
|
46
|
+
// Python/Ruby style: def test_funcname() / def test_funcname: → index the suffix
|
|
47
|
+
for (const m of src.matchAll(/\bdef\s+test_([\w]+)\s*[(:]/g)) {
|
|
48
|
+
if (!m[1]) continue;
|
|
49
|
+
names.add(m[1].toLowerCase()); // full suffix: "validate_user"
|
|
50
|
+
for (const word of m[1].split('_')) {
|
|
51
|
+
if (word.length >= 3) names.add(word.toLowerCase());
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Also capture identifiers directly invoked in expect/assert calls
|
|
55
|
+
for (const m of src.matchAll(/\b(?:expect|assert)\s*\(\s*(?:await\s+)?([\w]+)\s*\(/g)) {
|
|
56
|
+
if (m[1] && m[1].length >= 3) names.add(m[1].toLowerCase());
|
|
43
57
|
}
|
|
44
58
|
}
|
|
45
59
|
}
|
|
@@ -50,7 +64,15 @@ function buildTestIndex(cwd, testDirs) {
|
|
|
50
64
|
function isTested(funcName, testIndex) {
|
|
51
65
|
if (!funcName || funcName.length < 3 || !testIndex || testIndex.size === 0) return false;
|
|
52
66
|
const lower = funcName.toLowerCase();
|
|
67
|
+
// Direct match or test_ prefix match
|
|
53
68
|
if (testIndex.has(lower) || testIndex.has(`test_${lower}`)) return true;
|
|
69
|
+
// Token-level match: split camelCase and snake_case, check if all meaningful tokens are covered
|
|
70
|
+
const tokens = funcName
|
|
71
|
+
.replace(/([A-Z])/g, '_$1')
|
|
72
|
+
.toLowerCase()
|
|
73
|
+
.split(/[_\s]+/)
|
|
74
|
+
.filter(t => t.length >= 3);
|
|
75
|
+
if (tokens.length >= 2 && tokens.every(t => testIndex.has(t))) return true;
|
|
54
76
|
return false;
|
|
55
77
|
}
|
|
56
78
|
|
package/src/extractors/css.js
CHANGED
|
@@ -37,13 +37,31 @@ function extract(src) {
|
|
|
37
37
|
sigs.push(`@function ${m[1]}(${m[2].trim()})`);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
// Key class names (top-level)
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
// Key class names (top-level) — prefer hyphenated BEM/component names over utilities
|
|
41
|
+
const allClassMatches = [...stripped.matchAll(/^\.([\w-]+)(?=[^{]*\{)/gm)];
|
|
42
|
+
// Utility-class detection: classes are "utility-like" if they have no hyphen (e.g. .flex)
|
|
43
|
+
// OR match Tailwind patterns: -digit suffix (.p-4, .bg-blue-500) or common size abbreviations
|
|
44
|
+
// (.text-sm, .py-lg). Files where ≥70% of selectors are utility-like are skipped to avoid noise.
|
|
45
|
+
function looksLikeUtility(name) {
|
|
46
|
+
if (!name.includes('-')) return true;
|
|
47
|
+
if (/-\d/.test(name)) return true;
|
|
48
|
+
if (/-(?:sm|md|lg|xl|xs|2xl|3xl|px|py|full|auto|none|screen)$/.test(name)) return true;
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
const utilityCount = allClassMatches.filter(m => looksLikeUtility(m[1])).length;
|
|
52
|
+
const isUtilityFile = allClassMatches.length >= 5 && (utilityCount / allClassMatches.length) >= 0.70;
|
|
53
|
+
if (!isUtilityFile) {
|
|
54
|
+
const hyphenated = [];
|
|
55
|
+
const singleWord = [];
|
|
56
|
+
for (const m of allClassMatches) {
|
|
57
|
+
if (m[1].includes('__') || m[1].includes('--')) hyphenated.push(m[1]); // BEM names first
|
|
58
|
+
else if (m[1].includes('-')) hyphenated.push(m[1]); // other hyphenated component names
|
|
59
|
+
else singleWord.push(m[1]);
|
|
60
|
+
}
|
|
61
|
+
// Up to 8 slots: hyphenated classes (semantic) first, then single-word to fill remaining
|
|
62
|
+
const selected = [...hyphenated, ...singleWord].slice(0, 8);
|
|
63
|
+
for (const name of selected) sigs.push(`.${name}`);
|
|
45
64
|
}
|
|
46
|
-
for (const name of classNames) sigs.push(`.${name}`);
|
|
47
65
|
|
|
48
66
|
return sigs.slice(0, 25);
|
|
49
67
|
}
|
package/src/mcp/server.js
CHANGED