blitzstrike 1.0.38 → 1.0.39

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
@@ -5,6 +5,20 @@ 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.39] — 2026-09-13
9
+
10
+ ### Added
11
+
12
+ - **Phase 5 — Framework Intelligence** (`src/frameworks.ts` + `intelligence/frameworks.json`):
13
+ - Data-driven framework knowledge for 7 frameworks (361 patterns), extensible
14
+ without code changes: Laravel, WordPress (PHP); Express, Next.js (JS/TS);
15
+ Django, FastAPI (Python); Spring (Java). Rails (Ruby) deferred until Ruby support.
16
+ - Each framework defines sources, sinks, sanitizers, auth gates, routing, ORM,
17
+ and middleware patterns (with CWE tags on sinks).
18
+ - `detectFramework(code, language)` — heuristic detection via scored signals.
19
+ - `scanFramework(code, framework)` — framework-aware source/sink/sanitizer/auth hits.
20
+ - New MCP tools: `list_frameworks`, `detect_framework`, `framework_intel`.
21
+
8
22
  ## [1.0.38] — 2026-09-13
9
23
 
10
24
  ### Fixed (Phase 4 review)
package/dist/index.js CHANGED
@@ -14820,10 +14820,10 @@ var require_src = __commonJS(function(exports, module) {
14820
14820
  });
14821
14821
 
14822
14822
  // src/sync.ts
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";
14823
+ import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4, readFileSync as readFileSync16, readdirSync as readdirSync5, statSync as statSync3, rmSync as rmSync3 } from "node:fs";
14824
+ import { join as join12 } from "node:path";
14825
14825
  import { homedir as homedir4 } from "node:os";
14826
- import { fileURLToPath as fileURLToPath7 } from "node:url";
14826
+ import { fileURLToPath as fileURLToPath8 } from "node:url";
14827
14827
  import { spawnSync as spawnSync2 } from "node:child_process";
14828
14828
  function dirExists(p) {
14829
14829
  try {
@@ -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(join11(d, e.name));
14841
+ walk(join12(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 = join11(DATA_ROOT2, "payloads");
14856
- const templatesPath = join11(DATA_ROOT2, "templates");
14855
+ const payloadsPath = join12(DATA_ROOT2, "payloads");
14856
+ const templatesPath = join12(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 = join11(ROOT5, "payloads");
14867
- const localTemplates = join11(ROOT5, "templates");
14866
+ const localPayloads = join12(ROOT6, "payloads");
14867
+ const localTemplates = join12(ROOT6, "templates");
14868
14868
  let payloadsSynced = false;
14869
14869
  let templatesSynced = false;
14870
14870
  if (!hasPayloads && dirExists(localPayloads)) {
@@ -14877,7 +14877,7 @@ 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 = join11(DATA_ROOT2, ".tmp-clone");
14880
+ const tmp = join12(DATA_ROOT2, ".tmp-clone");
14881
14881
  if (dirExists(tmp))
14882
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 });
@@ -14885,11 +14885,11 @@ function syncData() {
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(join11(tmp, "payloads"), payloadsPath);
14888
+ copyDir(join12(tmp, "payloads"), payloadsPath);
14889
14889
  payloadsSynced = true;
14890
14890
  }
14891
14891
  if (!dirExists(templatesPath) || countFiles(templatesPath) === 0) {
14892
- copyDir(join11(tmp, "templates"), templatesPath);
14892
+ copyDir(join12(tmp, "templates"), templatesPath);
14893
14893
  templatesSynced = true;
14894
14894
  }
14895
14895
  }
@@ -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 = join11(src, e.name);
14915
- const d = join11(dst, e.name);
14914
+ const s = join12(src, e.name);
14915
+ const d = join12(dst, e.name);
14916
14916
  if (e.isDirectory())
14917
14917
  manualCopy(s, d);
14918
14918
  else
14919
- writeFileSync4(d, readFileSync15(s));
14919
+ writeFileSync4(d, readFileSync16(s));
14920
14920
  }
14921
14921
  }
14922
- var ROOT5, DATA_ROOT2, GITHUB_REPO = "https://github.com/shinthink/blitzstrike.git";
14922
+ var ROOT6, DATA_ROOT2, GITHUB_REPO = "https://github.com/shinthink/blitzstrike.git";
14923
14923
  var init_sync = __esm(() => {
14924
- ROOT5 = join11(fileURLToPath7(new URL(".", import.meta.url)), "..");
14925
- DATA_ROOT2 = process.env.BLITZSTRIKE_DATA ?? join11(homedir4(), ".blitzstrike", "data");
14924
+ ROOT6 = join12(fileURLToPath8(new URL(".", import.meta.url)), "..");
14925
+ DATA_ROOT2 = process.env.BLITZSTRIKE_DATA ?? join12(homedir4(), ".blitzstrike", "data");
14926
14926
  });
14927
14927
 
14928
14928
  // src/update.ts
@@ -29601,6 +29601,7 @@ class StdioServerTransport {
29601
29601
 
29602
29602
  // src/server.ts
29603
29603
  init_scanner();
29604
+ import { readFileSync as readFileSync17 } from "node:fs";
29604
29605
 
29605
29606
  // src/recon.ts
29606
29607
  var TIMEOUT_MS = 25000;
@@ -50937,6 +50938,98 @@ function analyzeGitDiff(repo, base = "HEAD~1", head = "HEAD") {
50937
50938
  return { repo, base, head, files, total_changes, security_sensitive };
50938
50939
  }
50939
50940
 
50941
+ // src/frameworks.ts
50942
+ import { readFileSync as readFileSync14, existsSync as existsSync6 } from "node:fs";
50943
+ import { join as join10 } from "node:path";
50944
+ import { fileURLToPath as fileURLToPath6 } from "node:url";
50945
+ var ROOT4 = join10(fileURLToPath6(new URL(".", import.meta.url)), "..");
50946
+ var INTEL2 = join10(ROOT4, "intelligence");
50947
+ var cache = null;
50948
+ function load() {
50949
+ if (cache)
50950
+ return cache;
50951
+ const p = join10(INTEL2, "frameworks.json");
50952
+ if (!existsSync6(p))
50953
+ return null;
50954
+ try {
50955
+ cache = JSON.parse(readFileSync14(p, "utf8"));
50956
+ return cache;
50957
+ } catch {
50958
+ return null;
50959
+ }
50960
+ }
50961
+ function listFrameworks() {
50962
+ const data = load();
50963
+ if (!data)
50964
+ return [];
50965
+ return data.frameworks.map((f) => ({ id: f.id, name: f.name, language: f.language }));
50966
+ }
50967
+ function frameworkProfile(id) {
50968
+ const data = load();
50969
+ if (!data)
50970
+ return null;
50971
+ return data.frameworks.find((f) => f.id === id) ?? null;
50972
+ }
50973
+ function detectFramework(code, language) {
50974
+ const data = load();
50975
+ const hay = code.toLowerCase();
50976
+ const candidates = [];
50977
+ if (data) {
50978
+ for (const f of data.frameworks) {
50979
+ if (f.language !== language)
50980
+ continue;
50981
+ const score = f.detect.reduce((n, sig) => hay.includes(sig.toLowerCase()) ? n + 1 : n, 0);
50982
+ if (score > 0)
50983
+ candidates.push({ id: f.id, name: f.name, score });
50984
+ }
50985
+ }
50986
+ candidates.sort((a, b) => b.score - a.score);
50987
+ return { framework: candidates[0]?.id ?? null, language, candidates };
50988
+ }
50989
+ function scanFramework(code, frameworkId) {
50990
+ const profile = frameworkProfile(frameworkId);
50991
+ const empty = {
50992
+ framework: frameworkId,
50993
+ language: profile?.language ?? "unknown",
50994
+ detected: false,
50995
+ sources: [],
50996
+ sinks: [],
50997
+ sanitizers: [],
50998
+ auth: [],
50999
+ summary: { sources: 0, sinks: 0, sanitizers: 0, auth: 0 }
51000
+ };
51001
+ if (!profile)
51002
+ return empty;
51003
+ const lines = code.split(`
51004
+ `);
51005
+ const scan = (patterns) => {
51006
+ const hits = [];
51007
+ for (let i = 0;i < lines.length; i++) {
51008
+ const line = lines[i].toLowerCase();
51009
+ for (const p of patterns) {
51010
+ if (line.includes(p.pattern.toLowerCase())) {
51011
+ hits.push({ line: i + 1, pattern: p.pattern, label: p.label, cwe: p.cwe });
51012
+ }
51013
+ }
51014
+ }
51015
+ return hits;
51016
+ };
51017
+ const sources = scan(profile.sources);
51018
+ const sinks = scan(profile.sinks);
51019
+ const sanitizers = scan(profile.sanitizers);
51020
+ const auth = scan(profile.auth);
51021
+ return {
51022
+ framework: frameworkId,
51023
+ language: profile.language,
51024
+ detected: true,
51025
+ sources,
51026
+ sinks,
51027
+ sanitizers,
51028
+ auth,
51029
+ summary: { sources: sources.length, sinks: sinks.length, sanitizers: sanitizers.length, auth: auth.length }
51030
+ };
51031
+ }
51032
+
50940
51033
  // src/guidance.ts
50941
51034
  var SINK_DIRECTIVES = {
50942
51035
  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.",
@@ -51750,16 +51843,16 @@ function reportJson(findings, opts = {}) {
51750
51843
  }
51751
51844
 
51752
51845
  // src/benchmark.ts
51753
- import { readFileSync as readFileSync14, readdirSync as readdirSync4, writeFileSync as writeFileSync3, rmSync as rmSync2 } from "node:fs";
51754
- import { join as join10 } from "node:path";
51755
- import { fileURLToPath as fileURLToPath6 } from "node:url";
51846
+ import { readFileSync as readFileSync15, readdirSync as readdirSync4, writeFileSync as writeFileSync3, rmSync as rmSync2 } from "node:fs";
51847
+ import { join as join11 } from "node:path";
51848
+ import { fileURLToPath as fileURLToPath7 } from "node:url";
51756
51849
  import { tmpdir as tmpdir2 } from "node:os";
51757
- var ROOT4 = join10(fileURLToPath6(new URL(".", import.meta.url)), "..");
51758
- var CORPUS = join10(ROOT4, "bench", "corpus");
51850
+ var ROOT5 = join11(fileURLToPath7(new URL(".", import.meta.url)), "..");
51851
+ var CORPUS = join11(ROOT5, "bench", "corpus");
51759
51852
  function parseCorpusFile(path) {
51760
51853
  let text;
51761
51854
  try {
51762
- text = readFileSync14(path, "utf8");
51855
+ text = readFileSync15(path, "utf8");
51763
51856
  } catch {
51764
51857
  return null;
51765
51858
  }
@@ -51809,7 +51902,7 @@ function loadCorpus() {
51809
51902
  for (const f of readdirSync4(CORPUS)) {
51810
51903
  if (!f.endsWith(".md"))
51811
51904
  continue;
51812
- const e = parseCorpusFile(join10(CORPUS, f));
51905
+ const e = parseCorpusFile(join11(CORPUS, f));
51813
51906
  if (e)
51814
51907
  entries.push(e);
51815
51908
  }
@@ -51821,7 +51914,7 @@ function loadCorpus() {
51821
51914
  function detect(entry) {
51822
51915
  const lang = entry.language;
51823
51916
  if (lang === "php") {
51824
- const tmp = join10(tmpdir2(), `blitzstrike-bench-${entry.id}-${process.pid}.php`);
51917
+ const tmp = join11(tmpdir2(), `blitzstrike-bench-${entry.id}-${process.pid}.php`);
51825
51918
  writeFileSync3(tmp, entry.code);
51826
51919
  const r = analyzeDataFlow2(tmp);
51827
51920
  rmSync2(tmp, { force: true });
@@ -52669,6 +52762,62 @@ function createServer() {
52669
52762
  }
52670
52763
  return { content: [{ type: "text", text: JSON.stringify(result) }] };
52671
52764
  });
52765
+ server.registerTool("list_frameworks", {
52766
+ title: "List supported frameworks",
52767
+ description: "PHASE 5: list the web frameworks Blitz Strike understands for framework-aware security analysis (Laravel, WordPress, Express, Next.js, Django, FastAPI, Spring)."
52768
+ }, async () => {
52769
+ return { content: [{ type: "text", text: JSON.stringify({ frameworks: listFrameworks() }) }] };
52770
+ });
52771
+ server.registerTool("detect_framework", {
52772
+ title: "Detect web framework",
52773
+ description: "PHASE 5: detect the web framework used by a source file (reads the file, then scores each framework's detection signals for the file's language).",
52774
+ inputSchema: {
52775
+ path: string2().optional().describe("Source file path (alternative: code)"),
52776
+ code: string2().optional().describe("Source code directly (alternative to path)"),
52777
+ language: string2().optional().describe("Language (auto-detected from path if omitted)")
52778
+ }
52779
+ }, async ({ path, code, language }) => {
52780
+ let src = code ?? "";
52781
+ let lang = language;
52782
+ if (path && !code) {
52783
+ try {
52784
+ src = readFileSync17(path, "utf8");
52785
+ } catch {
52786
+ return { content: [{ type: "text", text: JSON.stringify({ error: `cannot read ${path}` }) }] };
52787
+ }
52788
+ lang = lang ?? detectLanguage(path)?.language ?? "php";
52789
+ }
52790
+ const d = detectFramework(src, lang ?? "php");
52791
+ const scan = d.framework ? scanFramework(src, d.framework) : null;
52792
+ return {
52793
+ content: [{
52794
+ type: "text",
52795
+ text: JSON.stringify({
52796
+ framework: d.framework,
52797
+ language: d.language,
52798
+ candidates: d.candidates,
52799
+ scan
52800
+ })
52801
+ }]
52802
+ };
52803
+ });
52804
+ server.registerTool("framework_intel", {
52805
+ title: "Framework security knowledge",
52806
+ description: "PHASE 5: return a framework's security knowledge (sources, sinks, sanitizers, auth gates, routing, ORM, middleware) and optionally scan code with those patterns.",
52807
+ inputSchema: {
52808
+ framework: string2().describe("Framework id (see list_frameworks)"),
52809
+ code: string2().optional().describe("Optional source code to scan with the framework's patterns")
52810
+ }
52811
+ }, async ({ framework, code }) => {
52812
+ const profile = frameworkProfile(framework);
52813
+ if (!profile) {
52814
+ return { content: [{ type: "text", text: JSON.stringify({ error: `unknown framework: ${framework}` }) }] };
52815
+ }
52816
+ const out = { profile };
52817
+ if (code !== undefined)
52818
+ out.scan = scanFramework(code, framework);
52819
+ return { content: [{ type: "text", text: JSON.stringify(out) }] };
52820
+ });
52672
52821
  server.registerTool("taint_tree", {
52673
52822
  title: "Whole-tree taint scan",
52674
52823
  description: "EAGLE-EYE: run inter-procedural taint analysis across an entire source tree.",
@@ -52822,12 +52971,12 @@ async function serve() {
52822
52971
  }
52823
52972
 
52824
52973
  // src/cli.ts
52825
- import { readFileSync as readFileSync16, existsSync as existsSync6, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4 } from "node:fs";
52826
- import { join as join12, dirname as dirname2 } from "node:path";
52974
+ import { readFileSync as readFileSync18, existsSync as existsSync7, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4 } from "node:fs";
52975
+ import { join as join13, dirname as dirname2 } from "node:path";
52827
52976
  import { homedir as homedir5 } from "node:os";
52828
- import { fileURLToPath as fileURLToPath8 } from "node:url";
52977
+ import { fileURLToPath as fileURLToPath9 } from "node:url";
52829
52978
  import { spawnSync as spawnSync3 } from "node:child_process";
52830
- var ROOT6 = join12(fileURLToPath8(new URL(".", import.meta.url)), "..");
52979
+ var ROOT7 = join13(fileURLToPath9(new URL(".", import.meta.url)), "..");
52831
52980
  function which(cmd) {
52832
52981
  return spawnSync3(`command -v ${cmd}`, { shell: true }).status === 0;
52833
52982
  }
@@ -52862,7 +53011,7 @@ function runDoctor() {
52862
53011
  detail: fofa ? "set" : "FOFA_EMAIL/FOFA_KEY not set",
52863
53012
  fix: fofa ? undefined : "export FOFA_EMAIL=... && export FOFA_KEY=... (enables fofa_search)"
52864
53013
  });
52865
- const dataOk = existsSync6(join12(ROOT6, "chains.json")) && existsSync6(join12(ROOT6, "tools-catalog.json"));
53014
+ const dataOk = existsSync7(join13(ROOT7, "chains.json")) && existsSync7(join13(ROOT7, "tools-catalog.json"));
52866
53015
  issues.push({
52867
53016
  name: "Data layers (chains + tools-catalog)",
52868
53017
  status: dataOk ? "ok" : "fail",
@@ -52890,21 +53039,21 @@ BlitzStrike — ${verdict}`);
52890
53039
  function resolveCommand() {
52891
53040
  if (which("blitzstrike"))
52892
53041
  return { command: "blitzstrike", args: ["serve", "--mcp"] };
52893
- const src = join12(ROOT6, "src", "index.ts");
52894
- if (which("bun") && existsSync6(src))
53042
+ const src = join13(ROOT7, "src", "index.ts");
53043
+ if (which("bun") && existsSync7(src))
52895
53044
  return { command: "bun", args: ["run", src, "serve", "--mcp"] };
52896
53045
  if (which("npx"))
52897
53046
  return { command: "npx", args: ["-y", "blitzstrike", "serve", "--mcp"] };
52898
53047
  if (which("bunx"))
52899
53048
  return { command: "bunx", args: ["blitzstrike", "serve", "--mcp"] };
52900
- const dist = join12(ROOT6, "dist", "index.js");
52901
- if (existsSync6(dist))
53049
+ const dist = join13(ROOT7, "dist", "index.js");
53050
+ if (existsSync7(dist))
52902
53051
  return { command: "bun", args: [dist, "serve", "--mcp"] };
52903
53052
  return { command: "npx", args: ["-y", "blitzstrike", "serve", "--mcp"] };
52904
53053
  }
52905
53054
  function readJson(p) {
52906
53055
  try {
52907
- return JSON.parse(readFileSync16(p, "utf8"));
53056
+ return JSON.parse(readFileSync18(p, "utf8"));
52908
53057
  } catch {
52909
53058
  return null;
52910
53059
  }
@@ -52912,7 +53061,7 @@ function readJson(p) {
52912
53061
  function jsonMcpServersWrite(p) {
52913
53062
  return (command, args) => {
52914
53063
  const dir = dirname2(p);
52915
- if (!existsSync6(dir))
53064
+ if (!existsSync7(dir))
52916
53065
  mkdirSync4(dir, { recursive: true });
52917
53066
  const existing = readJson(p) ?? {};
52918
53067
  existing.mcpServers = {
@@ -52925,7 +53074,7 @@ function jsonMcpServersWrite(p) {
52925
53074
  function opencodeWrite(p) {
52926
53075
  return (_command, _args) => {
52927
53076
  const dir = dirname2(p);
52928
- if (!existsSync6(dir))
53077
+ if (!existsSync7(dir))
52929
53078
  mkdirSync4(dir, { recursive: true });
52930
53079
  const existing = readJson(p) ?? {};
52931
53080
  existing.mcp = {
@@ -52937,26 +53086,26 @@ function opencodeWrite(p) {
52937
53086
  };
52938
53087
  }
52939
53088
  function copyOpenCodeAgents() {
52940
- const srcDir = join12(ROOT6, "opencode-agents");
52941
- const dstDir = join12(homedir5(), ".config", "opencode", "agents");
52942
- if (!existsSync6(srcDir))
53089
+ const srcDir = join13(ROOT7, "opencode-agents");
53090
+ const dstDir = join13(homedir5(), ".config", "opencode", "agents");
53091
+ if (!existsSync7(srcDir))
52943
53092
  return;
52944
- if (!existsSync6(dstDir))
53093
+ if (!existsSync7(dstDir))
52945
53094
  mkdirSync4(dstDir, { recursive: true });
52946
53095
  for (const f of ["Blitz Strike.md", "Blitz.md", "Eagle Eye.md", "Strike.md"]) {
52947
- const src = join12(srcDir, f);
52948
- const dst = join12(dstDir, f);
52949
- if (existsSync6(src)) {
52950
- writeFileSync5(dst, readFileSync16(src, "utf8"));
53096
+ const src = join13(srcDir, f);
53097
+ const dst = join13(dstDir, f);
53098
+ if (existsSync7(src)) {
53099
+ writeFileSync5(dst, readFileSync18(src, "utf8"));
52951
53100
  }
52952
53101
  }
52953
53102
  }
52954
53103
  function codexWrite(p) {
52955
53104
  return (command, args) => {
52956
53105
  const dir = dirname2(p);
52957
- if (!existsSync6(dir))
53106
+ if (!existsSync7(dir))
52958
53107
  mkdirSync4(dir, { recursive: true });
52959
- let existing = existsSync6(p) ? readFileSync16(p, "utf8") : "";
53108
+ let existing = existsSync7(p) ? readFileSync18(p, "utf8") : "";
52960
53109
  if (!existing.trimEnd().endsWith(`
52961
53110
  `))
52962
53111
  existing += `
@@ -52973,9 +53122,9 @@ args = ${JSON.stringify(args)}
52973
53122
  function hermesWrite(p) {
52974
53123
  return (command, args) => {
52975
53124
  const dir = dirname2(p);
52976
- if (!existsSync6(dir))
53125
+ if (!existsSync7(dir))
52977
53126
  mkdirSync4(dir, { recursive: true });
52978
- let existing = existsSync6(p) ? readFileSync16(p, "utf8") : "";
53127
+ let existing = existsSync7(p) ? readFileSync18(p, "utf8") : "";
52979
53128
  existing = existing.replace(/^ blitzstrike:\n(?: .*\n?)*/m, "");
52980
53129
  if (!existing.trimEnd().endsWith(`
52981
53130
  `))
@@ -53002,74 +53151,74 @@ mcp_servers:
53002
53151
  function detectAgents() {
53003
53152
  const home = homedir5();
53004
53153
  const agents = [];
53005
- const claudeJson = join12(home, ".claude.json");
53154
+ const claudeJson = join13(home, ".claude.json");
53006
53155
  agents.push({
53007
53156
  name: "Claude Code",
53008
53157
  detectPath: claudeJson,
53009
- installed: () => existsSync6(claudeJson) || which("claude"),
53158
+ installed: () => existsSync7(claudeJson) || which("claude"),
53010
53159
  write: jsonMcpServersWrite(claudeJson)
53011
53160
  });
53012
- const claudeDesktop = join12(home, ".config", "Claude", "claude_desktop_config.json");
53161
+ const claudeDesktop = join13(home, ".config", "Claude", "claude_desktop_config.json");
53013
53162
  agents.push({
53014
53163
  name: "Claude Desktop",
53015
53164
  detectPath: claudeDesktop,
53016
- installed: () => existsSync6(claudeDesktop),
53165
+ installed: () => existsSync7(claudeDesktop),
53017
53166
  write: jsonMcpServersWrite(claudeDesktop)
53018
53167
  });
53019
- const cursor = join12(home, ".cursor", "mcp.json");
53168
+ const cursor = join13(home, ".cursor", "mcp.json");
53020
53169
  agents.push({
53021
53170
  name: "Cursor",
53022
53171
  detectPath: cursor,
53023
- installed: () => existsSync6(join12(home, ".cursor")) || which("cursor"),
53172
+ installed: () => existsSync7(join13(home, ".cursor")) || which("cursor"),
53024
53173
  write: jsonMcpServersWrite(cursor)
53025
53174
  });
53026
- const opencode = join12(home, ".config", "opencode", "opencode.jsonc");
53175
+ const opencode = join13(home, ".config", "opencode", "opencode.jsonc");
53027
53176
  agents.push({
53028
53177
  name: "OpenCode",
53029
53178
  detectPath: opencode,
53030
- installed: () => existsSync6(join12(home, ".config", "opencode")) || which("opencode"),
53179
+ installed: () => existsSync7(join13(home, ".config", "opencode")) || which("opencode"),
53031
53180
  write: opencodeWrite(opencode)
53032
53181
  });
53033
- const codex = join12(home, ".codex", "config.toml");
53182
+ const codex = join13(home, ".codex", "config.toml");
53034
53183
  agents.push({
53035
53184
  name: "Codex",
53036
53185
  detectPath: codex,
53037
- installed: () => existsSync6(join12(home, ".codex")) || which("codex"),
53186
+ installed: () => existsSync7(join13(home, ".codex")) || which("codex"),
53038
53187
  write: codexWrite(codex)
53039
53188
  });
53040
- const hermes = join12(home, ".hermes", "config.yaml");
53189
+ const hermes = join13(home, ".hermes", "config.yaml");
53041
53190
  agents.push({
53042
53191
  name: "Hermes",
53043
53192
  detectPath: hermes,
53044
- installed: () => existsSync6(join12(home, ".hermes")) || which("hermes"),
53193
+ installed: () => existsSync7(join13(home, ".hermes")) || which("hermes"),
53045
53194
  write: hermesWrite(hermes)
53046
53195
  });
53047
- const gemini = join12(home, ".gemini", "settings.json");
53196
+ const gemini = join13(home, ".gemini", "settings.json");
53048
53197
  agents.push({
53049
53198
  name: "Gemini CLI",
53050
53199
  detectPath: gemini,
53051
- installed: () => existsSync6(join12(home, ".gemini")) || which("gemini"),
53200
+ installed: () => existsSync7(join13(home, ".gemini")) || which("gemini"),
53052
53201
  write: jsonMcpServersWrite(gemini)
53053
53202
  });
53054
- const windsurf = join12(home, ".codeium", "windsurf", "mcp_config.json");
53203
+ const windsurf = join13(home, ".codeium", "windsurf", "mcp_config.json");
53055
53204
  agents.push({
53056
53205
  name: "Windsurf",
53057
53206
  detectPath: windsurf,
53058
- installed: () => existsSync6(join12(home, ".codeium")) || which("windsurf"),
53207
+ installed: () => existsSync7(join13(home, ".codeium")) || which("windsurf"),
53059
53208
  write: jsonMcpServersWrite(windsurf)
53060
53209
  });
53061
- const copilot = join12(home, ".copilot", "mcp.json");
53210
+ const copilot = join13(home, ".copilot", "mcp.json");
53062
53211
  agents.push({
53063
53212
  name: "Copilot",
53064
53213
  detectPath: copilot,
53065
- installed: () => existsSync6(join12(home, ".copilot")),
53214
+ installed: () => existsSync7(join13(home, ".copilot")),
53066
53215
  write: jsonMcpServersWrite(copilot)
53067
53216
  });
53068
- const cline = join12(home, ".cline", "mcp_settings.json");
53217
+ const cline = join13(home, ".cline", "mcp_settings.json");
53069
53218
  agents.push({
53070
53219
  name: "Cline",
53071
53220
  detectPath: cline,
53072
- installed: () => existsSync6(join12(home, ".cline")),
53221
+ installed: () => existsSync7(join13(home, ".cline")),
53073
53222
  write: jsonMcpServersWrite(cline)
53074
53223
  });
53075
53224
  return agents;
@@ -53113,15 +53262,15 @@ Registered with ${ok}/${installed.length} agent(s).`);
53113
53262
  }
53114
53263
 
53115
53264
  // src/index.ts
53116
- import { readFileSync as readFileSync17, existsSync as existsSync7 } from "node:fs";
53117
- import { join as join13 } from "node:path";
53118
- import { fileURLToPath as fileURLToPath9 } from "node:url";
53119
- var ROOT7 = join13(fileURLToPath9(new URL(".", import.meta.url)), "..");
53265
+ import { readFileSync as readFileSync19, existsSync as existsSync8 } from "node:fs";
53266
+ import { join as join14 } from "node:path";
53267
+ import { fileURLToPath as fileURLToPath10 } from "node:url";
53268
+ var ROOT8 = join14(fileURLToPath10(new URL(".", import.meta.url)), "..");
53120
53269
  function readVersion() {
53121
53270
  try {
53122
- const p = join13(ROOT7, "package.json");
53123
- if (existsSync7(p))
53124
- return JSON.parse(readFileSync17(p, "utf8")).version ?? "1.0.0";
53271
+ const p = join14(ROOT8, "package.json");
53272
+ if (existsSync8(p))
53273
+ return JSON.parse(readFileSync19(p, "utf8")).version ?? "1.0.0";
53125
53274
  } catch {}
53126
53275
  return "1.0.0";
53127
53276
  }