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 +14 -0
- package/dist/index.js +225 -76
- package/intelligence/frameworks.json +1781 -0
- package/package.json +1 -1
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
|
|
14824
|
-
import { join as
|
|
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
|
|
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(
|
|
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 =
|
|
14856
|
-
const templatesPath =
|
|
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 =
|
|
14867
|
-
const localTemplates =
|
|
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 =
|
|
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(
|
|
14888
|
+
copyDir(join12(tmp, "payloads"), payloadsPath);
|
|
14889
14889
|
payloadsSynced = true;
|
|
14890
14890
|
}
|
|
14891
14891
|
if (!dirExists(templatesPath) || countFiles(templatesPath) === 0) {
|
|
14892
|
-
copyDir(
|
|
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 =
|
|
14915
|
-
const d =
|
|
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,
|
|
14919
|
+
writeFileSync4(d, readFileSync16(s));
|
|
14920
14920
|
}
|
|
14921
14921
|
}
|
|
14922
|
-
var
|
|
14922
|
+
var ROOT6, DATA_ROOT2, GITHUB_REPO = "https://github.com/shinthink/blitzstrike.git";
|
|
14923
14923
|
var init_sync = __esm(() => {
|
|
14924
|
-
|
|
14925
|
-
DATA_ROOT2 = process.env.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
|
|
51754
|
-
import { join as
|
|
51755
|
-
import { fileURLToPath as
|
|
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
|
|
51758
|
-
var 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 =
|
|
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(
|
|
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 =
|
|
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
|
|
52826
|
-
import { join as
|
|
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
|
|
52977
|
+
import { fileURLToPath as fileURLToPath9 } from "node:url";
|
|
52829
52978
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
52830
|
-
var
|
|
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 =
|
|
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 =
|
|
52894
|
-
if (which("bun") &&
|
|
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 =
|
|
52901
|
-
if (
|
|
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(
|
|
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 (!
|
|
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 (!
|
|
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 =
|
|
52941
|
-
const dstDir =
|
|
52942
|
-
if (!
|
|
53089
|
+
const srcDir = join13(ROOT7, "opencode-agents");
|
|
53090
|
+
const dstDir = join13(homedir5(), ".config", "opencode", "agents");
|
|
53091
|
+
if (!existsSync7(srcDir))
|
|
52943
53092
|
return;
|
|
52944
|
-
if (!
|
|
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 =
|
|
52948
|
-
const dst =
|
|
52949
|
-
if (
|
|
52950
|
-
writeFileSync5(dst,
|
|
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 (!
|
|
53106
|
+
if (!existsSync7(dir))
|
|
52958
53107
|
mkdirSync4(dir, { recursive: true });
|
|
52959
|
-
let existing =
|
|
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 (!
|
|
53125
|
+
if (!existsSync7(dir))
|
|
52977
53126
|
mkdirSync4(dir, { recursive: true });
|
|
52978
|
-
let existing =
|
|
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 =
|
|
53154
|
+
const claudeJson = join13(home, ".claude.json");
|
|
53006
53155
|
agents.push({
|
|
53007
53156
|
name: "Claude Code",
|
|
53008
53157
|
detectPath: claudeJson,
|
|
53009
|
-
installed: () =>
|
|
53158
|
+
installed: () => existsSync7(claudeJson) || which("claude"),
|
|
53010
53159
|
write: jsonMcpServersWrite(claudeJson)
|
|
53011
53160
|
});
|
|
53012
|
-
const claudeDesktop =
|
|
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: () =>
|
|
53165
|
+
installed: () => existsSync7(claudeDesktop),
|
|
53017
53166
|
write: jsonMcpServersWrite(claudeDesktop)
|
|
53018
53167
|
});
|
|
53019
|
-
const cursor =
|
|
53168
|
+
const cursor = join13(home, ".cursor", "mcp.json");
|
|
53020
53169
|
agents.push({
|
|
53021
53170
|
name: "Cursor",
|
|
53022
53171
|
detectPath: cursor,
|
|
53023
|
-
installed: () =>
|
|
53172
|
+
installed: () => existsSync7(join13(home, ".cursor")) || which("cursor"),
|
|
53024
53173
|
write: jsonMcpServersWrite(cursor)
|
|
53025
53174
|
});
|
|
53026
|
-
const opencode =
|
|
53175
|
+
const opencode = join13(home, ".config", "opencode", "opencode.jsonc");
|
|
53027
53176
|
agents.push({
|
|
53028
53177
|
name: "OpenCode",
|
|
53029
53178
|
detectPath: opencode,
|
|
53030
|
-
installed: () =>
|
|
53179
|
+
installed: () => existsSync7(join13(home, ".config", "opencode")) || which("opencode"),
|
|
53031
53180
|
write: opencodeWrite(opencode)
|
|
53032
53181
|
});
|
|
53033
|
-
const codex =
|
|
53182
|
+
const codex = join13(home, ".codex", "config.toml");
|
|
53034
53183
|
agents.push({
|
|
53035
53184
|
name: "Codex",
|
|
53036
53185
|
detectPath: codex,
|
|
53037
|
-
installed: () =>
|
|
53186
|
+
installed: () => existsSync7(join13(home, ".codex")) || which("codex"),
|
|
53038
53187
|
write: codexWrite(codex)
|
|
53039
53188
|
});
|
|
53040
|
-
const hermes =
|
|
53189
|
+
const hermes = join13(home, ".hermes", "config.yaml");
|
|
53041
53190
|
agents.push({
|
|
53042
53191
|
name: "Hermes",
|
|
53043
53192
|
detectPath: hermes,
|
|
53044
|
-
installed: () =>
|
|
53193
|
+
installed: () => existsSync7(join13(home, ".hermes")) || which("hermes"),
|
|
53045
53194
|
write: hermesWrite(hermes)
|
|
53046
53195
|
});
|
|
53047
|
-
const gemini =
|
|
53196
|
+
const gemini = join13(home, ".gemini", "settings.json");
|
|
53048
53197
|
agents.push({
|
|
53049
53198
|
name: "Gemini CLI",
|
|
53050
53199
|
detectPath: gemini,
|
|
53051
|
-
installed: () =>
|
|
53200
|
+
installed: () => existsSync7(join13(home, ".gemini")) || which("gemini"),
|
|
53052
53201
|
write: jsonMcpServersWrite(gemini)
|
|
53053
53202
|
});
|
|
53054
|
-
const windsurf =
|
|
53203
|
+
const windsurf = join13(home, ".codeium", "windsurf", "mcp_config.json");
|
|
53055
53204
|
agents.push({
|
|
53056
53205
|
name: "Windsurf",
|
|
53057
53206
|
detectPath: windsurf,
|
|
53058
|
-
installed: () =>
|
|
53207
|
+
installed: () => existsSync7(join13(home, ".codeium")) || which("windsurf"),
|
|
53059
53208
|
write: jsonMcpServersWrite(windsurf)
|
|
53060
53209
|
});
|
|
53061
|
-
const copilot =
|
|
53210
|
+
const copilot = join13(home, ".copilot", "mcp.json");
|
|
53062
53211
|
agents.push({
|
|
53063
53212
|
name: "Copilot",
|
|
53064
53213
|
detectPath: copilot,
|
|
53065
|
-
installed: () =>
|
|
53214
|
+
installed: () => existsSync7(join13(home, ".copilot")),
|
|
53066
53215
|
write: jsonMcpServersWrite(copilot)
|
|
53067
53216
|
});
|
|
53068
|
-
const cline =
|
|
53217
|
+
const cline = join13(home, ".cline", "mcp_settings.json");
|
|
53069
53218
|
agents.push({
|
|
53070
53219
|
name: "Cline",
|
|
53071
53220
|
detectPath: cline,
|
|
53072
|
-
installed: () =>
|
|
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
|
|
53117
|
-
import { join as
|
|
53118
|
-
import { fileURLToPath as
|
|
53119
|
-
var
|
|
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 =
|
|
53123
|
-
if (
|
|
53124
|
-
return JSON.parse(
|
|
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
|
}
|