sigmap 8.25.0 → 8.26.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -10,6 +10,30 @@ Format: [Semantic Versioning](https://semver.org/)
10
10
 
11
11
  ---
12
12
 
13
+ ## [8.26.1] — 2026-08-18
14
+
15
+ Patch release — **"Trust Quick Wins II" (G1)**: the extraction layer gets the same honesty treatment the benchmarks got in v8.19.
16
+
17
+ ### Added
18
+ - **`KNOWN_LIMITATIONS.md` + README extraction-honesty tiers (#520, PR #521):** one page stating plainly what each extractor tier can and cannot do — Tier 1 AST (Python via `python_ast.py`, regex fallback without `python3`), Tier 2 anchored regex (the 11 `withAnchor` brace languages, doc hints on 6), Tier 3 pattern/heuristic (the rest + generic fallback) — plus the truncation caps (25 signatures/file, 8 members/block) and what falls off, the nested-paren regex gap (the stated G4/D1 precondition), and the honest `verify` implication: a real symbol missing from the index flags `fake-symbol` at **medium** confidence — a conservative false positive, never a silent pass. README carries a compact "Extraction honesty" tier label linking the page. A 6-check guard test drift-locks the doc's counts to `version.json` and cross-checks the Tier-2 count against the extractors that actually call `withAnchor`.
19
+
20
+ ### Changed
21
+ - 6 new guard checks (133 test files). No runtime code changed; the npm tarball is unaffected.
22
+
23
+ ---
24
+
25
+ ## [8.26.0] — 2026-08-18
26
+
27
+ Minor release — **"Agent Economy III" (v8.26, F3+F4)**: the optimal SigMap usage loop ships as installable agent skills in every client's native format — completing the Agent Economy pillar (F1 budget → F2 tune → F3+F4 skills).
28
+
29
+ ### Added
30
+ - **`sigmap skills` (#517, PR #518):** new `src/skills/skills.js` — two canonical, deterministic skill documents: **sigmap-usage-maximizer** (F3 — the spend-minimizing loop: `ask` before any file read · `get_lines` for anchored ranges instead of whole files · `verify_suggestion` before trusting generated code · `squeeze` any big log/trace/JSON · checkpoint via `create_checkpoint`/`note` · check `get_budget` and, near budget, summarize-then-drop; token accounting reads the F1 ledger — no LLM calls) and **sigmap-config-optimizer** (the F2 playbook: `tune` → review the evidence-naming reasons → `tune --apply` → `validate`). Multi-client installer mirroring the `mcp/install.js` CLIENTS pattern: Claude Code (`.claude/skills/<skill>/SKILL.md`), Cursor (`.cursor/rules/*.mdc`), Windsurf (`.windsurf/rules/*.md`), GitHub Copilot (`.github/instructions/*.instructions.md`), and Codex — a marker-delimited block injected into `AGENTS.md` **above** the `## Auto-generated signatures` marker, so the codex adapter's regeneration preserves it (proven by an end-to-end regeneration test). Human content is never touched; every install is idempotent (`installed`/`updated`/`already`). CLI: `sigmap skills list` and `sigmap skills install [--client <name> | --all] [--json]` — plain `install` wires only clients whose parent artifact exists (the `--setup` only-touch-existing precedent); `--client`/`--all` create. Zero new dependencies.
31
+
32
+ ### Changed
33
+ - 8 new integration tests (132 test files); bundle rebuilt (149 modules).
34
+
35
+ ---
36
+
13
37
  ## [8.25.0] — 2026-08-18
14
38
 
15
39
  Minor release — **"Agent Economy II" (v8.25, F2)**: the discovery stack becomes a config optimizer — one command that recommends (and can apply) the config a repo actually needs.
package/README.md CHANGED
@@ -122,7 +122,7 @@ Ask → Rank → Context → Validate → Judge → Learn
122
122
 
123
123
  <!--SM:benchmarkBlock-->
124
124
  ```
125
- Benchmark : sigmap-v8.25-main (21 repositories, including R language)
125
+ Benchmark : sigmap-v8.26-main (21 repositories, including R language)
126
126
  Date : 2026-08-17
127
127
 
128
128
  Hit@5 : 82.2% (grep-agent baseline 44.0% — 1.87× lift)
@@ -397,6 +397,18 @@ All implemented with zero external dependencies.
397
397
 
398
398
  [Full language table →](https://sigmap.io/guide/generalization.html)
399
399
 
400
+ ### Extraction honesty
401
+
402
+ Not all 33 languages get the same depth — and we say so plainly:
403
+
404
+ | Tier | Coverage | Depth |
405
+ |------|----------|-------|
406
+ | **AST** | Python (`python3` on PATH; regex fallback without) | Full parse |
407
+ | **Anchored regex** | 11 brace languages (JS, TS, Go, Rust, Java, Kotlin, Swift, PHP, Scala, Dart, C#) | Declarations + `:start-end` line anchors; doc hints on 6 |
408
+ | **Pattern/heuristic** | Everything else + generic fallback | Line-oriented patterns |
409
+
410
+ Caps: 25 signatures/file · 8 members/block. Full details, known regex gaps, and what they mean for `verify`: **[KNOWN_LIMITATIONS.md](KNOWN_LIMITATIONS.md)**.
411
+
400
412
  ---
401
413
 
402
414
  ## License
package/gen-context.js CHANGED
@@ -15195,7 +15195,7 @@ __factories["./src/mcp/server"] = function(module, exports) {
15195
15195
 
15196
15196
  const SERVER_INFO = {
15197
15197
  name: 'sigmap',
15198
- version: '8.25.0',
15198
+ version: '8.26.1',
15199
15199
  description: 'SigMap MCP server — code signatures on demand',
15200
15200
  };
15201
15201
 
@@ -18089,6 +18089,192 @@ __factories["./src/session/notes"] = function(module, exports) {
18089
18089
 
18090
18090
  };
18091
18091
 
18092
+ // ── ./src/skills/skills ──
18093
+ __factories["./src/skills/skills"] = function(module, exports) {
18094
+
18095
+ /**
18096
+ * sigmap skills — canonical agent playbooks + multi-client installer (F3+F4, #517).
18097
+ *
18098
+ * Two deterministic skill documents (the spend-minimizing usage loop and the
18099
+ * config-optimizer playbook) emitted in each client's native skill/rules
18100
+ * format — the `mcp/install.js` CLIENTS pattern applied to skills. File-based
18101
+ * clients get sigmap-namespaced files we own outright; AGENTS.md (codex) gets
18102
+ * a marker-delimited block inserted ABOVE the `## Auto-generated signatures`
18103
+ * marker, since the codex adapter preserves content above it and replaces
18104
+ * everything below. Idempotent; human content is never touched.
18105
+ */
18106
+
18107
+ const fs = require('fs');
18108
+ const path = require('path');
18109
+
18110
+ const START = '<!-- sigmap-skills:start -->';
18111
+ const END = '<!-- sigmap-skills:end -->';
18112
+ const SIGNATURES_MARKER = '## Auto-generated signatures';
18113
+
18114
+ const SKILLS = {
18115
+ 'sigmap-usage-maximizer': {
18116
+ title: 'SigMap usage maximizer',
18117
+ description: 'Spend-minimizing loop for agents in a SigMap-indexed repo: ask before reading, read ranges, verify before trusting, squeeze big pastes, checkpoint, watch the budget.',
18118
+ body: [
18119
+ 'Follow this loop before any file exploration in a repo with SigMap installed.',
18120
+ '',
18121
+ '1. **Ask before reading.** `sigmap ask "<task>"` (or the `query_context` MCP tool) ranks the relevant files as ~hundreds of tokens of signatures instead of thousands of raw-file tokens. Never open files to "look around".',
18122
+ '2. **Read ranges, not files.** Use the `get_lines` MCP tool with the `:start-end` line anchors carried on every signature to pull only the lines you need.',
18123
+ '3. **Ground before trusting.** Run the `verify_suggestion` MCP tool (or `sigmap verify-ai-output`) on generated code before applying it — it flags fabricated files, imports, symbols, and npm scripts against the live index.',
18124
+ '4. **Squeeze big pastes.** Any stack trace, CI/build log, or JSON blob goes through `sigmap squeeze` (or the `squeeze_output` MCP tool) before it enters context — the signal survives, the noise does not.',
18125
+ '5. **Checkpoint progress.** Use the `create_checkpoint` MCP tool or `sigmap note "<decision>"` so a follow-up session resumes without re-deriving state.',
18126
+ '6. **Watch the budget.** Check the `get_budget` MCP tool or `sigmap budget` (estimates from SigMap\'s local ledger — no LLM calls). Near the budget: summarize-then-drop older context instead of accumulating, and prefer terse output.',
18127
+ ].join('\n'),
18128
+ },
18129
+ 'sigmap-config-optimizer': {
18130
+ title: 'SigMap config optimizer',
18131
+ description: 'Playbook for getting a correct SigMap config on any repo: detect with sigmap tune, review the per-change reasons, apply, validate.',
18132
+ body: [
18133
+ 'Playbook for configuring SigMap on a new or misconfigured repo.',
18134
+ '',
18135
+ '1. **Detect.** `sigmap tune` prints a recommended config diff — srcDirs pin, monorepo mode, adapters, excludes, budget — with one evidence-naming reason per change. Read-only.',
18136
+ '2. **Review.** Every reason names its evidence (workspace marker, client artifact, vendored dir, file-count estimate). Explicit user config is never proposed against.',
18137
+ '3. **Apply.** `sigmap tune --apply` merges the recommendations into `gen-context.config.json`, preserving every existing key. Idempotent — a second `tune` proposes nothing.',
18138
+ '4. **Validate.** `sigmap validate` confirms the generated context matches the config; `sigmap doctor` diagnoses anything left.',
18139
+ ].join('\n'),
18140
+ },
18141
+ };
18142
+
18143
+ // Client registry — parent = artifact whose presence means "user uses this
18144
+ // client" (the `--setup` only-touch-existing rule for plain `skills install`).
18145
+ const SKILL_CLIENTS = {
18146
+ claude: { label: 'Claude Code', parent: ['.claude'],
18147
+ target: (cwd, skill) => path.join(cwd, '.claude', 'skills', skill, 'SKILL.md') },
18148
+ cursor: { label: 'Cursor', parent: ['.cursor'],
18149
+ target: (cwd, skill) => path.join(cwd, '.cursor', 'rules', `${skill}.mdc`) },
18150
+ windsurf: { label: 'Windsurf', parent: ['.windsurf'],
18151
+ target: (cwd, skill) => path.join(cwd, '.windsurf', 'rules', `${skill}.md`) },
18152
+ copilot: { label: 'GitHub Copilot', parent: ['.github'],
18153
+ target: (cwd, skill) => path.join(cwd, '.github', 'instructions', `${skill}.instructions.md`) },
18154
+ codex: { label: 'Codex CLI (AGENTS.md)', parent: ['AGENTS.md'],
18155
+ target: (cwd) => path.join(cwd, 'AGENTS.md'), inject: true },
18156
+ };
18157
+
18158
+ function _footer(version) {
18159
+ const ver = version ? ` v${version}` : '';
18160
+ return `<sub>Generated by SigMap${ver} · run \`sigmap skills install\` to refresh.</sub>`;
18161
+ }
18162
+
18163
+ /** Render one skill's client-specific file content. */
18164
+ function renderSkill(client, skillName, version) {
18165
+ const skill = SKILLS[skillName];
18166
+ const body = `# ${skill.title}\n\n${skill.body}\n\n${_footer(version)}\n`;
18167
+ if (client === 'claude') {
18168
+ return `---\nname: ${skillName}\ndescription: ${skill.description}\n---\n\n${body}`;
18169
+ }
18170
+ if (client === 'cursor') {
18171
+ return `---\ndescription: ${skill.description}\nalwaysApply: false\n---\n\n${body}`;
18172
+ }
18173
+ if (client === 'copilot') {
18174
+ return `---\napplyTo: "**"\n---\n\n${body}`;
18175
+ }
18176
+ return body; // windsurf: plain markdown
18177
+ }
18178
+
18179
+ /** Render the combined AGENTS.md block (both skills, marker-delimited). */
18180
+ function renderAgentsBlock(version) {
18181
+ const parts = [START, '## SigMap agent skills', ''];
18182
+ for (const name of Object.keys(SKILLS)) {
18183
+ parts.push(`### ${SKILLS[name].title}`, '', SKILLS[name].body, '');
18184
+ }
18185
+ parts.push(_footer(version), END);
18186
+ return parts.join('\n');
18187
+ }
18188
+
18189
+ /**
18190
+ * Inject (or replace) the skills block in AGENTS.md content.
18191
+ * A new block is inserted ABOVE the `## Auto-generated signatures` marker —
18192
+ * the codex adapter's write() preserves content above that marker and
18193
+ * replaces everything below it. Never touches content outside the markers.
18194
+ */
18195
+ function injectSkillsBlock(existing, block) {
18196
+ const src = String(existing || '');
18197
+ const startIdx = src.indexOf(START);
18198
+ if (startIdx !== -1) {
18199
+ const endIdx = src.indexOf(END, startIdx);
18200
+ if (endIdx !== -1) {
18201
+ return src.slice(0, startIdx) + block + src.slice(endIdx + END.length);
18202
+ }
18203
+ }
18204
+ const sigIdx = src.indexOf(SIGNATURES_MARKER);
18205
+ if (sigIdx !== -1) {
18206
+ return src.slice(0, sigIdx) + block + '\n\n' + src.slice(sigIdx);
18207
+ }
18208
+ if (src.trim() === '') return block + '\n';
18209
+ return src + (src.endsWith('\n') ? '\n' : '\n\n') + block + '\n';
18210
+ }
18211
+
18212
+ function _writeIfChanged(filePath, content) {
18213
+ if (fs.existsSync(filePath)) {
18214
+ const existing = fs.readFileSync(filePath, 'utf8');
18215
+ if (existing === content) return 'already';
18216
+ fs.writeFileSync(filePath, content);
18217
+ return 'updated';
18218
+ }
18219
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
18220
+ fs.writeFileSync(filePath, content);
18221
+ return 'installed';
18222
+ }
18223
+
18224
+ /**
18225
+ * Install both skills for one client.
18226
+ * @returns {{ client, label, results: Array<{skill, path, status}> }}
18227
+ * status: 'installed' | 'updated' | 'already'; or { status:'unknown', valid } for a bad client.
18228
+ */
18229
+ function installSkills(client, opts = {}) {
18230
+ const spec = SKILL_CLIENTS[client];
18231
+ if (!spec) return { client, status: 'unknown', valid: Object.keys(SKILL_CLIENTS) };
18232
+ const cwd = opts.cwd || process.cwd();
18233
+ const version = opts.version || null;
18234
+ const results = [];
18235
+
18236
+ if (spec.inject) {
18237
+ const filePath = spec.target(cwd);
18238
+ const existing = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : '';
18239
+ const next = injectSkillsBlock(existing, renderAgentsBlock(version));
18240
+ let status;
18241
+ if (next === existing) status = 'already';
18242
+ else { fs.writeFileSync(filePath, next); status = existing ? 'updated' : 'installed'; }
18243
+ results.push({ skill: Object.keys(SKILLS).join(' + '), path: filePath, status });
18244
+ } else {
18245
+ for (const skillName of Object.keys(SKILLS)) {
18246
+ const filePath = spec.target(cwd, skillName);
18247
+ results.push({ skill: skillName, path: filePath, status: _writeIfChanged(filePath, renderSkill(client, skillName, version)) });
18248
+ }
18249
+ }
18250
+ return { client, label: spec.label, results };
18251
+ }
18252
+
18253
+ /** True when the client's parent artifact exists (plain-install eligibility). */
18254
+ function clientPresent(client, cwd) {
18255
+ const spec = SKILL_CLIENTS[client];
18256
+ return !!spec && fs.existsSync(path.join(cwd, ...spec.parent));
18257
+ }
18258
+
18259
+ /** List clients with target paths, presence, and installed state. */
18260
+ function listSkillClients(opts = {}) {
18261
+ const cwd = opts.cwd || process.cwd();
18262
+ return Object.keys(SKILL_CLIENTS).map((client) => {
18263
+ const spec = SKILL_CLIENTS[client];
18264
+ const targets = spec.inject
18265
+ ? [spec.target(cwd)]
18266
+ : Object.keys(SKILLS).map((s) => spec.target(cwd, s));
18267
+ const installed = spec.inject
18268
+ ? (fs.existsSync(targets[0]) && fs.readFileSync(targets[0], 'utf8').includes(START))
18269
+ : targets.every((t) => fs.existsSync(t));
18270
+ return { client, label: spec.label, present: clientPresent(client, cwd), installed, targets };
18271
+ });
18272
+ }
18273
+
18274
+ module.exports = { SKILLS, SKILL_CLIENTS, renderSkill, renderAgentsBlock, injectSkillsBlock, installSkills, listSkillClients, clientPresent, START, END };
18275
+
18276
+ };
18277
+
18092
18278
  // ── ./src/squeeze/cilog ──
18093
18279
  __factories["./src/squeeze/cilog"] = function(module, exports) {
18094
18280
 
@@ -20621,7 +20807,7 @@ function __tryGit(args, opts = {}) {
20621
20807
  catch (_) { return ''; }
20622
20808
  }
20623
20809
 
20624
- const VERSION = '8.25.0';
20810
+ const VERSION = '8.26.1';
20625
20811
  const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
20626
20812
 
20627
20813
  function requireSourceOrBundled(key) {
@@ -22511,6 +22697,8 @@ Usage:
22511
22697
  ${cmd} redact [file] Mask secrets in a file or stdin (10-pattern bank); redacted text to stdout (--json)
22512
22698
  ${cmd} tune Recommend config from repo detection — srcDirs, monorepo, adapters, exclude, budget (--json)
22513
22699
  ${cmd} tune --apply Write the recommendations into gen-context.config.json (merges; your keys preserved)
22700
+ ${cmd} skills list List skill clients (Claude/Cursor/Windsurf/Copilot/AGENTS.md) and install state (--json)
22701
+ ${cmd} skills install Install the SigMap agent playbooks for detected clients (--client <name> | --all)
22514
22702
  ${cmd} note "<text>" Append a note to the cross-session decision log
22515
22703
  ${cmd} note List recent notes (also: note --list <N>)
22516
22704
  ${cmd} status Show repo state — branch, dirty files, index freshness, notes
@@ -23940,6 +24128,64 @@ function main() {
23940
24128
  process.exit(0);
23941
24129
  }
23942
24130
 
24131
+ // `sigmap skills list|install [--client <name>|--all] [--json]` — install the
24132
+ // agent playbooks (usage-maximizer, config-optimizer) in each client's native
24133
+ // skill/rules format. Plain `install` touches only clients whose parent
24134
+ // artifact exists; --client/--all create. Idempotent.
24135
+ if (args[0] === 'skills') {
24136
+ const { installSkills, listSkillClients, SKILL_CLIENTS } = requireSourceOrBundled('./src/skills/skills');
24137
+ const sub = args[1];
24138
+ const jsonOut = args.includes('--json');
24139
+
24140
+ if (sub === 'list' || sub === undefined) {
24141
+ const clients = listSkillClients({ cwd });
24142
+ if (jsonOut) {
24143
+ process.stdout.write(JSON.stringify(clients, null, 2) + '\n');
24144
+ process.exit(0);
24145
+ }
24146
+ console.log('Supported skill clients:\n');
24147
+ for (const c of clients) {
24148
+ const state = c.installed ? 'installed' : (c.present ? 'client present, skills not installed' : 'client not detected');
24149
+ console.log(` ${c.client.padEnd(10)} ${c.label.padEnd(24)} ${state}`);
24150
+ for (const t of c.targets) console.log(` ${path.relative(cwd, t)}`);
24151
+ }
24152
+ console.log('\nInstall with: sigmap skills install [--client <name> | --all]');
24153
+ process.exit(0);
24154
+ }
24155
+
24156
+ if (sub === 'install') {
24157
+ const cIdx = args.indexOf('--client');
24158
+ const one = cIdx !== -1 ? args[cIdx + 1] : null;
24159
+ let targets;
24160
+ if (one) targets = [one];
24161
+ else if (args.includes('--all')) targets = Object.keys(SKILL_CLIENTS);
24162
+ else targets = listSkillClients({ cwd }).filter((c) => c.present).map((c) => c.client);
24163
+ if (targets.length === 0) {
24164
+ console.error('[sigmap] skills: no clients detected — use --client <name> or --all (see: sigmap skills list)');
24165
+ process.exit(1);
24166
+ }
24167
+ const out = targets.map((c) => installSkills(c, { cwd, version: VERSION }));
24168
+ const bad = out.find((r) => r.status === 'unknown');
24169
+ if (bad) {
24170
+ console.error(`[sigmap] skills: unknown client "${bad.client}" — valid: ${bad.valid.join(', ')}`);
24171
+ process.exit(1);
24172
+ }
24173
+ if (jsonOut) {
24174
+ process.stdout.write(JSON.stringify(out, null, 2) + '\n');
24175
+ process.exit(0);
24176
+ }
24177
+ for (const r of out) {
24178
+ for (const res of r.results) {
24179
+ console.log(` ${r.client.padEnd(10)} ${res.status.padEnd(10)} ${path.relative(cwd, res.path)}`);
24180
+ }
24181
+ }
24182
+ process.exit(0);
24183
+ }
24184
+
24185
+ console.error('[sigmap] usage: sigmap skills [list | install [--client <name> | --all]] [--json]');
24186
+ process.exit(1);
24187
+ }
24188
+
23943
24189
  // `sigmap note "<text>"` — append to the cross-session decision log.
23944
24190
  // With no text, lists recent notes (also `note --list [N]`).
23945
24191
  // `sigmap memory` — one view over the cross-session stores in .context/.
package/llms-full.txt CHANGED
@@ -11,13 +11,13 @@ ranking keeps the relevant context in scope (cutting tokens ~97% as a side
11
11
  effect), with no LLM calls, embeddings, or vector database. Works with Claude,
12
12
  Cursor, GitHub Copilot, Aider, Windsurf, local LLMs, and MCP.
13
13
 
14
- # Version: 8.25.0 | Benchmark: sigmap-v8.25-main (2026-08-17)
14
+ # Version: 8.26.1 | Benchmark: sigmap-v8.26-main (2026-08-17)
15
15
  # Source: auto-generated from package.json, version.json, benchmarks/latest.json, src/mcp/tools.js, src/config/defaults.js
16
16
  # Regenerate: npm run generate:llms | Validate: npm run validate:llms
17
17
 
18
18
  ---
19
19
 
20
- ## Core metrics (benchmark: sigmap-v8.25-main, 2026-08-17)
20
+ ## Core metrics (benchmark: sigmap-v8.26-main, 2026-08-17)
21
21
 
22
22
  | Metric | Without SigMap | With SigMap |
23
23
  |--------|----------------|-------------|
@@ -130,6 +130,8 @@ sigmap budget --budget <tokens> One-off budget override (config: sessio
130
130
  sigmap redact [file] Mask secrets in a file or stdin (10-pattern bank); redacted text to stdout (--json)
131
131
  sigmap tune Recommend config from repo detection — srcDirs, monorepo, adapters, exclude, budget (--json)
132
132
  sigmap tune --apply Write the recommendations into gen-context.config.json (merges; your keys preserved)
133
+ sigmap skills list List skill clients (Claude/Cursor/Windsurf/Copilot/AGENTS.md) and install state (--json)
134
+ sigmap skills install Install the SigMap agent playbooks for detected clients (--client <name> | --all)
133
135
  sigmap note "<text>" Append a note to the cross-session decision log
134
136
  sigmap note List recent notes (also: note --list <N>)
135
137
  sigmap status Show repo state — branch, dirty files, index freshness, notes
package/llms.txt CHANGED
@@ -11,7 +11,7 @@ ranking keeps the relevant context in scope (cutting tokens ~97% as a side
11
11
  effect), with no LLM calls, embeddings, or vector database. Works with Claude,
12
12
  Cursor, GitHub Copilot, Aider, Windsurf, local LLMs, and MCP.
13
13
 
14
- # Version: 8.25.0 | Benchmark: sigmap-v8.25-main (2026-08-17)
14
+ # Version: 8.26.1 | Benchmark: sigmap-v8.26-main (2026-08-17)
15
15
  # Source: auto-generated from package.json, version.json, benchmarks/latest.json, src/mcp/tools.js, src/config/defaults.js
16
16
  # Regenerate: npm run generate:llms | Validate: npm run validate:llms
17
17
 
@@ -23,7 +23,7 @@ Cursor, GitHub Copilot, Aider, Windsurf, local LLMs, and MCP.
23
23
  - No blast-radius awareness before editing a hub file — `--impact` shows every file a change touches.
24
24
  - Pasted stack traces, CI logs, and JSON bloat the prompt — `squeeze` minimizes them and enriches the top frame from the symbol index.
25
25
 
26
- ## Core metrics (benchmark: sigmap-v8.25-main, 2026-08-17)
26
+ ## Core metrics (benchmark: sigmap-v8.26-main, 2026-08-17)
27
27
 
28
28
  - hit@5 retrieval: 82.2% vs 44.0% single-shot grep baseline (1.87× lift)
29
29
  - Token reduction: 96.8% average across benchmark repos
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigmap",
3
- "version": "8.25.0",
3
+ "version": "8.26.1",
4
4
  "description": "The deterministic, verifiable grounding layer for AI code work — a zero-dependency signature-and-evidence map that grounds Claude, Cursor, Copilot, Aider, Windsurf, local LLMs & MCP agents against your real code (repo + installed libraries) so they stop hallucinating files, imports & APIs. Runs offline via npx; byte-stable output; ~97% token reduction as proof.",
5
5
  "main": "packages/core/index.js",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigmap-cli",
3
- "version": "8.25.0",
3
+ "version": "8.26.1",
4
4
  "description": "SigMap CLI wrapper — thin adapter for programmatic CLI invocation",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigmap-core",
3
- "version": "8.25.0",
3
+ "version": "8.26.1",
4
4
  "description": "SigMap core library — zero-dependency code signature extraction, retrieval, and security scanning",
5
5
  "main": "index.js",
6
6
  "keywords": [
package/src/mcp/server.js CHANGED
@@ -18,7 +18,7 @@ const { readContext, searchSignatures, getMap, createCheckpoint, getRouting, exp
18
18
 
19
19
  const SERVER_INFO = {
20
20
  name: 'sigmap',
21
- version: '8.25.0',
21
+ version: '8.26.1',
22
22
  description: 'SigMap MCP server — code signatures on demand',
23
23
  };
24
24
 
@@ -0,0 +1,182 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * sigmap skills — canonical agent playbooks + multi-client installer (F3+F4, #517).
5
+ *
6
+ * Two deterministic skill documents (the spend-minimizing usage loop and the
7
+ * config-optimizer playbook) emitted in each client's native skill/rules
8
+ * format — the `mcp/install.js` CLIENTS pattern applied to skills. File-based
9
+ * clients get sigmap-namespaced files we own outright; AGENTS.md (codex) gets
10
+ * a marker-delimited block inserted ABOVE the `## Auto-generated signatures`
11
+ * marker, since the codex adapter preserves content above it and replaces
12
+ * everything below. Idempotent; human content is never touched.
13
+ */
14
+
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+
18
+ const START = '<!-- sigmap-skills:start -->';
19
+ const END = '<!-- sigmap-skills:end -->';
20
+ const SIGNATURES_MARKER = '## Auto-generated signatures';
21
+
22
+ const SKILLS = {
23
+ 'sigmap-usage-maximizer': {
24
+ title: 'SigMap usage maximizer',
25
+ description: 'Spend-minimizing loop for agents in a SigMap-indexed repo: ask before reading, read ranges, verify before trusting, squeeze big pastes, checkpoint, watch the budget.',
26
+ body: [
27
+ 'Follow this loop before any file exploration in a repo with SigMap installed.',
28
+ '',
29
+ '1. **Ask before reading.** `sigmap ask "<task>"` (or the `query_context` MCP tool) ranks the relevant files as ~hundreds of tokens of signatures instead of thousands of raw-file tokens. Never open files to "look around".',
30
+ '2. **Read ranges, not files.** Use the `get_lines` MCP tool with the `:start-end` line anchors carried on every signature to pull only the lines you need.',
31
+ '3. **Ground before trusting.** Run the `verify_suggestion` MCP tool (or `sigmap verify-ai-output`) on generated code before applying it — it flags fabricated files, imports, symbols, and npm scripts against the live index.',
32
+ '4. **Squeeze big pastes.** Any stack trace, CI/build log, or JSON blob goes through `sigmap squeeze` (or the `squeeze_output` MCP tool) before it enters context — the signal survives, the noise does not.',
33
+ '5. **Checkpoint progress.** Use the `create_checkpoint` MCP tool or `sigmap note "<decision>"` so a follow-up session resumes without re-deriving state.',
34
+ '6. **Watch the budget.** Check the `get_budget` MCP tool or `sigmap budget` (estimates from SigMap\'s local ledger — no LLM calls). Near the budget: summarize-then-drop older context instead of accumulating, and prefer terse output.',
35
+ ].join('\n'),
36
+ },
37
+ 'sigmap-config-optimizer': {
38
+ title: 'SigMap config optimizer',
39
+ description: 'Playbook for getting a correct SigMap config on any repo: detect with sigmap tune, review the per-change reasons, apply, validate.',
40
+ body: [
41
+ 'Playbook for configuring SigMap on a new or misconfigured repo.',
42
+ '',
43
+ '1. **Detect.** `sigmap tune` prints a recommended config diff — srcDirs pin, monorepo mode, adapters, excludes, budget — with one evidence-naming reason per change. Read-only.',
44
+ '2. **Review.** Every reason names its evidence (workspace marker, client artifact, vendored dir, file-count estimate). Explicit user config is never proposed against.',
45
+ '3. **Apply.** `sigmap tune --apply` merges the recommendations into `gen-context.config.json`, preserving every existing key. Idempotent — a second `tune` proposes nothing.',
46
+ '4. **Validate.** `sigmap validate` confirms the generated context matches the config; `sigmap doctor` diagnoses anything left.',
47
+ ].join('\n'),
48
+ },
49
+ };
50
+
51
+ // Client registry — parent = artifact whose presence means "user uses this
52
+ // client" (the `--setup` only-touch-existing rule for plain `skills install`).
53
+ const SKILL_CLIENTS = {
54
+ claude: { label: 'Claude Code', parent: ['.claude'],
55
+ target: (cwd, skill) => path.join(cwd, '.claude', 'skills', skill, 'SKILL.md') },
56
+ cursor: { label: 'Cursor', parent: ['.cursor'],
57
+ target: (cwd, skill) => path.join(cwd, '.cursor', 'rules', `${skill}.mdc`) },
58
+ windsurf: { label: 'Windsurf', parent: ['.windsurf'],
59
+ target: (cwd, skill) => path.join(cwd, '.windsurf', 'rules', `${skill}.md`) },
60
+ copilot: { label: 'GitHub Copilot', parent: ['.github'],
61
+ target: (cwd, skill) => path.join(cwd, '.github', 'instructions', `${skill}.instructions.md`) },
62
+ codex: { label: 'Codex CLI (AGENTS.md)', parent: ['AGENTS.md'],
63
+ target: (cwd) => path.join(cwd, 'AGENTS.md'), inject: true },
64
+ };
65
+
66
+ function _footer(version) {
67
+ const ver = version ? ` v${version}` : '';
68
+ return `<sub>Generated by SigMap${ver} · run \`sigmap skills install\` to refresh.</sub>`;
69
+ }
70
+
71
+ /** Render one skill's client-specific file content. */
72
+ function renderSkill(client, skillName, version) {
73
+ const skill = SKILLS[skillName];
74
+ const body = `# ${skill.title}\n\n${skill.body}\n\n${_footer(version)}\n`;
75
+ if (client === 'claude') {
76
+ return `---\nname: ${skillName}\ndescription: ${skill.description}\n---\n\n${body}`;
77
+ }
78
+ if (client === 'cursor') {
79
+ return `---\ndescription: ${skill.description}\nalwaysApply: false\n---\n\n${body}`;
80
+ }
81
+ if (client === 'copilot') {
82
+ return `---\napplyTo: "**"\n---\n\n${body}`;
83
+ }
84
+ return body; // windsurf: plain markdown
85
+ }
86
+
87
+ /** Render the combined AGENTS.md block (both skills, marker-delimited). */
88
+ function renderAgentsBlock(version) {
89
+ const parts = [START, '## SigMap agent skills', ''];
90
+ for (const name of Object.keys(SKILLS)) {
91
+ parts.push(`### ${SKILLS[name].title}`, '', SKILLS[name].body, '');
92
+ }
93
+ parts.push(_footer(version), END);
94
+ return parts.join('\n');
95
+ }
96
+
97
+ /**
98
+ * Inject (or replace) the skills block in AGENTS.md content.
99
+ * A new block is inserted ABOVE the `## Auto-generated signatures` marker —
100
+ * the codex adapter's write() preserves content above that marker and
101
+ * replaces everything below it. Never touches content outside the markers.
102
+ */
103
+ function injectSkillsBlock(existing, block) {
104
+ const src = String(existing || '');
105
+ const startIdx = src.indexOf(START);
106
+ if (startIdx !== -1) {
107
+ const endIdx = src.indexOf(END, startIdx);
108
+ if (endIdx !== -1) {
109
+ return src.slice(0, startIdx) + block + src.slice(endIdx + END.length);
110
+ }
111
+ }
112
+ const sigIdx = src.indexOf(SIGNATURES_MARKER);
113
+ if (sigIdx !== -1) {
114
+ return src.slice(0, sigIdx) + block + '\n\n' + src.slice(sigIdx);
115
+ }
116
+ if (src.trim() === '') return block + '\n';
117
+ return src + (src.endsWith('\n') ? '\n' : '\n\n') + block + '\n';
118
+ }
119
+
120
+ function _writeIfChanged(filePath, content) {
121
+ if (fs.existsSync(filePath)) {
122
+ const existing = fs.readFileSync(filePath, 'utf8');
123
+ if (existing === content) return 'already';
124
+ fs.writeFileSync(filePath, content);
125
+ return 'updated';
126
+ }
127
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
128
+ fs.writeFileSync(filePath, content);
129
+ return 'installed';
130
+ }
131
+
132
+ /**
133
+ * Install both skills for one client.
134
+ * @returns {{ client, label, results: Array<{skill, path, status}> }}
135
+ * status: 'installed' | 'updated' | 'already'; or { status:'unknown', valid } for a bad client.
136
+ */
137
+ function installSkills(client, opts = {}) {
138
+ const spec = SKILL_CLIENTS[client];
139
+ if (!spec) return { client, status: 'unknown', valid: Object.keys(SKILL_CLIENTS) };
140
+ const cwd = opts.cwd || process.cwd();
141
+ const version = opts.version || null;
142
+ const results = [];
143
+
144
+ if (spec.inject) {
145
+ const filePath = spec.target(cwd);
146
+ const existing = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : '';
147
+ const next = injectSkillsBlock(existing, renderAgentsBlock(version));
148
+ let status;
149
+ if (next === existing) status = 'already';
150
+ else { fs.writeFileSync(filePath, next); status = existing ? 'updated' : 'installed'; }
151
+ results.push({ skill: Object.keys(SKILLS).join(' + '), path: filePath, status });
152
+ } else {
153
+ for (const skillName of Object.keys(SKILLS)) {
154
+ const filePath = spec.target(cwd, skillName);
155
+ results.push({ skill: skillName, path: filePath, status: _writeIfChanged(filePath, renderSkill(client, skillName, version)) });
156
+ }
157
+ }
158
+ return { client, label: spec.label, results };
159
+ }
160
+
161
+ /** True when the client's parent artifact exists (plain-install eligibility). */
162
+ function clientPresent(client, cwd) {
163
+ const spec = SKILL_CLIENTS[client];
164
+ return !!spec && fs.existsSync(path.join(cwd, ...spec.parent));
165
+ }
166
+
167
+ /** List clients with target paths, presence, and installed state. */
168
+ function listSkillClients(opts = {}) {
169
+ const cwd = opts.cwd || process.cwd();
170
+ return Object.keys(SKILL_CLIENTS).map((client) => {
171
+ const spec = SKILL_CLIENTS[client];
172
+ const targets = spec.inject
173
+ ? [spec.target(cwd)]
174
+ : Object.keys(SKILLS).map((s) => spec.target(cwd, s));
175
+ const installed = spec.inject
176
+ ? (fs.existsSync(targets[0]) && fs.readFileSync(targets[0], 'utf8').includes(START))
177
+ : targets.every((t) => fs.existsSync(t));
178
+ return { client, label: spec.label, present: clientPresent(client, cwd), installed, targets };
179
+ });
180
+ }
181
+
182
+ module.exports = { SKILLS, SKILL_CLIENTS, renderSkill, renderAgentsBlock, injectSkillsBlock, installSkills, listSkillClients, clientPresent, START, END };