blitzstrike 1.0.36 → 1.0.37

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/index.js +289 -68
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,21 @@ All notable changes to Blitz Strike are documented here.
5
5
  The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
+ ## [1.0.37] — 2026-09-13
9
+
10
+ ### Added
11
+
12
+ - **Phase 4 — Differential security analysis** (`src/differential.ts`):
13
+ - `analyzeDifferential(old, new, language)` — compares two code versions and
14
+ surfaces only security-sensitive changes:
15
+ - **new vulnerability** (source→sink path present only in new code — authoritative, engine-backed)
16
+ - **fixed vulnerability** (path present only in old code)
17
+ - **new sink** / **new source** (line-level hints)
18
+ - **sanitizer removed** / **authorization removed** (high-severity signals)
19
+ - `analyzeDifferentialFiles(old_path, new_path)` — file-based comparison.
20
+ - `analyzeGitDiff(repo, base, head)` — runs across a git commit range.
21
+ - New MCP tool `diff_analyze` (supports file-pair, code-pair, or git-diff mode).
22
+
8
23
  ## [1.0.36] — 2026-09-13
9
24
 
10
25
  ### Added
package/dist/index.js CHANGED
@@ -14820,8 +14820,8 @@ var require_src = __commonJS(function(exports, module) {
14820
14820
  });
14821
14821
 
14822
14822
  // src/sync.ts
14823
- import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3, readFileSync as readFileSync14, readdirSync as readdirSync5, statSync as statSync3, rmSync as rmSync2 } from "node:fs";
14824
- import { join as join10 } from "node:path";
14823
+ import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4, readFileSync as readFileSync15, readdirSync as readdirSync5, statSync as statSync3, rmSync as rmSync3 } from "node:fs";
14824
+ import { join as join11 } from "node:path";
14825
14825
  import { homedir as homedir4 } from "node:os";
14826
14826
  import { fileURLToPath as fileURLToPath7 } from "node:url";
14827
14827
  import { spawnSync as spawnSync2 } from "node:child_process";
@@ -14838,7 +14838,7 @@ function countFiles(dir) {
14838
14838
  const walk = (d) => {
14839
14839
  for (const e of readdirSync5(d, { withFileTypes: true })) {
14840
14840
  if (e.isDirectory())
14841
- walk(join10(d, e.name));
14841
+ walk(join11(d, e.name));
14842
14842
  else
14843
14843
  n++;
14844
14844
  }
@@ -14852,8 +14852,8 @@ function countFiles(dir) {
14852
14852
  function syncData() {
14853
14853
  if (!dirExists(DATA_ROOT2))
14854
14854
  mkdirSync3(DATA_ROOT2, { recursive: true });
14855
- const payloadsPath = join10(DATA_ROOT2, "payloads");
14856
- const templatesPath = join10(DATA_ROOT2, "templates");
14855
+ const payloadsPath = join11(DATA_ROOT2, "payloads");
14856
+ const templatesPath = join11(DATA_ROOT2, "templates");
14857
14857
  const hasPayloads = dirExists(payloadsPath) && countFiles(payloadsPath) > 0;
14858
14858
  const hasTemplates = dirExists(templatesPath) && countFiles(templatesPath) > 0;
14859
14859
  if (hasPayloads && hasTemplates) {
@@ -14863,8 +14863,8 @@ function syncData() {
14863
14863
  data_root: DATA_ROOT2
14864
14864
  };
14865
14865
  }
14866
- const localPayloads = join10(ROOT5, "payloads");
14867
- const localTemplates = join10(ROOT5, "templates");
14866
+ const localPayloads = join11(ROOT5, "payloads");
14867
+ const localTemplates = join11(ROOT5, "templates");
14868
14868
  let payloadsSynced = false;
14869
14869
  let templatesSynced = false;
14870
14870
  if (!hasPayloads && dirExists(localPayloads)) {
@@ -14877,23 +14877,23 @@ function syncData() {
14877
14877
  }
14878
14878
  const gitOk = spawnSync2("git", ["--version"]).status === 0;
14879
14879
  if (gitOk && (!dirExists(payloadsPath) || countFiles(payloadsPath) === 0 || !dirExists(templatesPath) || countFiles(templatesPath) === 0)) {
14880
- const tmp = join10(DATA_ROOT2, ".tmp-clone");
14880
+ const tmp = join11(DATA_ROOT2, ".tmp-clone");
14881
14881
  if (dirExists(tmp))
14882
- rmSync2(tmp, { recursive: true, force: true });
14882
+ rmSync3(tmp, { recursive: true, force: true });
14883
14883
  const r = spawnSync2("git", ["clone", "--depth", "1", "--filter=blob:none", "--sparse", GITHUB_REPO, tmp], { timeout: 120000 });
14884
14884
  if (r.status === 0) {
14885
14885
  const sparse = spawnSync2("git", ["-C", tmp, "sparse-checkout", "set", "payloads", "templates"], { timeout: 120000 });
14886
14886
  if (sparse.status === 0) {
14887
14887
  if (!dirExists(payloadsPath) || countFiles(payloadsPath) === 0) {
14888
- copyDir(join10(tmp, "payloads"), payloadsPath);
14888
+ copyDir(join11(tmp, "payloads"), payloadsPath);
14889
14889
  payloadsSynced = true;
14890
14890
  }
14891
14891
  if (!dirExists(templatesPath) || countFiles(templatesPath) === 0) {
14892
- copyDir(join10(tmp, "templates"), templatesPath);
14892
+ copyDir(join11(tmp, "templates"), templatesPath);
14893
14893
  templatesSynced = true;
14894
14894
  }
14895
14895
  }
14896
- rmSync2(tmp, { recursive: true, force: true });
14896
+ rmSync3(tmp, { recursive: true, force: true });
14897
14897
  }
14898
14898
  }
14899
14899
  return {
@@ -14911,18 +14911,18 @@ function copyDir(src, dst) {
14911
14911
  function manualCopy(src, dst) {
14912
14912
  mkdirSync3(dst, { recursive: true });
14913
14913
  for (const e of readdirSync5(src, { withFileTypes: true })) {
14914
- const s = join10(src, e.name);
14915
- const d = join10(dst, e.name);
14914
+ const s = join11(src, e.name);
14915
+ const d = join11(dst, e.name);
14916
14916
  if (e.isDirectory())
14917
14917
  manualCopy(s, d);
14918
14918
  else
14919
- writeFileSync3(d, readFileSync14(s));
14919
+ writeFileSync4(d, readFileSync15(s));
14920
14920
  }
14921
14921
  }
14922
14922
  var ROOT5, DATA_ROOT2, GITHUB_REPO = "https://github.com/shinthink/blitzstrike.git";
14923
14923
  var init_sync = __esm(() => {
14924
- ROOT5 = join10(fileURLToPath7(new URL(".", import.meta.url)), "..");
14925
- DATA_ROOT2 = process.env.BLITZSTRIKE_DATA ?? join10(homedir4(), ".blitzstrike", "data");
14924
+ ROOT5 = join11(fileURLToPath7(new URL(".", import.meta.url)), "..");
14925
+ DATA_ROOT2 = process.env.BLITZSTRIKE_DATA ?? join11(homedir4(), ".blitzstrike", "data");
14926
14926
  });
14927
14927
 
14928
14928
  // src/update.ts
@@ -32326,6 +32326,12 @@ function analyzeDataFlow2(path) {
32326
32326
  return result;
32327
32327
  }
32328
32328
 
32329
+ // src/differential.ts
32330
+ import { writeFileSync as writeFileSync2, rmSync, mkdtempSync, readFileSync as readFileSync13 } from "node:fs";
32331
+ import { join as join9 } from "node:path";
32332
+ import { tmpdir } from "node:os";
32333
+ import { execSync } from "node:child_process";
32334
+
32329
32335
  // src/universal-taint.ts
32330
32336
  function runTaintEngine(adapter, code, file) {
32331
32337
  const result = { file, language: adapter.language, findings: [], suppressed: 0 };
@@ -50712,6 +50718,193 @@ registerLanguage(jsAdapter);
50712
50718
  registerLanguage(pythonAdapter);
50713
50719
  registerLanguage(javaAdapter);
50714
50720
 
50721
+ // src/differential.ts
50722
+ init_scanner();
50723
+ function runEngine(code, language, file) {
50724
+ if (language === "php") {
50725
+ const dir = mkdtempSync(join9(tmpdir(), "blitz-diff-"));
50726
+ const tmp = join9(dir, "old.php");
50727
+ writeFileSync2(tmp, code);
50728
+ try {
50729
+ const r = analyzeDataFlow2(tmp);
50730
+ return r.findings.map((f) => ({ sink: f.sink, sink_line: f.sink_line, category: f.category, source: f.source, cwe: f.cwe }));
50731
+ } finally {
50732
+ rmSync(dir, { recursive: true, force: true });
50733
+ }
50734
+ }
50735
+ const r = analyzeTaintUniversal(code, file);
50736
+ return r.findings.map((f) => ({ sink: f.sink, sink_line: f.sink_line, category: f.sink, source: f.source_kind, cwe: f.cwe }));
50737
+ }
50738
+ function diffLines(oldText, newText) {
50739
+ const a = oldText.split(`
50740
+ `);
50741
+ const b = newText.split(`
50742
+ `);
50743
+ const m = a.length;
50744
+ const n = b.length;
50745
+ const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
50746
+ for (let i = m - 1;i >= 0; i--) {
50747
+ for (let j = n - 1;j >= 0; j--) {
50748
+ dp[i][j] = a[i] === b[j] ? dp[i + 1][j + 1] + 1 : Math.max(dp[i + 1][j], dp[i][j + 1]);
50749
+ }
50750
+ }
50751
+ const added = [];
50752
+ const removed = [];
50753
+ let i = 0;
50754
+ let j = 0;
50755
+ while (i < m && j < n) {
50756
+ if (a[i] === b[j]) {
50757
+ i++;
50758
+ j++;
50759
+ } else if (dp[i + 1][j] >= dp[i][j + 1]) {
50760
+ removed.push({ line: i + 1, text: a[i] });
50761
+ i++;
50762
+ } else {
50763
+ added.push({ line: j + 1, text: b[j] });
50764
+ j++;
50765
+ }
50766
+ }
50767
+ while (i < m) {
50768
+ removed.push({ line: i + 1, text: a[i] });
50769
+ i++;
50770
+ }
50771
+ while (j < n) {
50772
+ added.push({ line: j + 1, text: b[j] });
50773
+ j++;
50774
+ }
50775
+ return { added, removed };
50776
+ }
50777
+ function analyzeDifferential(oldCode, newCode, language, file = "target") {
50778
+ const changes = [];
50779
+ const oldFindings = runEngine(oldCode, language, file);
50780
+ const newFindings = runEngine(newCode, language, file);
50781
+ const key = (f) => `${f.category}|${f.sink}|${f.sink_line}`;
50782
+ const oldKeys = new Set(oldFindings.map(key));
50783
+ const newKeys = new Set(newFindings.map(key));
50784
+ for (const f of newFindings) {
50785
+ if (!oldKeys.has(key(f))) {
50786
+ changes.push({
50787
+ kind: "new_vulnerability",
50788
+ severity: "high",
50789
+ sink: f.sink,
50790
+ category: f.category,
50791
+ source: f.source,
50792
+ cwe: f.cwe,
50793
+ line: f.sink_line,
50794
+ detail: `new ${f.category} at line ${f.sink_line} (${f.sink.trim()})`
50795
+ });
50796
+ }
50797
+ }
50798
+ for (const f of oldFindings) {
50799
+ if (!newKeys.has(key(f))) {
50800
+ changes.push({
50801
+ kind: "fixed_vulnerability",
50802
+ severity: "low",
50803
+ sink: f.sink,
50804
+ category: f.category,
50805
+ source: f.source,
50806
+ cwe: f.cwe,
50807
+ line: f.sink_line,
50808
+ detail: `removed ${f.category} at line ${f.sink_line} (${f.sink.trim()})`
50809
+ });
50810
+ }
50811
+ }
50812
+ const { added, removed } = diffLines(oldCode, newCode);
50813
+ for (const l of added) {
50814
+ const sink = classifySink2(l.text);
50815
+ if (sink) {
50816
+ changes.push({
50817
+ kind: "new_sink",
50818
+ severity: "medium",
50819
+ sink: l.text.trim(),
50820
+ category: sink.category,
50821
+ cwe: sink.cwe,
50822
+ line: l.line,
50823
+ detail: `added sink ${l.text.trim()} (${sink.category})`
50824
+ });
50825
+ }
50826
+ const source = classifySource(l.text);
50827
+ if (source) {
50828
+ changes.push({
50829
+ kind: "new_source",
50830
+ severity: source.attacker_controlled ? "medium" : "low",
50831
+ source: source.label,
50832
+ line: l.line,
50833
+ detail: `added source ${l.text.trim()} (${source.label})`
50834
+ });
50835
+ }
50836
+ }
50837
+ for (const l of removed) {
50838
+ const san = findSanitizers(l.text);
50839
+ if (san.length > 0) {
50840
+ changes.push({
50841
+ kind: "sanitizer_removed",
50842
+ severity: "high",
50843
+ line: l.line,
50844
+ detail: `removed sanitizer ${san.map((s) => s.label).join(", ")} (${l.text.trim()})`
50845
+ });
50846
+ }
50847
+ const gates = AUTH_GATES.filter((g) => l.text.includes(g));
50848
+ if (gates.length > 0) {
50849
+ changes.push({
50850
+ kind: "authorization_removed",
50851
+ severity: "high",
50852
+ line: l.line,
50853
+ detail: `removed authorization gate ${gates.join(", ")} (${l.text.trim()})`
50854
+ });
50855
+ }
50856
+ }
50857
+ const rank = { high: 0, medium: 1, low: 2 };
50858
+ changes.sort((x, y) => rank[x.severity] - rank[y.severity] || x.line - y.line);
50859
+ const count = (k) => changes.filter((c) => c.kind === k).length;
50860
+ return {
50861
+ language,
50862
+ changes,
50863
+ summary: {
50864
+ new_vulnerabilities: count("new_vulnerability"),
50865
+ fixed_vulnerabilities: count("fixed_vulnerability"),
50866
+ new_sinks: count("new_sink"),
50867
+ new_sources: count("new_source"),
50868
+ sanitizers_removed: count("sanitizer_removed"),
50869
+ authorizations_removed: count("authorization_removed")
50870
+ }
50871
+ };
50872
+ }
50873
+ function languageOf(file) {
50874
+ const adapter = detectLanguage(file);
50875
+ return adapter?.language ?? "php";
50876
+ }
50877
+ function analyzeDifferentialFiles(oldPath, newPath, language) {
50878
+ const oldCode = readFileSync13(oldPath, "utf8");
50879
+ const newCode = readFileSync13(newPath, "utf8");
50880
+ const lang = language ?? languageOf(newPath);
50881
+ return analyzeDifferential(oldCode, newCode, lang, newPath);
50882
+ }
50883
+ var SOURCE_EXTS = [".php", ".phtml", ".php5", ".js", ".mjs", ".ts", ".tsx", ".py", ".pyw", ".java"];
50884
+ function analyzeGitDiff(repo, base = "HEAD~1", head = "HEAD") {
50885
+ const git = (args) => {
50886
+ try {
50887
+ return execSync(`git ${args}`, { cwd: repo, encoding: "utf8", maxBuffer: 50 * 1024 * 1024 }).trim();
50888
+ } catch {
50889
+ return "";
50890
+ }
50891
+ };
50892
+ const changed = git(`diff --name-only ${base} ${head}`).split(`
50893
+ `).filter((f) => f && SOURCE_EXTS.some((e) => f.endsWith(e)));
50894
+ const files = [];
50895
+ for (const file of changed) {
50896
+ const oldCode = git(`show ${base}:${file}`);
50897
+ const newCode = git(`show ${head}:${file}`);
50898
+ if (!newCode)
50899
+ continue;
50900
+ const lang = languageOf(file);
50901
+ files.push({ file, result: analyzeDifferential(oldCode || "", newCode, lang, file) });
50902
+ }
50903
+ const total_changes = files.reduce((n, f) => n + f.result.changes.length, 0);
50904
+ const security_sensitive = files.reduce((n, f) => n + f.result.changes.filter((c) => c.severity === "high").length, 0);
50905
+ return { repo, base, head, files, total_changes, security_sensitive };
50906
+ }
50907
+
50715
50908
  // src/guidance.ts
50716
50909
  var SINK_DIRECTIVES = {
50717
50910
  sql_execution: "Validate the SQL injection live with strike_verify (use a unique MARKER and a NEGATIVE CONTROL that stays inert), or read_tool_manual for sqlmap. Only a reflected marker + inert control = confirmed.",
@@ -51525,16 +51718,16 @@ function reportJson(findings, opts = {}) {
51525
51718
  }
51526
51719
 
51527
51720
  // src/benchmark.ts
51528
- import { readFileSync as readFileSync13, readdirSync as readdirSync4, writeFileSync as writeFileSync2, rmSync } from "node:fs";
51529
- import { join as join9 } from "node:path";
51721
+ import { readFileSync as readFileSync14, readdirSync as readdirSync4, writeFileSync as writeFileSync3, rmSync as rmSync2 } from "node:fs";
51722
+ import { join as join10 } from "node:path";
51530
51723
  import { fileURLToPath as fileURLToPath6 } from "node:url";
51531
- import { tmpdir } from "node:os";
51532
- var ROOT4 = join9(fileURLToPath6(new URL(".", import.meta.url)), "..");
51533
- var CORPUS = join9(ROOT4, "bench", "corpus");
51724
+ import { tmpdir as tmpdir2 } from "node:os";
51725
+ var ROOT4 = join10(fileURLToPath6(new URL(".", import.meta.url)), "..");
51726
+ var CORPUS = join10(ROOT4, "bench", "corpus");
51534
51727
  function parseCorpusFile(path) {
51535
51728
  let text;
51536
51729
  try {
51537
- text = readFileSync13(path, "utf8");
51730
+ text = readFileSync14(path, "utf8");
51538
51731
  } catch {
51539
51732
  return null;
51540
51733
  }
@@ -51584,7 +51777,7 @@ function loadCorpus() {
51584
51777
  for (const f of readdirSync4(CORPUS)) {
51585
51778
  if (!f.endsWith(".md"))
51586
51779
  continue;
51587
- const e = parseCorpusFile(join9(CORPUS, f));
51780
+ const e = parseCorpusFile(join10(CORPUS, f));
51588
51781
  if (e)
51589
51782
  entries.push(e);
51590
51783
  }
@@ -51596,10 +51789,10 @@ function loadCorpus() {
51596
51789
  function detect(entry) {
51597
51790
  const lang = entry.language;
51598
51791
  if (lang === "php") {
51599
- const tmp = join9(tmpdir(), `blitzstrike-bench-${entry.id}-${process.pid}.php`);
51600
- writeFileSync2(tmp, entry.code);
51792
+ const tmp = join10(tmpdir2(), `blitzstrike-bench-${entry.id}-${process.pid}.php`);
51793
+ writeFileSync3(tmp, entry.code);
51601
51794
  const r = analyzeDataFlow2(tmp);
51602
- rmSync(tmp, { force: true });
51795
+ rmSync2(tmp, { force: true });
51603
51796
  if (r.findings.length > 0)
51604
51797
  return { detected: true, sinkKind: r.findings[0].sink };
51605
51798
  return { detected: false };
@@ -52414,6 +52607,34 @@ function createServer() {
52414
52607
  }]
52415
52608
  };
52416
52609
  });
52610
+ server.registerTool("diff_analyze", {
52611
+ title: "Differential security analysis",
52612
+ description: "PHASE 4: compare two versions of code (files or a git commit range) and surface only security-sensitive changes — new vulnerabilities, fixed vulnerabilities, new sinks/sources, removed sanitizers, and removed authorization gates.",
52613
+ inputSchema: {
52614
+ old_path: string2().optional().describe("Old file path"),
52615
+ new_path: string2().optional().describe("New file path"),
52616
+ old_code: string2().optional().describe("Old source code (when not using paths)"),
52617
+ new_code: string2().optional().describe("New source code (when not using paths)"),
52618
+ language: string2().optional().describe("Language (php/javascript/python/java); auto-detected from path if omitted"),
52619
+ repo: string2().optional().describe("Git repo path (for git-diff mode)"),
52620
+ base: string2().optional().describe("Git base ref (default HEAD~1)"),
52621
+ head: string2().optional().describe("Git head ref (default HEAD)")
52622
+ }
52623
+ }, async ({ old_path, new_path, old_code, new_code, language, repo, base, head }) => {
52624
+ if (repo) {
52625
+ const r = analyzeGitDiff(repo, base ?? "HEAD~1", head ?? "HEAD");
52626
+ return { content: [{ type: "text", text: JSON.stringify(r) }] };
52627
+ }
52628
+ let result;
52629
+ if (old_path && new_path) {
52630
+ result = analyzeDifferentialFiles(old_path, new_path, language);
52631
+ } else if (old_code !== undefined && new_code !== undefined) {
52632
+ result = analyzeDifferential(old_code, new_code, language ?? "php", new_path ?? "target");
52633
+ } else {
52634
+ return { content: [{ type: "text", text: JSON.stringify({ error: "provide (old_path+new_path), (old_code+new_code), or (repo)" }) }] };
52635
+ }
52636
+ return { content: [{ type: "text", text: JSON.stringify(result) }] };
52637
+ });
52417
52638
  server.registerTool("taint_tree", {
52418
52639
  title: "Whole-tree taint scan",
52419
52640
  description: "EAGLE-EYE: run inter-procedural taint analysis across an entire source tree.",
@@ -52567,12 +52788,12 @@ async function serve() {
52567
52788
  }
52568
52789
 
52569
52790
  // src/cli.ts
52570
- import { readFileSync as readFileSync15, existsSync as existsSync6, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4 } from "node:fs";
52571
- import { join as join11, dirname as dirname2 } from "node:path";
52791
+ import { readFileSync as readFileSync16, existsSync as existsSync6, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4 } from "node:fs";
52792
+ import { join as join12, dirname as dirname2 } from "node:path";
52572
52793
  import { homedir as homedir5 } from "node:os";
52573
52794
  import { fileURLToPath as fileURLToPath8 } from "node:url";
52574
52795
  import { spawnSync as spawnSync3 } from "node:child_process";
52575
- var ROOT6 = join11(fileURLToPath8(new URL(".", import.meta.url)), "..");
52796
+ var ROOT6 = join12(fileURLToPath8(new URL(".", import.meta.url)), "..");
52576
52797
  function which(cmd) {
52577
52798
  return spawnSync3(`command -v ${cmd}`, { shell: true }).status === 0;
52578
52799
  }
@@ -52607,7 +52828,7 @@ function runDoctor() {
52607
52828
  detail: fofa ? "set" : "FOFA_EMAIL/FOFA_KEY not set",
52608
52829
  fix: fofa ? undefined : "export FOFA_EMAIL=... && export FOFA_KEY=... (enables fofa_search)"
52609
52830
  });
52610
- const dataOk = existsSync6(join11(ROOT6, "chains.json")) && existsSync6(join11(ROOT6, "tools-catalog.json"));
52831
+ const dataOk = existsSync6(join12(ROOT6, "chains.json")) && existsSync6(join12(ROOT6, "tools-catalog.json"));
52611
52832
  issues.push({
52612
52833
  name: "Data layers (chains + tools-catalog)",
52613
52834
  status: dataOk ? "ok" : "fail",
@@ -52635,21 +52856,21 @@ BlitzStrike — ${verdict}`);
52635
52856
  function resolveCommand() {
52636
52857
  if (which("blitzstrike"))
52637
52858
  return { command: "blitzstrike", args: ["serve", "--mcp"] };
52638
- const src = join11(ROOT6, "src", "index.ts");
52859
+ const src = join12(ROOT6, "src", "index.ts");
52639
52860
  if (which("bun") && existsSync6(src))
52640
52861
  return { command: "bun", args: ["run", src, "serve", "--mcp"] };
52641
52862
  if (which("npx"))
52642
52863
  return { command: "npx", args: ["-y", "blitzstrike", "serve", "--mcp"] };
52643
52864
  if (which("bunx"))
52644
52865
  return { command: "bunx", args: ["blitzstrike", "serve", "--mcp"] };
52645
- const dist = join11(ROOT6, "dist", "index.js");
52866
+ const dist = join12(ROOT6, "dist", "index.js");
52646
52867
  if (existsSync6(dist))
52647
52868
  return { command: "bun", args: [dist, "serve", "--mcp"] };
52648
52869
  return { command: "npx", args: ["-y", "blitzstrike", "serve", "--mcp"] };
52649
52870
  }
52650
52871
  function readJson(p) {
52651
52872
  try {
52652
- return JSON.parse(readFileSync15(p, "utf8"));
52873
+ return JSON.parse(readFileSync16(p, "utf8"));
52653
52874
  } catch {
52654
52875
  return null;
52655
52876
  }
@@ -52664,7 +52885,7 @@ function jsonMcpServersWrite(p) {
52664
52885
  ...existing.mcpServers ?? {},
52665
52886
  blitzstrike: { command, args }
52666
52887
  };
52667
- writeFileSync4(p, JSON.stringify(existing, null, 2));
52888
+ writeFileSync5(p, JSON.stringify(existing, null, 2));
52668
52889
  };
52669
52890
  }
52670
52891
  function opencodeWrite(p) {
@@ -52677,22 +52898,22 @@ function opencodeWrite(p) {
52677
52898
  ...existing.mcp ?? {},
52678
52899
  blitzstrike: { type: "local", command: ["npx", "-y", "blitzstrike", "serve", "--mcp"], enabled: true }
52679
52900
  };
52680
- writeFileSync4(p, JSON.stringify(existing, null, 2));
52901
+ writeFileSync5(p, JSON.stringify(existing, null, 2));
52681
52902
  copyOpenCodeAgents();
52682
52903
  };
52683
52904
  }
52684
52905
  function copyOpenCodeAgents() {
52685
- const srcDir = join11(ROOT6, "opencode-agents");
52686
- const dstDir = join11(homedir5(), ".config", "opencode", "agents");
52906
+ const srcDir = join12(ROOT6, "opencode-agents");
52907
+ const dstDir = join12(homedir5(), ".config", "opencode", "agents");
52687
52908
  if (!existsSync6(srcDir))
52688
52909
  return;
52689
52910
  if (!existsSync6(dstDir))
52690
52911
  mkdirSync4(dstDir, { recursive: true });
52691
52912
  for (const f of ["Blitz Strike.md", "Blitz.md", "Eagle Eye.md", "Strike.md"]) {
52692
- const src = join11(srcDir, f);
52693
- const dst = join11(dstDir, f);
52913
+ const src = join12(srcDir, f);
52914
+ const dst = join12(dstDir, f);
52694
52915
  if (existsSync6(src)) {
52695
- writeFileSync4(dst, readFileSync15(src, "utf8"));
52916
+ writeFileSync5(dst, readFileSync16(src, "utf8"));
52696
52917
  }
52697
52918
  }
52698
52919
  }
@@ -52701,7 +52922,7 @@ function codexWrite(p) {
52701
52922
  const dir = dirname2(p);
52702
52923
  if (!existsSync6(dir))
52703
52924
  mkdirSync4(dir, { recursive: true });
52704
- let existing = existsSync6(p) ? readFileSync15(p, "utf8") : "";
52925
+ let existing = existsSync6(p) ? readFileSync16(p, "utf8") : "";
52705
52926
  if (!existing.trimEnd().endsWith(`
52706
52927
  `))
52707
52928
  existing += `
@@ -52712,7 +52933,7 @@ function codexWrite(p) {
52712
52933
  command = "${command}"
52713
52934
  args = ${JSON.stringify(args)}
52714
52935
  `;
52715
- writeFileSync4(p, existing);
52936
+ writeFileSync5(p, existing);
52716
52937
  };
52717
52938
  }
52718
52939
  function hermesWrite(p) {
@@ -52720,7 +52941,7 @@ function hermesWrite(p) {
52720
52941
  const dir = dirname2(p);
52721
52942
  if (!existsSync6(dir))
52722
52943
  mkdirSync4(dir, { recursive: true });
52723
- let existing = existsSync6(p) ? readFileSync15(p, "utf8") : "";
52944
+ let existing = existsSync6(p) ? readFileSync16(p, "utf8") : "";
52724
52945
  existing = existing.replace(/^ blitzstrike:\n(?: .*\n?)*/m, "");
52725
52946
  if (!existing.trimEnd().endsWith(`
52726
52947
  `))
@@ -52741,80 +52962,80 @@ mcp_servers:
52741
52962
  args: ${JSON.stringify(args)}
52742
52963
  `;
52743
52964
  }
52744
- writeFileSync4(p, existing);
52965
+ writeFileSync5(p, existing);
52745
52966
  };
52746
52967
  }
52747
52968
  function detectAgents() {
52748
52969
  const home = homedir5();
52749
52970
  const agents = [];
52750
- const claudeJson = join11(home, ".claude.json");
52971
+ const claudeJson = join12(home, ".claude.json");
52751
52972
  agents.push({
52752
52973
  name: "Claude Code",
52753
52974
  detectPath: claudeJson,
52754
52975
  installed: () => existsSync6(claudeJson) || which("claude"),
52755
52976
  write: jsonMcpServersWrite(claudeJson)
52756
52977
  });
52757
- const claudeDesktop = join11(home, ".config", "Claude", "claude_desktop_config.json");
52978
+ const claudeDesktop = join12(home, ".config", "Claude", "claude_desktop_config.json");
52758
52979
  agents.push({
52759
52980
  name: "Claude Desktop",
52760
52981
  detectPath: claudeDesktop,
52761
52982
  installed: () => existsSync6(claudeDesktop),
52762
52983
  write: jsonMcpServersWrite(claudeDesktop)
52763
52984
  });
52764
- const cursor = join11(home, ".cursor", "mcp.json");
52985
+ const cursor = join12(home, ".cursor", "mcp.json");
52765
52986
  agents.push({
52766
52987
  name: "Cursor",
52767
52988
  detectPath: cursor,
52768
- installed: () => existsSync6(join11(home, ".cursor")) || which("cursor"),
52989
+ installed: () => existsSync6(join12(home, ".cursor")) || which("cursor"),
52769
52990
  write: jsonMcpServersWrite(cursor)
52770
52991
  });
52771
- const opencode = join11(home, ".config", "opencode", "opencode.jsonc");
52992
+ const opencode = join12(home, ".config", "opencode", "opencode.jsonc");
52772
52993
  agents.push({
52773
52994
  name: "OpenCode",
52774
52995
  detectPath: opencode,
52775
- installed: () => existsSync6(join11(home, ".config", "opencode")) || which("opencode"),
52996
+ installed: () => existsSync6(join12(home, ".config", "opencode")) || which("opencode"),
52776
52997
  write: opencodeWrite(opencode)
52777
52998
  });
52778
- const codex = join11(home, ".codex", "config.toml");
52999
+ const codex = join12(home, ".codex", "config.toml");
52779
53000
  agents.push({
52780
53001
  name: "Codex",
52781
53002
  detectPath: codex,
52782
- installed: () => existsSync6(join11(home, ".codex")) || which("codex"),
53003
+ installed: () => existsSync6(join12(home, ".codex")) || which("codex"),
52783
53004
  write: codexWrite(codex)
52784
53005
  });
52785
- const hermes = join11(home, ".hermes", "config.yaml");
53006
+ const hermes = join12(home, ".hermes", "config.yaml");
52786
53007
  agents.push({
52787
53008
  name: "Hermes",
52788
53009
  detectPath: hermes,
52789
- installed: () => existsSync6(join11(home, ".hermes")) || which("hermes"),
53010
+ installed: () => existsSync6(join12(home, ".hermes")) || which("hermes"),
52790
53011
  write: hermesWrite(hermes)
52791
53012
  });
52792
- const gemini = join11(home, ".gemini", "settings.json");
53013
+ const gemini = join12(home, ".gemini", "settings.json");
52793
53014
  agents.push({
52794
53015
  name: "Gemini CLI",
52795
53016
  detectPath: gemini,
52796
- installed: () => existsSync6(join11(home, ".gemini")) || which("gemini"),
53017
+ installed: () => existsSync6(join12(home, ".gemini")) || which("gemini"),
52797
53018
  write: jsonMcpServersWrite(gemini)
52798
53019
  });
52799
- const windsurf = join11(home, ".codeium", "windsurf", "mcp_config.json");
53020
+ const windsurf = join12(home, ".codeium", "windsurf", "mcp_config.json");
52800
53021
  agents.push({
52801
53022
  name: "Windsurf",
52802
53023
  detectPath: windsurf,
52803
- installed: () => existsSync6(join11(home, ".codeium")) || which("windsurf"),
53024
+ installed: () => existsSync6(join12(home, ".codeium")) || which("windsurf"),
52804
53025
  write: jsonMcpServersWrite(windsurf)
52805
53026
  });
52806
- const copilot = join11(home, ".copilot", "mcp.json");
53027
+ const copilot = join12(home, ".copilot", "mcp.json");
52807
53028
  agents.push({
52808
53029
  name: "Copilot",
52809
53030
  detectPath: copilot,
52810
- installed: () => existsSync6(join11(home, ".copilot")),
53031
+ installed: () => existsSync6(join12(home, ".copilot")),
52811
53032
  write: jsonMcpServersWrite(copilot)
52812
53033
  });
52813
- const cline = join11(home, ".cline", "mcp_settings.json");
53034
+ const cline = join12(home, ".cline", "mcp_settings.json");
52814
53035
  agents.push({
52815
53036
  name: "Cline",
52816
53037
  detectPath: cline,
52817
- installed: () => existsSync6(join11(home, ".cline")),
53038
+ installed: () => existsSync6(join12(home, ".cline")),
52818
53039
  write: jsonMcpServersWrite(cline)
52819
53040
  });
52820
53041
  return agents;
@@ -52858,15 +53079,15 @@ Registered with ${ok}/${installed.length} agent(s).`);
52858
53079
  }
52859
53080
 
52860
53081
  // src/index.ts
52861
- import { readFileSync as readFileSync16, existsSync as existsSync7 } from "node:fs";
52862
- import { join as join12 } from "node:path";
53082
+ import { readFileSync as readFileSync17, existsSync as existsSync7 } from "node:fs";
53083
+ import { join as join13 } from "node:path";
52863
53084
  import { fileURLToPath as fileURLToPath9 } from "node:url";
52864
- var ROOT7 = join12(fileURLToPath9(new URL(".", import.meta.url)), "..");
53085
+ var ROOT7 = join13(fileURLToPath9(new URL(".", import.meta.url)), "..");
52865
53086
  function readVersion() {
52866
53087
  try {
52867
- const p = join12(ROOT7, "package.json");
53088
+ const p = join13(ROOT7, "package.json");
52868
53089
  if (existsSync7(p))
52869
- return JSON.parse(readFileSync16(p, "utf8")).version ?? "1.0.0";
53090
+ return JSON.parse(readFileSync17(p, "utf8")).version ?? "1.0.0";
52870
53091
  } catch {}
52871
53092
  return "1.0.0";
52872
53093
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blitzstrike",
3
- "version": "1.0.36",
3
+ "version": "1.0.37",
4
4
  "description": "Blitz Strike — a universal MCP security-audit toolbelt. BLITZ sweeps the attack surface, EAGLE-EYE traces source-to-sink, STRIKE verifies live. 57 attack chains, 130-tool catalog, intelligence data layer. One server, every agent.",
5
5
  "type": "module",
6
6
  "bin": {