blitzstrike 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/index.js +280 -141
  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.6.0] — 2026-09-13
9
+
10
+ ### Added — task checkpoint journal (per-workstream resume)
11
+
12
+ - Four new ORCHESTRATION tools for resilient delegation:
13
+ - `task_start` — register a workstream with an ordered plan (steps).
14
+ - `task_checkpoint` — record a checkpoint per step (in_progress / done /
15
+ failed / blocked) with an optional note + output hash.
16
+ - `task_status` — read the current step, progress (done/total), remaining
17
+ steps, and full checkpoint history.
18
+ - `task_resume` — compute the resume instruction: last completed checkpoint,
19
+ current step, remaining steps, and the exact re-dispatch prompt.
20
+ On-disk, so a failed workstream resumes from its last completed step instead
21
+ of restarting from scratch.
22
+
8
23
  ## [1.5.0] — 2026-09-13
9
24
 
10
25
  ### Added — run_autonomous (self-driving orchestrator with escalation)
package/dist/index.js CHANGED
@@ -14898,10 +14898,10 @@ var require_src = __commonJS(function(exports, module) {
14898
14898
  });
14899
14899
 
14900
14900
  // src/sync.ts
14901
- import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync5, readFileSync as readFileSync17, readdirSync as readdirSync6, statSync as statSync4, rmSync as rmSync4 } from "node:fs";
14902
- import { join as join14 } from "node:path";
14901
+ import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync6, readFileSync as readFileSync18, readdirSync as readdirSync6, statSync as statSync4, rmSync as rmSync4 } from "node:fs";
14902
+ import { join as join15 } from "node:path";
14903
14903
  import { homedir as homedir4 } from "node:os";
14904
- import { fileURLToPath as fileURLToPath8 } from "node:url";
14904
+ import { fileURLToPath as fileURLToPath9 } from "node:url";
14905
14905
  import { spawnSync as spawnSync2 } from "node:child_process";
14906
14906
  function dirExists(p) {
14907
14907
  try {
@@ -14916,7 +14916,7 @@ function countFiles(dir) {
14916
14916
  const walk = (d) => {
14917
14917
  for (const e of readdirSync6(d, { withFileTypes: true })) {
14918
14918
  if (e.isDirectory())
14919
- walk(join14(d, e.name));
14919
+ walk(join15(d, e.name));
14920
14920
  else
14921
14921
  n++;
14922
14922
  }
@@ -14929,9 +14929,9 @@ function countFiles(dir) {
14929
14929
  }
14930
14930
  function syncData() {
14931
14931
  if (!dirExists(DATA_ROOT2))
14932
- mkdirSync3(DATA_ROOT2, { recursive: true });
14933
- const payloadsPath = join14(DATA_ROOT2, "payloads");
14934
- const templatesPath = join14(DATA_ROOT2, "templates");
14932
+ mkdirSync4(DATA_ROOT2, { recursive: true });
14933
+ const payloadsPath = join15(DATA_ROOT2, "payloads");
14934
+ const templatesPath = join15(DATA_ROOT2, "templates");
14935
14935
  const hasPayloads = dirExists(payloadsPath) && countFiles(payloadsPath) > 0;
14936
14936
  const hasTemplates = dirExists(templatesPath) && countFiles(templatesPath) > 0;
14937
14937
  if (hasPayloads && hasTemplates) {
@@ -14941,8 +14941,8 @@ function syncData() {
14941
14941
  data_root: DATA_ROOT2
14942
14942
  };
14943
14943
  }
14944
- const localPayloads = join14(ROOT7, "payloads");
14945
- const localTemplates = join14(ROOT7, "templates");
14944
+ const localPayloads = join15(ROOT7, "payloads");
14945
+ const localTemplates = join15(ROOT7, "templates");
14946
14946
  let payloadsSynced = false;
14947
14947
  let templatesSynced = false;
14948
14948
  if (!hasPayloads && dirExists(localPayloads)) {
@@ -14955,7 +14955,7 @@ function syncData() {
14955
14955
  }
14956
14956
  const gitOk = spawnSync2("git", ["--version"]).status === 0;
14957
14957
  if (gitOk && (!dirExists(payloadsPath) || countFiles(payloadsPath) === 0 || !dirExists(templatesPath) || countFiles(templatesPath) === 0)) {
14958
- const tmp = join14(DATA_ROOT2, ".tmp-clone");
14958
+ const tmp = join15(DATA_ROOT2, ".tmp-clone");
14959
14959
  if (dirExists(tmp))
14960
14960
  rmSync4(tmp, { recursive: true, force: true });
14961
14961
  const r = spawnSync2("git", ["clone", "--depth", "1", "--filter=blob:none", "--sparse", GITHUB_REPO, tmp], { timeout: 120000 });
@@ -14963,11 +14963,11 @@ function syncData() {
14963
14963
  const sparse = spawnSync2("git", ["-C", tmp, "sparse-checkout", "set", "payloads", "templates"], { timeout: 120000 });
14964
14964
  if (sparse.status === 0) {
14965
14965
  if (!dirExists(payloadsPath) || countFiles(payloadsPath) === 0) {
14966
- copyDir(join14(tmp, "payloads"), payloadsPath);
14966
+ copyDir(join15(tmp, "payloads"), payloadsPath);
14967
14967
  payloadsSynced = true;
14968
14968
  }
14969
14969
  if (!dirExists(templatesPath) || countFiles(templatesPath) === 0) {
14970
- copyDir(join14(tmp, "templates"), templatesPath);
14970
+ copyDir(join15(tmp, "templates"), templatesPath);
14971
14971
  templatesSynced = true;
14972
14972
  }
14973
14973
  }
@@ -14987,20 +14987,20 @@ function copyDir(src, dst) {
14987
14987
  }
14988
14988
  }
14989
14989
  function manualCopy(src, dst) {
14990
- mkdirSync3(dst, { recursive: true });
14990
+ mkdirSync4(dst, { recursive: true });
14991
14991
  for (const e of readdirSync6(src, { withFileTypes: true })) {
14992
- const s = join14(src, e.name);
14993
- const d = join14(dst, e.name);
14992
+ const s = join15(src, e.name);
14993
+ const d = join15(dst, e.name);
14994
14994
  if (e.isDirectory())
14995
14995
  manualCopy(s, d);
14996
14996
  else
14997
- writeFileSync5(d, readFileSync17(s));
14997
+ writeFileSync6(d, readFileSync18(s));
14998
14998
  }
14999
14999
  }
15000
15000
  var ROOT7, DATA_ROOT2, GITHUB_REPO = "https://github.com/shinthink/blitzstrike.git";
15001
15001
  var init_sync = __esm(() => {
15002
- ROOT7 = join14(fileURLToPath8(new URL(".", import.meta.url)), "..");
15003
- DATA_ROOT2 = process.env.BLITZSTRIKE_DATA ?? join14(homedir4(), ".blitzstrike", "data");
15002
+ ROOT7 = join15(fileURLToPath9(new URL(".", import.meta.url)), "..");
15003
+ DATA_ROOT2 = process.env.BLITZSTRIKE_DATA ?? join15(homedir4(), ".blitzstrike", "data");
15004
15004
  });
15005
15005
 
15006
15006
  // src/update.ts
@@ -29679,7 +29679,7 @@ class StdioServerTransport {
29679
29679
 
29680
29680
  // src/server.ts
29681
29681
  init_scanner();
29682
- import { readFileSync as readFileSync18 } from "node:fs";
29682
+ import { readFileSync as readFileSync19 } from "node:fs";
29683
29683
 
29684
29684
  // src/recon.ts
29685
29685
  var TIMEOUT_MS = 25000;
@@ -32409,19 +32409,116 @@ function compactState(phase, findings) {
32409
32409
  };
32410
32410
  }
32411
32411
 
32412
- // src/catalog.ts
32413
- import { readFileSync as readFileSync8, readdirSync as readdirSync3 } from "node:fs";
32412
+ // src/tasks.ts
32413
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync2 } from "node:fs";
32414
32414
  import { join as join8 } from "node:path";
32415
32415
  import { fileURLToPath as fileURLToPath5 } from "node:url";
32416
+ function tasksDir() {
32417
+ return join8(fileURLToPath5(new URL(".", import.meta.url)), "..", ".blitzstrike", "tasks");
32418
+ }
32419
+ function taskPath(id) {
32420
+ return join8(tasksDir(), `${id}.json`);
32421
+ }
32422
+ function load(id) {
32423
+ try {
32424
+ return JSON.parse(readFileSync8(taskPath(id), "utf8"));
32425
+ } catch {
32426
+ return null;
32427
+ }
32428
+ }
32429
+ function save(t) {
32430
+ mkdirSync3(tasksDir(), { recursive: true });
32431
+ writeFileSync2(taskPath(t.task_id), JSON.stringify(t, null, 2));
32432
+ }
32433
+ function taskStart(id, objective, plan) {
32434
+ const now = Date.now();
32435
+ const t = {
32436
+ task_id: id,
32437
+ objective,
32438
+ plan,
32439
+ current_step: plan[0] ?? "complete",
32440
+ status: plan.length ? "in_progress" : "done",
32441
+ checkpoints: [],
32442
+ created_ts: now,
32443
+ updated_ts: now
32444
+ };
32445
+ save(t);
32446
+ return { ...t, progress: `0/${plan.length}`, next: t.current_step };
32447
+ }
32448
+ function taskCheckpoint(id, step, status, note, outputHash) {
32449
+ const t = load(id);
32450
+ if (!t)
32451
+ return { ok: false, error: `task '${id}' not found — call task_start first` };
32452
+ t.checkpoints.push({ step, status, output_hash: outputHash, note, ts: Date.now() });
32453
+ t.updated_ts = Date.now();
32454
+ const idx = t.plan.indexOf(step);
32455
+ if (status === "done") {
32456
+ t.current_step = idx >= 0 && idx + 1 < t.plan.length ? t.plan[idx + 1] : "complete";
32457
+ if (t.current_step === "complete")
32458
+ t.status = "done";
32459
+ } else if (status === "failed") {
32460
+ t.status = "failed";
32461
+ } else if (status === "blocked" && t.status !== "failed") {
32462
+ t.status = "blocked";
32463
+ }
32464
+ save(t);
32465
+ return {
32466
+ task_id: id,
32467
+ step,
32468
+ status,
32469
+ current_step: t.current_step,
32470
+ progress: `${t.checkpoints.filter((c) => c.status === "done").length}/${t.plan.length}`,
32471
+ next: t.current_step
32472
+ };
32473
+ }
32474
+ function taskStatus(id) {
32475
+ const t = load(id);
32476
+ if (!t)
32477
+ return { found: false, error: `task '${id}' not found` };
32478
+ const done = t.checkpoints.filter((c) => c.status === "done").length;
32479
+ return {
32480
+ found: true,
32481
+ task_id: t.task_id,
32482
+ objective: t.objective,
32483
+ status: t.status,
32484
+ current_step: t.current_step,
32485
+ progress: `${done}/${t.plan.length}`,
32486
+ remaining: t.plan.slice(t.plan.indexOf(t.current_step)),
32487
+ checkpoints: t.checkpoints
32488
+ };
32489
+ }
32490
+ function taskResume(id) {
32491
+ const t = load(id);
32492
+ if (!t)
32493
+ return { found: false, error: `task '${id}' not found` };
32494
+ const doneSteps = t.checkpoints.filter((c) => c.status === "done").map((c) => c.step);
32495
+ const remaining = t.plan.filter((s) => !doneSteps.includes(s));
32496
+ const last = t.checkpoints[t.checkpoints.length - 1];
32497
+ return {
32498
+ found: true,
32499
+ task_id: id,
32500
+ status: t.status,
32501
+ resume_from: t.current_step,
32502
+ already_done: doneSteps,
32503
+ remaining_steps: remaining,
32504
+ last_checkpoint: last ?? null,
32505
+ instruction: t.status === "done" ? "task complete — nothing to resume" : `resume '${id}' at step '${t.current_step}'. Completed: [${doneSteps.join(", ") || "none"}]. ` + `Remaining: [${remaining.join(", ") || "none"}]. Re-dispatch with the FAILED context (not fresh): ` + `'RESUME ${id}: you already completed [${doneSteps.join(", ")}]; continue from '${t.current_step}'.'`
32506
+ };
32507
+ }
32508
+
32509
+ // src/catalog.ts
32510
+ import { readFileSync as readFileSync9, readdirSync as readdirSync3 } from "node:fs";
32511
+ import { join as join9 } from "node:path";
32512
+ import { fileURLToPath as fileURLToPath6 } from "node:url";
32416
32513
  import { spawnSync } from "node:child_process";
32417
- var ROOT3 = join8(fileURLToPath5(new URL(".", import.meta.url)), "..");
32514
+ var ROOT3 = join9(fileURLToPath6(new URL(".", import.meta.url)), "..");
32418
32515
  var _toolsCache = null;
32419
32516
  var _skillsCache = null;
32420
32517
  function loadTools() {
32421
32518
  if (_toolsCache)
32422
32519
  return _toolsCache;
32423
32520
  try {
32424
- const raw = JSON.parse(readFileSync8(join8(ROOT3, "tools-catalog.json"), "utf8"));
32521
+ const raw = JSON.parse(readFileSync9(join9(ROOT3, "tools-catalog.json"), "utf8"));
32425
32522
  _toolsCache = raw.tools ?? [];
32426
32523
  } catch {
32427
32524
  _toolsCache = [];
@@ -32431,14 +32528,14 @@ function loadTools() {
32431
32528
  function loadSkills() {
32432
32529
  if (_skillsCache)
32433
32530
  return _skillsCache;
32434
- const dir = join8(ROOT3, "skills");
32531
+ const dir = join9(ROOT3, "skills");
32435
32532
  const out = [];
32436
32533
  try {
32437
32534
  for (const f of readdirSync3(dir)) {
32438
32535
  if (!f.endsWith(".md"))
32439
32536
  continue;
32440
- const p = join8(dir, f);
32441
- const text = readFileSync8(p, "utf8");
32537
+ const p = join9(dir, f);
32538
+ const text = readFileSync9(p, "utf8");
32442
32539
  const descMatch = text.match(/^description:\s*(.+)$/m);
32443
32540
  out.push({
32444
32541
  name: f.replace(/\.md$/, ""),
@@ -32511,7 +32608,7 @@ function readSkill(name) {
32511
32608
  if (!s)
32512
32609
  return { found: false, query: name };
32513
32610
  try {
32514
- const text = readFileSync8(join8(ROOT3, "skills", s.file), "utf8");
32611
+ const text = readFileSync9(join9(ROOT3, "skills", s.file), "utf8");
32515
32612
  return { found: true, name: s.name, content: text.slice(0, 12000) };
32516
32613
  } catch {
32517
32614
  return { found: false, query: name, error: "unreadable" };
@@ -32758,17 +32855,17 @@ function engagementStatus() {
32758
32855
 
32759
32856
  // src/incremental.ts
32760
32857
  import { execSync as execSync2 } from "node:child_process";
32761
- import { writeFileSync as writeFileSync2, mkdtempSync, rmSync } from "node:fs";
32762
- import { join as join9 } from "node:path";
32858
+ import { writeFileSync as writeFileSync3, mkdtempSync, rmSync } from "node:fs";
32859
+ import { join as join10 } from "node:path";
32763
32860
  import { tmpdir } from "node:os";
32764
32861
 
32765
32862
  // src/eagle2.ts
32766
32863
  var import_php_parser2 = __toESM(require_src(), 1);
32767
- import { readFileSync as readFileSync11 } from "node:fs";
32864
+ import { readFileSync as readFileSync12 } from "node:fs";
32768
32865
 
32769
32866
  // src/dataflow.ts
32770
32867
  init_scanner();
32771
- import { readFileSync as readFileSync9 } from "node:fs";
32868
+ import { readFileSync as readFileSync10 } from "node:fs";
32772
32869
  var SOURCES = {
32773
32870
  $_GET: { id: "http_get", label: "HTTP GET parameter", attacker_controlled: true },
32774
32871
  $_POST: { id: "http_post", label: "HTTP POST body", attacker_controlled: true },
@@ -32853,7 +32950,7 @@ function traceDataFlow(path) {
32853
32950
  return result;
32854
32951
  let text;
32855
32952
  try {
32856
- text = readFileSync9(path, "utf8");
32953
+ text = readFileSync10(path, "utf8");
32857
32954
  } catch {
32858
32955
  return result;
32859
32956
  }
@@ -32934,7 +33031,7 @@ init_scanner();
32934
33031
 
32935
33032
  // src/callgraph.ts
32936
33033
  var import_php_parser = __toESM(require_src(), 1);
32937
- import { readFileSync as readFileSync10 } from "node:fs";
33034
+ import { readFileSync as readFileSync11 } from "node:fs";
32938
33035
  var parser = new import_php_parser.Engine({
32939
33036
  parser: { extractDoc: false, php7: true },
32940
33037
  ast: { withPositions: true, withSource: false }
@@ -32995,7 +33092,7 @@ function buildCallGraph(path) {
32995
33092
  const g = { nodes: new Map, edges: [] };
32996
33093
  let code;
32997
33094
  try {
32998
- code = readFileSync10(path, "utf8");
33095
+ code = readFileSync11(path, "utf8");
32999
33096
  } catch {
33000
33097
  return g;
33001
33098
  }
@@ -33656,7 +33753,7 @@ function resolveFuncId(name, summaries) {
33656
33753
  function analyzeDataFlow2(path) {
33657
33754
  let cacheCode = "";
33658
33755
  try {
33659
- cacheCode = readFileSync11(path, "utf8");
33756
+ cacheCode = readFileSync12(path, "utf8");
33660
33757
  } catch {}
33661
33758
  if (cacheCode) {
33662
33759
  const cached = getCache("eagle2", cacheCode);
@@ -33678,7 +33775,7 @@ function analyzeDataFlow2(path) {
33678
33775
  };
33679
33776
  let code;
33680
33777
  try {
33681
- code = cacheCode || readFileSync11(path, "utf8");
33778
+ code = cacheCode || readFileSync12(path, "utf8");
33682
33779
  } catch {
33683
33780
  return result;
33684
33781
  }
@@ -52273,9 +52370,9 @@ function analyzeChangedFiles(repo, base = "HEAD~1", head = "HEAD") {
52273
52370
  continue;
52274
52371
  const lang = detectLanguage(file)?.language;
52275
52372
  if (lang === "php") {
52276
- const dir = mkdtempSync(join9(tmpdir(), "blitz-incr-"));
52277
- const tmp = join9(dir, "changed.php");
52278
- writeFileSync2(tmp, code);
52373
+ const dir = mkdtempSync(join10(tmpdir(), "blitz-incr-"));
52374
+ const tmp = join10(dir, "changed.php");
52375
+ writeFileSync3(tmp, code);
52279
52376
  try {
52280
52377
  const r = analyzeDataFlow2(tmp);
52281
52378
  for (const f of r.findings)
@@ -52295,7 +52392,7 @@ function analyzeChangedFiles(repo, base = "HEAD~1", head = "HEAD") {
52295
52392
  // src/taint.ts
52296
52393
  init_scanner();
52297
52394
  var import_php_parser4 = __toESM(require_src(), 1);
52298
- import { readFileSync as readFileSync12 } from "node:fs";
52395
+ import { readFileSync as readFileSync13 } from "node:fs";
52299
52396
  var parser4 = new import_php_parser4.Engine({
52300
52397
  parser: { extractDoc: false, php7: true },
52301
52398
  ast: { withPositions: true, withSource: false }
@@ -52593,7 +52690,7 @@ function analyzeTaint(path) {
52593
52690
  const result = { file: path, findings: [], suppressed: 0 };
52594
52691
  let code;
52595
52692
  try {
52596
- code = readFileSync12(path, "utf8");
52693
+ code = readFileSync13(path, "utf8");
52597
52694
  } catch {
52598
52695
  return result;
52599
52696
  }
@@ -52687,16 +52784,16 @@ function taintTree(root, maxFiles = 2000) {
52687
52784
  }
52688
52785
 
52689
52786
  // src/differential.ts
52690
- import { writeFileSync as writeFileSync3, rmSync as rmSync2, mkdtempSync as mkdtempSync2, readFileSync as readFileSync13 } from "node:fs";
52691
- import { join as join10 } from "node:path";
52787
+ import { writeFileSync as writeFileSync4, rmSync as rmSync2, mkdtempSync as mkdtempSync2, readFileSync as readFileSync14 } from "node:fs";
52788
+ import { join as join11 } from "node:path";
52692
52789
  import { tmpdir as tmpdir2 } from "node:os";
52693
52790
  import { execSync as execSync3 } from "node:child_process";
52694
52791
  init_scanner();
52695
52792
  function runEngine(code, language, file) {
52696
52793
  if (language === "php") {
52697
- const dir = mkdtempSync2(join10(tmpdir2(), "blitz-diff-"));
52698
- const tmp = join10(dir, "old.php");
52699
- writeFileSync3(tmp, code);
52794
+ const dir = mkdtempSync2(join11(tmpdir2(), "blitz-diff-"));
52795
+ const tmp = join11(dir, "old.php");
52796
+ writeFileSync4(tmp, code);
52700
52797
  try {
52701
52798
  const r = analyzeDataFlow2(tmp);
52702
52799
  return r.findings.map((f) => ({ sink: f.sink, sink_line: f.sink_line, category: f.category, source: f.source, cwe: f.cwe }));
@@ -52849,8 +52946,8 @@ function languageOf(file) {
52849
52946
  return adapter?.language ?? "php";
52850
52947
  }
52851
52948
  function analyzeDifferentialFiles(oldPath, newPath, language) {
52852
- const oldCode = readFileSync13(oldPath, "utf8");
52853
- const newCode = readFileSync13(newPath, "utf8");
52949
+ const oldCode = readFileSync14(oldPath, "utf8");
52950
+ const newCode = readFileSync14(newPath, "utf8");
52854
52951
  const lang = language ?? languageOf(newPath);
52855
52952
  return analyzeDifferential(oldCode, newCode, lang, newPath);
52856
52953
  }
@@ -52880,39 +52977,39 @@ function analyzeGitDiff(repo, base = "HEAD~1", head = "HEAD") {
52880
52977
  }
52881
52978
 
52882
52979
  // src/frameworks.ts
52883
- import { readFileSync as readFileSync14, existsSync as existsSync6 } from "node:fs";
52884
- import { join as join11 } from "node:path";
52885
- import { fileURLToPath as fileURLToPath6 } from "node:url";
52886
- var ROOT4 = join11(fileURLToPath6(new URL(".", import.meta.url)), "..");
52887
- var INTEL2 = join11(ROOT4, "intelligence");
52980
+ import { readFileSync as readFileSync15, existsSync as existsSync6 } from "node:fs";
52981
+ import { join as join12 } from "node:path";
52982
+ import { fileURLToPath as fileURLToPath7 } from "node:url";
52983
+ var ROOT4 = join12(fileURLToPath7(new URL(".", import.meta.url)), "..");
52984
+ var INTEL2 = join12(ROOT4, "intelligence");
52888
52985
  var cache = null;
52889
- function load() {
52986
+ function load2() {
52890
52987
  if (cache)
52891
52988
  return cache;
52892
- const p = join11(INTEL2, "frameworks.json");
52989
+ const p = join12(INTEL2, "frameworks.json");
52893
52990
  if (!existsSync6(p))
52894
52991
  return null;
52895
52992
  try {
52896
- cache = JSON.parse(readFileSync14(p, "utf8"));
52993
+ cache = JSON.parse(readFileSync15(p, "utf8"));
52897
52994
  return cache;
52898
52995
  } catch {
52899
52996
  return null;
52900
52997
  }
52901
52998
  }
52902
52999
  function listFrameworks() {
52903
- const data = load();
53000
+ const data = load2();
52904
53001
  if (!data)
52905
53002
  return [];
52906
53003
  return data.frameworks.map((f) => ({ id: f.id, name: f.name, language: f.language }));
52907
53004
  }
52908
53005
  function frameworkProfile(id) {
52909
- const data = load();
53006
+ const data = load2();
52910
53007
  if (!data)
52911
53008
  return null;
52912
53009
  return data.frameworks.find((f) => f.id === id) ?? null;
52913
53010
  }
52914
53011
  function detectFramework(code, language) {
52915
- const data = load();
53012
+ const data = load2();
52916
53013
  const hay = code.toLowerCase();
52917
53014
  const candidates = [];
52918
53015
  if (data) {
@@ -53724,16 +53821,16 @@ function nextSteps(ctx) {
53724
53821
  }
53725
53822
 
53726
53823
  // src/benchmark.ts
53727
- import { readFileSync as readFileSync15, readdirSync as readdirSync4, writeFileSync as writeFileSync4, rmSync as rmSync3 } from "node:fs";
53728
- import { join as join12 } from "node:path";
53729
- import { fileURLToPath as fileURLToPath7 } from "node:url";
53824
+ import { readFileSync as readFileSync16, readdirSync as readdirSync4, writeFileSync as writeFileSync5, rmSync as rmSync3 } from "node:fs";
53825
+ import { join as join13 } from "node:path";
53826
+ import { fileURLToPath as fileURLToPath8 } from "node:url";
53730
53827
  import { tmpdir as tmpdir3 } from "node:os";
53731
- var ROOT5 = join12(fileURLToPath7(new URL(".", import.meta.url)), "..");
53732
- var CORPUS = join12(ROOT5, "bench", "corpus");
53828
+ var ROOT5 = join13(fileURLToPath8(new URL(".", import.meta.url)), "..");
53829
+ var CORPUS = join13(ROOT5, "bench", "corpus");
53733
53830
  function parseCorpusFile(path) {
53734
53831
  let text;
53735
53832
  try {
53736
- text = readFileSync15(path, "utf8");
53833
+ text = readFileSync16(path, "utf8");
53737
53834
  } catch {
53738
53835
  return null;
53739
53836
  }
@@ -53783,7 +53880,7 @@ function loadCorpus() {
53783
53880
  for (const f of readdirSync4(CORPUS)) {
53784
53881
  if (!f.endsWith(".md"))
53785
53882
  continue;
53786
- const e = parseCorpusFile(join12(CORPUS, f));
53883
+ const e = parseCorpusFile(join13(CORPUS, f));
53787
53884
  if (e)
53788
53885
  entries.push(e);
53789
53886
  }
@@ -53795,8 +53892,8 @@ function loadCorpus() {
53795
53892
  function detect(entry) {
53796
53893
  const lang = entry.language;
53797
53894
  if (lang === "php") {
53798
- const tmp = join12(tmpdir3(), `blitzstrike-bench-${entry.id}-${process.pid}.php`);
53799
- writeFileSync4(tmp, entry.code);
53895
+ const tmp = join13(tmpdir3(), `blitzstrike-bench-${entry.id}-${process.pid}.php`);
53896
+ writeFileSync5(tmp, entry.code);
53800
53897
  const r = analyzeDataFlow2(tmp);
53801
53898
  rmSync3(tmp, { force: true });
53802
53899
  if (r.findings.length > 0)
@@ -53838,16 +53935,16 @@ function runBenchmark() {
53838
53935
  }
53839
53936
 
53840
53937
  // src/enterprise-benchmark.ts
53841
- import { existsSync as existsSync7, readFileSync as readFileSync16, readdirSync as readdirSync5, statSync as statSync3 } from "node:fs";
53842
- import { join as join13, resolve } from "node:path";
53938
+ import { existsSync as existsSync7, readFileSync as readFileSync17, readdirSync as readdirSync5, statSync as statSync3 } from "node:fs";
53939
+ import { join as join14, resolve } from "node:path";
53843
53940
  var ROOT6 = resolve(process.cwd(), "bench", "enterprise");
53844
53941
  var ENTERPRISE_FIXTURES = [
53845
- { id: "php-laravel", dir: join13(ROOT6, "php-laravel"), expected_vulns: 4 },
53846
- { id: "js-express", dir: join13(ROOT6, "js-express"), expected_vulns: 2 },
53847
- { id: "py-django", dir: join13(ROOT6, "py-django"), expected_vulns: 2 },
53848
- { id: "java-spring", dir: join13(ROOT6, "java-spring"), expected_vulns: 2 },
53849
- { id: "wp-batch-route", dir: join13(ROOT6, "wp-batch-route"), expected_vulns: 1 },
53850
- { id: "php-complex", dir: join13(ROOT6, "php-complex"), expected_vulns: 4 }
53942
+ { id: "php-laravel", dir: join14(ROOT6, "php-laravel"), expected_vulns: 4 },
53943
+ { id: "js-express", dir: join14(ROOT6, "js-express"), expected_vulns: 2 },
53944
+ { id: "py-django", dir: join14(ROOT6, "py-django"), expected_vulns: 2 },
53945
+ { id: "java-spring", dir: join14(ROOT6, "java-spring"), expected_vulns: 2 },
53946
+ { id: "wp-batch-route", dir: join14(ROOT6, "wp-batch-route"), expected_vulns: 1 },
53947
+ { id: "php-complex", dir: join14(ROOT6, "php-complex"), expected_vulns: 4 }
53851
53948
  ];
53852
53949
  function sourceFiles(root) {
53853
53950
  const out = [];
@@ -53859,7 +53956,7 @@ function sourceFiles(root) {
53859
53956
  return;
53860
53957
  }
53861
53958
  for (const e of entries) {
53862
- const p = join13(dir, e);
53959
+ const p = join14(dir, e);
53863
53960
  let st;
53864
53961
  try {
53865
53962
  st = statSync3(p);
@@ -53880,7 +53977,7 @@ function detectFixture(dir) {
53880
53977
  for (const f of sourceFiles(dir)) {
53881
53978
  let code;
53882
53979
  try {
53883
- code = readFileSync16(f, "utf8");
53980
+ code = readFileSync17(f, "utf8");
53884
53981
  } catch {
53885
53982
  continue;
53886
53983
  }
@@ -54066,7 +54163,7 @@ function createServer() {
54066
54163
  name: "blitzstrike",
54067
54164
  version: "1.0.0"
54068
54165
  }, {
54069
- instructions: "Blitz Strike is a universal penetration-testing toolbelt with three tiers — " + "BLITZ (reconnaissance / attack-surface mapping), EAGLE-EYE (source-to-sink " + "analysis), STRIKE (live validation) — plus a canonical evidence-first finding " + "engine and a multi-language taint engine.\\n\\n" + "CORE MENTAL MODEL:\\n" + "- A scanner hit is a HYPOTHESIS, not a finding. Live verification (or data-flow " + " proof) is the verdict. Never report an unverified hit as a vulnerability.\\n" + "- Distinguish REACHABLE from mere PRESENT: a sink is only a finding when tainted " + " input reaches it AND it is not neutralized by a sanitizer and not guarded by an " + " auth gate.\\n\\n" + "HOW TO RUN AN AUDIT (start here):\\n" + "0. ROUTE BY TARGET. Classify automatically - no need to ask. A URL - with or " + " without http/https - is LIVE (live_recon + the " + " web-hunting doctrine + strike_verify). A filesystem path (exists on disk, " + " or a code extension) is SOURCE audit (source-audit doctrine: blitz_scan + " + " eagle_eye2 + complex_scan/route_scan). TWO DOCTRINES - never mix: live " + " web/API -> web-hunting skill; source tree -> source-audit skill. For a WHOLE " + " engagement prefer run_engagement (one call, no babysitting) FIRST; steps 1-5 " + " below are the manual path for targeted follow-up on a specific finding.\\n" + "1. RECORD SCOPE. scope_check classifies the target (intel for the report) - " + " it does NOT gate the engagement. No authorization gate: the repo disclaimer " + " places responsibility on the user. Hunt the target you are given.\\n" + "2. MAP THE SURFACE (BLITZ). For a source tree use blitz_scan (tree) or blitz_file " + " (single file) then enrich_scan to match sinks to escalation chains. For a live " + " URL use live_recon for a full pass (fingerprint/WAF/tech/version/crawler/params/" + " subdomains/API endpoints/intel correlation) — it returns next_steps. active_scan " + " is the lighter alternative. Both record scope internally.\\n" + "3. TRACE REACHABILITY (EAGLE-EYE). For every sink, determine whether attacker " + " input actually reaches it:\\n" + " - PHP/JS/TS/Python/Java source: taint_file (auto-detects language) or " + " taint_scan/taint_tree (PHP AST). list_languages shows what's supported.\\n" + " - Any language: trace_data_flow (window heuristic) and eagle_eye/eagle_grep " + " (function-scoped view of sinks + auth gates). Beyond taint: route_scan (route confusion / dispatch abuse / batch forwarding) and complex_scan (deserialization->POP, type juggling, mass assignment, prototype pollution, CRLF/header injection, path confusion) catch classes plain taint misses.\\n" + " A sink counts only if tainted input reaches it unsanitized and unguarded.\\n" + "4. VALIDATE (STRIKE). Confirm live with strike_verify using a MARKER plus a " + " NEGATIVE CONTROL plus a BASELINE. The verdict is deterministic: marker " + " reflected AND control inert = confirmed; both reflected = false_positive; " + " marker not reflected = unconfirmed. Feed the verdict to strike_resolve to " + " advance the finding lifecycle (hypothesis->validating->confirmed). Use " + " read_tool_manual for the exploit tool's manual, payload_lookup for payloads, " + " detect_waf/tech_correlation for context.\\n" + "5. RECORD + REPORT. Create a canonical finding with finding_create, advance it " + " through the lifecycle with finding_transition, attach confidence_score, and " + " redact any secrets before persisting. Only report findings that survived " + " verification.\\n\\n" + "SUPPORTING LAYERS:\\n" + "- run_engagement: ONE call does the whole audit (no babysitting). Source path -> " + " full static pipeline + inline report (markdown/JSON/SARIF). URL target -> " + " full autonomous live pipeline (live_recon recon + deterministic checks: reflected " + " input, security headers, open redirect, path traversal, CORS) + findings + report. " + " PREFER THIS FIRST for a whole engagement; drive " + " the steps above manually only for targeted follow-up on a specific finding.\\n" + "- run_autonomous: run_engagement + the escalation path pre-attached to every " + " finding (chain name/severity/prerequisites + ordered next_steps with tool_hint/" + " success_criteria/negative_control) + a deterministic termination reason. Use it " + " when you want the 'what to do next' already computed per finding.\\n" + "- list_chains: 57 escalation chains (source -> sink -> impact).\\n" + "- list_languages / taint_file: multi-language taint (PHP, JS/TS, Python, Java).\\n" + "- tool_lookup / ensure_tool / list_tools: 130-tool catalog, auto-install.\\n" + "- read_playbook / list_playbooks: 17 engagement playbooks (web-app, api-security, " + " source-code-audit, active-directory, mobile, external-attack-surface, ...).\\n" + "- skill_lookup / read_skill / list_skills: 39 skills (7 bs-* agent doctrine: " + " orchestrate, scope, source-audit, web-hunting, verify, review, report; + " + " technique playbooks: WP/PHP 0-day, AD, malware, mobile, cloud).\\n" + "- read_tool_manual / list_manuals: 317 tool manuals (sqlmap, nmap, commix, ...).\\n" + "- payload_lookup / read_payload: 66 payload collections (sqli, xss, ssti, xxe, lfi).\\n" + "- detect_waf / tech_correlation / cve_correlation / port_correlation: signature " + " + correlation intelligence.\\n" + "- nvd_lookup / fofa_search: CVE lookup + asset reconnaissance (FOFA needs env creds).\\n" + "- remember / memory_lookup / memory_list: persist + recall verified knowledge.\\n\\n" + "RESOURCE MAP (know what to reach for):\\n" + "- ORCHESTRATION = the engagement framework. orchestration() returns the " + " lifecycle (scope->recon->analyze->deep->verify->report, each with " + " entry/exit criteria) + sub-agent team + handoff contract + termination " + " criteria. Load it at engagement start. Autonomous driving aids: plan() " + " (deterministic phase plan + next action), delegate() (parallel-first " + " subagent batches + retry pattern), worktree() (isolate a workstream), " + " watchdog() (detect a stalled phase), context_prune() (compact state so " + " you can drop raw detail).\\n" + "- TOOL = acts (scan, taint, verify, record, correlate). Call it to DO something.\\n" + "- PLAYBOOK = campaign plan for a target TYPE. read_playbook(web-application) " + " BEFORE a full engagement of that type.\\n" + "- SKILL = procedure doctrine. Load the bs-* skill for the phase you are on " + " (orchestrate, scope, web-hunting for LIVE, source-audit for SOURCE, verify, " + " review, report); load a technique skill for the specific technique.\\n" + "- MANUAL = deep reference for ONE tool. read_tool_manual right before using it.\\n" + "- CATALOG = external tools (130: sqlmap, nmap, nuclei, ffuf, ysoserial, ...). " + " Blitz Strike's OWN tools find + verify; the catalog is for deeper EXTERNAL " + " exploitation/enumeration. After your own analysis confirms a finding, run " + " tool_lookup -> ensure_tool (auto-install) -> read_tool_manual to drive the " + " external tool that demonstrates impact.\\n" + "- PAYLOAD = attack payloads for a class. payload_lookup before crafting a probe.\\n" + "- INTELLIGENCE = knowledge. list_attack_vectors (34 categories) maps the " + " full surface during RECON; list_chains (57) turns a finding into impact; " + " chain_links composes chains for complex bugs; bypass_lookup when blocked; " + " payload_lookup when crafting a probe. technique_lookup(cls) returns the " + " DETAILED methodology + tricks for a vulnerability class (ssrf, ssti, " + " jwt, deserialization, ...) - load before testing a class. wstg_map maps " + " an OWASP category to coverage; taxonomy maps findings to standard IDs " + " (OWASP Top 10 / API Top 10 / CWE / ASVS); doctrine_map shows how everything links.\\n\\n" + "NO-BABYSITTING (while scanning + testing):\\n" + "- Scan the WHOLE surface before reporting - every file, entry point, and sink. " + " Do not stop after the first hit to ask; keep enumerating the full tree.\\n" + "- Test every hypothesis through its full verification cycle - vary probes on " + " failure, apply bypasses when blocked, keep going. Do not pause to ask.\\n" + "- Persist through partial results: a scan that surfaces few sinks is NOT done - " + " widen the surface, escalate detectors, dig into each reachable sink.\\n" + "- NEVER ask 'should I continue?' / 'proceed?' / approval-style questions. " + " Continue autonomously - the moment one step verifies, start the next. " + " Pause ONLY when: a genuinely destructive/DoS action needs consent, an " + " external dependency beyond your control blocks you (e.g. phone OTP), or " + " the engagement is COMPLETE (deliver the final report). Otherwise keep hunting.\\n\\n" + "PERSISTENCE (complex bugs - do not give up):\\n" + "- There is NO retry cap. A failed probe/sub-agent is diagnosed and resumed " + " until verified or definitively ruled out - never move on unverified.\\n" + "- On any failure (empty output, blocked probe, failed sub-agent/model), call " + " retry_guidance(signal) for concrete recovery actions before giving up.\\n" + "- On a model/sub-agent error (rate-limit, quota, timeout, 5xx, auth, " + " unavailable), model_fallback(signal) classifies it and returns the exact " + " recovery (retry+backoff / fall back / shrink scope / fix credentials / " + " abort). Do not fall back blindly - the class decides.\\n" + "- One failed probe != not vulnerable. Vary: alternative sinks, other params, " + " encoding/case/whitespace bypasses, different injection context.\\n" + "- Empty output != no findings. Re-run (server may be stale after an upgrade - " + " /reload-mcp), or escalate to route_scan / complex_scan for classes taint " + " misses (POP chains, type juggling, mass assignment, route confusion).\\n" + "- A 'sanitized' sink may not be safe. Challenge the sanitizer: loose == vs ===, " + " missing EXTR_SKIP, int cast truncation, weak regex.\\n" + "- An unconfirmed hypothesis is KEPT as hypothesis - try alternate verification, " + " then let adversarial review decide.\\n" + "- Complex chains (deserialization->POP, LFI->RCE) need the FULL chain traced, " + ` not just the first sink.\\n
54166
+ instructions: "Blitz Strike is a universal penetration-testing toolbelt with three tiers — " + "BLITZ (reconnaissance / attack-surface mapping), EAGLE-EYE (source-to-sink " + "analysis), STRIKE (live validation) — plus a canonical evidence-first finding " + "engine and a multi-language taint engine.\\n\\n" + "CORE MENTAL MODEL:\\n" + "- A scanner hit is a HYPOTHESIS, not a finding. Live verification (or data-flow " + " proof) is the verdict. Never report an unverified hit as a vulnerability.\\n" + "- Distinguish REACHABLE from mere PRESENT: a sink is only a finding when tainted " + " input reaches it AND it is not neutralized by a sanitizer and not guarded by an " + " auth gate.\\n\\n" + "HOW TO RUN AN AUDIT (start here):\\n" + "0. ROUTE BY TARGET. Classify automatically - no need to ask. A URL - with or " + " without http/https - is LIVE (live_recon + the " + " web-hunting doctrine + strike_verify). A filesystem path (exists on disk, " + " or a code extension) is SOURCE audit (source-audit doctrine: blitz_scan + " + " eagle_eye2 + complex_scan/route_scan). TWO DOCTRINES - never mix: live " + " web/API -> web-hunting skill; source tree -> source-audit skill. For a WHOLE " + " engagement prefer run_engagement (one call, no babysitting) FIRST; steps 1-5 " + " below are the manual path for targeted follow-up on a specific finding.\\n" + "1. RECORD SCOPE. scope_check classifies the target (intel for the report) - " + " it does NOT gate the engagement. No authorization gate: the repo disclaimer " + " places responsibility on the user. Hunt the target you are given.\\n" + "2. MAP THE SURFACE (BLITZ). For a source tree use blitz_scan (tree) or blitz_file " + " (single file) then enrich_scan to match sinks to escalation chains. For a live " + " URL use live_recon for a full pass (fingerprint/WAF/tech/version/crawler/params/" + " subdomains/API endpoints/intel correlation) — it returns next_steps. active_scan " + " is the lighter alternative. Both record scope internally.\\n" + "3. TRACE REACHABILITY (EAGLE-EYE). For every sink, determine whether attacker " + " input actually reaches it:\\n" + " - PHP/JS/TS/Python/Java source: taint_file (auto-detects language) or " + " taint_scan/taint_tree (PHP AST). list_languages shows what's supported.\\n" + " - Any language: trace_data_flow (window heuristic) and eagle_eye/eagle_grep " + " (function-scoped view of sinks + auth gates). Beyond taint: route_scan (route confusion / dispatch abuse / batch forwarding) and complex_scan (deserialization->POP, type juggling, mass assignment, prototype pollution, CRLF/header injection, path confusion) catch classes plain taint misses.\\n" + " A sink counts only if tainted input reaches it unsanitized and unguarded.\\n" + "4. VALIDATE (STRIKE). Confirm live with strike_verify using a MARKER plus a " + " NEGATIVE CONTROL plus a BASELINE. The verdict is deterministic: marker " + " reflected AND control inert = confirmed; both reflected = false_positive; " + " marker not reflected = unconfirmed. Feed the verdict to strike_resolve to " + " advance the finding lifecycle (hypothesis->validating->confirmed). Use " + " read_tool_manual for the exploit tool's manual, payload_lookup for payloads, " + " detect_waf/tech_correlation for context.\\n" + "5. RECORD + REPORT. Create a canonical finding with finding_create, advance it " + " through the lifecycle with finding_transition, attach confidence_score, and " + " redact any secrets before persisting. Only report findings that survived " + " verification.\\n\\n" + "SUPPORTING LAYERS:\\n" + "- run_engagement: ONE call does the whole audit (no babysitting). Source path -> " + " full static pipeline + inline report (markdown/JSON/SARIF). URL target -> " + " full autonomous live pipeline (live_recon recon + deterministic checks: reflected " + " input, security headers, open redirect, path traversal, CORS) + findings + report. " + " PREFER THIS FIRST for a whole engagement; drive " + " the steps above manually only for targeted follow-up on a specific finding.\\n" + "- run_autonomous: run_engagement + the escalation path pre-attached to every " + " finding (chain name/severity/prerequisites + ordered next_steps with tool_hint/" + " success_criteria/negative_control) + a deterministic termination reason. Use it " + " when you want the 'what to do next' already computed per finding.\\n" + "- list_chains: 57 escalation chains (source -> sink -> impact).\\n" + "- list_languages / taint_file: multi-language taint (PHP, JS/TS, Python, Java).\\n" + "- tool_lookup / ensure_tool / list_tools: 130-tool catalog, auto-install.\\n" + "- read_playbook / list_playbooks: 17 engagement playbooks (web-app, api-security, " + " source-code-audit, active-directory, mobile, external-attack-surface, ...).\\n" + "- skill_lookup / read_skill / list_skills: 39 skills (7 bs-* agent doctrine: " + " orchestrate, scope, source-audit, web-hunting, verify, review, report; + " + " technique playbooks: WP/PHP 0-day, AD, malware, mobile, cloud).\\n" + "- read_tool_manual / list_manuals: 317 tool manuals (sqlmap, nmap, commix, ...).\\n" + "- payload_lookup / read_payload: 66 payload collections (sqli, xss, ssti, xxe, lfi).\\n" + "- detect_waf / tech_correlation / cve_correlation / port_correlation: signature " + " + correlation intelligence.\\n" + "- nvd_lookup / fofa_search: CVE lookup + asset reconnaissance (FOFA needs env creds).\\n" + "- remember / memory_lookup / memory_list: persist + recall verified knowledge.\\n\\n" + "RESOURCE MAP (know what to reach for):\\n" + "- ORCHESTRATION = the engagement framework. orchestration() returns the " + " lifecycle (scope->recon->analyze->deep->verify->report, each with " + " entry/exit criteria) + sub-agent team + handoff contract + termination " + " criteria. Load it at engagement start. Autonomous driving aids: plan() " + " (deterministic phase plan + next action), delegate() (parallel-first " + " subagent batches + retry pattern), worktree() (isolate a workstream), " + " watchdog() (detect a stalled phase), context_prune() (compact state so " + " you can drop raw detail), and task_start/task_checkpoint/task_status/" + " task_resume (per-workstream checkpoint journal — resume a failed task " + " from its last completed step, never restart).\\n" + "- TOOL = acts (scan, taint, verify, record, correlate). Call it to DO something.\\n" + "- PLAYBOOK = campaign plan for a target TYPE. read_playbook(web-application) " + " BEFORE a full engagement of that type.\\n" + "- SKILL = procedure doctrine. Load the bs-* skill for the phase you are on " + " (orchestrate, scope, web-hunting for LIVE, source-audit for SOURCE, verify, " + " review, report); load a technique skill for the specific technique.\\n" + "- MANUAL = deep reference for ONE tool. read_tool_manual right before using it.\\n" + "- CATALOG = external tools (130: sqlmap, nmap, nuclei, ffuf, ysoserial, ...). " + " Blitz Strike's OWN tools find + verify; the catalog is for deeper EXTERNAL " + " exploitation/enumeration. After your own analysis confirms a finding, run " + " tool_lookup -> ensure_tool (auto-install) -> read_tool_manual to drive the " + " external tool that demonstrates impact.\\n" + "- PAYLOAD = attack payloads for a class. payload_lookup before crafting a probe.\\n" + "- INTELLIGENCE = knowledge. list_attack_vectors (34 categories) maps the " + " full surface during RECON; list_chains (57) turns a finding into impact; " + " chain_links composes chains for complex bugs; bypass_lookup when blocked; " + " payload_lookup when crafting a probe. technique_lookup(cls) returns the " + " DETAILED methodology + tricks for a vulnerability class (ssrf, ssti, " + " jwt, deserialization, ...) - load before testing a class. wstg_map maps " + " an OWASP category to coverage; taxonomy maps findings to standard IDs " + " (OWASP Top 10 / API Top 10 / CWE / ASVS); doctrine_map shows how everything links.\\n\\n" + "NO-BABYSITTING (while scanning + testing):\\n" + "- Scan the WHOLE surface before reporting - every file, entry point, and sink. " + " Do not stop after the first hit to ask; keep enumerating the full tree.\\n" + "- Test every hypothesis through its full verification cycle - vary probes on " + " failure, apply bypasses when blocked, keep going. Do not pause to ask.\\n" + "- Persist through partial results: a scan that surfaces few sinks is NOT done - " + " widen the surface, escalate detectors, dig into each reachable sink.\\n" + "- NEVER ask 'should I continue?' / 'proceed?' / approval-style questions. " + " Continue autonomously - the moment one step verifies, start the next. " + " Pause ONLY when: a genuinely destructive/DoS action needs consent, an " + " external dependency beyond your control blocks you (e.g. phone OTP), or " + " the engagement is COMPLETE (deliver the final report). Otherwise keep hunting.\\n\\n" + "PERSISTENCE (complex bugs - do not give up):\\n" + "- There is NO retry cap. A failed probe/sub-agent is diagnosed and resumed " + " until verified or definitively ruled out - never move on unverified.\\n" + "- On any failure (empty output, blocked probe, failed sub-agent/model), call " + " retry_guidance(signal) for concrete recovery actions before giving up.\\n" + "- On a model/sub-agent error (rate-limit, quota, timeout, 5xx, auth, " + " unavailable), model_fallback(signal) classifies it and returns the exact " + " recovery (retry+backoff / fall back / shrink scope / fix credentials / " + " abort). Do not fall back blindly - the class decides.\\n" + "- One failed probe != not vulnerable. Vary: alternative sinks, other params, " + " encoding/case/whitespace bypasses, different injection context.\\n" + "- Empty output != no findings. Re-run (server may be stale after an upgrade - " + " /reload-mcp), or escalate to route_scan / complex_scan for classes taint " + " misses (POP chains, type juggling, mass assignment, route confusion).\\n" + "- A 'sanitized' sink may not be safe. Challenge the sanitizer: loose == vs ===, " + " missing EXTR_SKIP, int cast truncation, weak regex.\\n" + "- An unconfirmed hypothesis is KEPT as hypothesis - try alternate verification, " + " then let adversarial review decide.\\n" + "- Complex chains (deserialization->POP, LFI->RCE) need the FULL chain traced, " + ` not just the first sink.\\n
54070
54167
  ` + "- When a probe is BLOCKED by a WAF/filter/sanitizer, call bypass_lookup(defense) " + " for concrete techniques (encoding, case, comment insertion, chunked, null byte, " + " double-encoding, verb tampering, header spoofing) and apply them - do not give up.\\n" + "- CHAIN findings: a low-severity finding may escalate — do not assume it is the " + " end of the story. list_chains / chain_links show whether it composes into a " + " multi-stage chain (e.g. SSRF->metadata, SQLi->RCE, LFI->RCE, upload->shell). " + " Check before concluding, and do not overclaim an escalation that is not there.\\n\\n" + "- Complex bugs chain ACROSS chains: after confirming one chain, chain_links " + " shows what follows (SSRF->metadata then default-creds->pivot via stolen IAM). " + " Trace the full composition, not just a single chain.\\n\\n" + "HYPOTHESIS EXPANSION (from each finding, enumerate + verify every follow-up):\\n" + "- A confirmed finding is the START, not the end. From each one, enumerate " + " the follow-ups: ESCALATE (list_chains + chain_links), CHAIN (chain_links " + " from->to), BYPASS (bypass_lookup), PIVOT (new endpoints/sinks now reachable).\\n" + "- Every follow-up (this might happen, this could chain, maybe bypassable) is " + " a HYPOTHESIS, not a finding. Verify EACH via strike_verify (marker + negative " + " control) before it enters the report.\\n" + "- Never report speculation: might / could / possibly live in the " + " hypothesis list, never in the report. Every report line is a verified fact.\\n\\n" + "PROFESSIONAL JUDGMENT (act like a senior tester, not a checklist runner):\\n" + "- PRIORITIZE by severity, computed deterministically with cvss_score (impact x " + " exploitability) and weighted by escalation potential (chain_links). Do NOT rank " + " by bug class - a critical stored XSS that yields account takeover outranks an " + " unreachable blind RCE. Weight by the engagement context (which asset/data is " + " most sensitive for THIS target).\\n" + "- Recognize dead ends: if a branch yields nothing after a few varied probes, " + " note it and move on. Do not tunnel-vision one sink at the expense of the rest.\\n" + "- Live-test with care: respect rate limits, do not lock accounts, do not " + " trigger DoS. Test non-destructively and prove impact with the smallest payload.\\n" + "- Know when done: stop when the termination criteria are met, not when you feel " + " tired. A thorough audit over a rushed one, every time.\\n\\n" + "IRON RULES:\\n" + "- No authorization gate - the repo disclaimer places responsibility on the " + " user. Hunt the target you are given (like nmap/sqlmap/Burp, which do not " + " ask). The one hard stop: malicious/third-party infra (cari pelaku) - hold/static.\\n" + " need finer control."
54071
54168
  });
54072
54169
  server.registerTool("blitz_scan", {
@@ -54736,6 +54833,48 @@ ${statusLine}` : statusLine },
54736
54833
  const { batches, total } = decomposeBatches(tasks);
54737
54834
  return { content: [{ type: "text", text: JSON.stringify({ total, batches, retry: retryPattern("a workstream") }, null, 2) }] };
54738
54835
  });
54836
+ server.registerTool("task_start", {
54837
+ title: "Register a task with a checkpointed plan",
54838
+ description: "ORCHESTRATION: register a delegated workstream with an ordered plan (steps). Every step records a checkpoint so a failed workstream can be RESUMED from its last completed step instead of restarting. On-disk, so checkpoints survive restarts.",
54839
+ inputSchema: {
54840
+ task_id: string2().describe("Unique task id (e.g. 'recon-api', 'audit-auth')"),
54841
+ objective: string2().describe("What the task must accomplish"),
54842
+ plan: array(string2()).describe("Ordered steps (e.g. ['recon','analyze','verify','report'])")
54843
+ }
54844
+ }, async ({ task_id, objective, plan }) => {
54845
+ return { content: [{ type: "text", text: JSON.stringify(taskStart(task_id, objective, plan), null, 2) }] };
54846
+ });
54847
+ server.registerTool("task_checkpoint", {
54848
+ title: "Record a task checkpoint",
54849
+ description: "ORCHESTRATION: record a checkpoint for a task step (in_progress / done / failed / blocked) with an optional note + output hash. Advancing 'done' moves current_step forward; a 'failed' checkpoint marks the task for resume.",
54850
+ inputSchema: {
54851
+ task_id: string2().describe("Task id"),
54852
+ step: string2().describe("Step name (must be in the plan)"),
54853
+ status: _enum(["in_progress", "done", "failed", "blocked"]).describe("Checkpoint status"),
54854
+ note: string2().optional().describe("What happened at this step"),
54855
+ output_hash: string2().optional().describe("Hash of the artifacts produced (for resume dedup)")
54856
+ }
54857
+ }, async ({ task_id, step, status, note, output_hash }) => {
54858
+ return { content: [{ type: "text", text: JSON.stringify(taskCheckpoint(task_id, step, status, note, output_hash), null, 2) }] };
54859
+ });
54860
+ server.registerTool("task_status", {
54861
+ title: "Read a task's checkpoint state",
54862
+ description: "ORCHESTRATION: read a task's current state — status, current step, progress (done/total), remaining steps, and full checkpoint history. The orchestrator's deterministic view of where each workstream is.",
54863
+ inputSchema: {
54864
+ task_id: string2().describe("Task id")
54865
+ }
54866
+ }, async ({ task_id }) => {
54867
+ return { content: [{ type: "text", text: JSON.stringify(taskStatus(task_id), null, 2) }] };
54868
+ });
54869
+ server.registerTool("task_resume", {
54870
+ title: "Resume a task from its last checkpoint",
54871
+ description: "ORCHESTRATION: compute the resume instruction for a failed/blocked task — the last completed checkpoint, the current step, the remaining steps, and the exact re-dispatch prompt (resume from where it stopped, not a fresh start).",
54872
+ inputSchema: {
54873
+ task_id: string2().describe("Task id")
54874
+ }
54875
+ }, async ({ task_id }) => {
54876
+ return { content: [{ type: "text", text: JSON.stringify(taskResume(task_id), null, 2) }] };
54877
+ });
54739
54878
  server.registerTool("worktree", {
54740
54879
  title: "Isolate a git worktree",
54741
54880
  description: "ORCHESTRATION: create/list/remove an isolated git worktree so parallel workstreams don't conflict on the same files. add: `git worktree add -b <branch> <path>`; list: enumerate existing; remove: clean up after a workstream.",
@@ -54978,7 +55117,7 @@ ${statusLine}` : statusLine },
54978
55117
  description: "Route confusion (separate class from source→sink taint): detect attacker-controlled dispatch (call_user_func/forward/dispatch), dynamic method calls ($obj->$m()), dynamic include (LFI), batch/proxy forwarding without per-route auth re-check, and route matching without path normalization.",
54979
55118
  inputSchema: { path: string2().describe("Source file path (PHP)") }
54980
55119
  }, async ({ path }) => {
54981
- const code = readFileSync18(path, "utf8");
55120
+ const code = readFileSync19(path, "utf8");
54982
55121
  const findings = detectRouteConfusion(code, path);
54983
55122
  return {
54984
55123
  content: [{
@@ -54992,7 +55131,7 @@ ${statusLine}` : statusLine },
54992
55131
  description: "Complex-bug detection beyond taint: deserialization→POP gadget chain (unserialize + magic method), type juggling (loose ==/!= vs hash/secret), mass assignment (extract/parse_str without a safe flag), prototype pollution (unsafe merge of request data), CRLF/header injection (user input into a header), and path confusion (user input into a file path without canonicalization).",
54993
55132
  inputSchema: { path: string2().describe("Source file path") }
54994
55133
  }, async ({ path }) => {
54995
- const code = readFileSync18(path, "utf8");
55134
+ const code = readFileSync19(path, "utf8");
54996
55135
  const findings = detectComplexBugs(code, path);
54997
55136
  return {
54998
55137
  content: [{
@@ -55059,7 +55198,7 @@ ${statusLine}` : statusLine },
55059
55198
  let lang = language;
55060
55199
  if (path && !code) {
55061
55200
  try {
55062
- src = readFileSync18(path, "utf8");
55201
+ src = readFileSync19(path, "utf8");
55063
55202
  } catch {
55064
55203
  return { content: [{ type: "text", text: JSON.stringify({ error: `cannot read ${path}` }) }] };
55065
55204
  }
@@ -55290,23 +55429,23 @@ async function serve() {
55290
55429
  }
55291
55430
 
55292
55431
  // src/cli.ts
55293
- import { readFileSync as readFileSync21, existsSync as existsSync9, writeFileSync as writeFileSync6, mkdirSync as mkdirSync4 } from "node:fs";
55294
- import { join as join17, dirname as dirname2 } from "node:path";
55432
+ import { readFileSync as readFileSync22, existsSync as existsSync9, writeFileSync as writeFileSync7, mkdirSync as mkdirSync5 } from "node:fs";
55433
+ import { join as join18, dirname as dirname2 } from "node:path";
55295
55434
  import { homedir as homedir5 } from "node:os";
55296
- import { fileURLToPath as fileURLToPath11 } from "node:url";
55435
+ import { fileURLToPath as fileURLToPath12 } from "node:url";
55297
55436
  import { spawnSync as spawnSync3 } from "node:child_process";
55298
55437
 
55299
55438
  // src/release.ts
55300
- import { readFileSync as readFileSync19, existsSync as existsSync8 } from "node:fs";
55301
- import { join as join15 } from "node:path";
55302
- import { fileURLToPath as fileURLToPath9 } from "node:url";
55439
+ import { readFileSync as readFileSync20, existsSync as existsSync8 } from "node:fs";
55440
+ import { join as join16 } from "node:path";
55441
+ import { fileURLToPath as fileURLToPath10 } from "node:url";
55303
55442
  import { execSync as execSync4 } from "node:child_process";
55304
- var ROOT8 = join15(fileURLToPath9(new URL(".", import.meta.url)), "..");
55443
+ var ROOT8 = join16(fileURLToPath10(new URL(".", import.meta.url)), "..");
55305
55444
  function validateRelease() {
55306
55445
  const checks = [];
55307
55446
  checks.push({ name: "package.json version", ok: true, detail: VERSION });
55308
55447
  try {
55309
- const changelog = readFileSync19(join15(ROOT8, "CHANGELOG.md"), "utf8");
55448
+ const changelog = readFileSync20(join16(ROOT8, "CHANGELOG.md"), "utf8");
55310
55449
  const m = changelog.match(/^##\s+\[([0-9]+\.[0-9]+\.[0-9]+)\]/m);
55311
55450
  const changelogVersion = m?.[1] ?? null;
55312
55451
  checks.push({
@@ -55338,18 +55477,18 @@ function validateRelease() {
55338
55477
  return { version: VERSION, checks, consistent: checks.filter((c) => !c.soft).every((c) => c.ok) };
55339
55478
  }
55340
55479
  function lockfilePresent() {
55341
- return existsSync8(join15(ROOT8, "package-lock.json")) || existsSync8(join15(ROOT8, "bun.lock")) || existsSync8(join15(ROOT8, "bun.lockb"));
55480
+ return existsSync8(join16(ROOT8, "package-lock.json")) || existsSync8(join16(ROOT8, "bun.lock")) || existsSync8(join16(ROOT8, "bun.lockb"));
55342
55481
  }
55343
55482
 
55344
55483
  // src/dependency.ts
55345
- import { readFileSync as readFileSync20 } from "node:fs";
55346
- import { join as join16 } from "node:path";
55347
- import { fileURLToPath as fileURLToPath10 } from "node:url";
55348
- var ROOT9 = join16(fileURLToPath10(new URL(".", import.meta.url)), "..");
55484
+ import { readFileSync as readFileSync21 } from "node:fs";
55485
+ import { join as join17 } from "node:path";
55486
+ import { fileURLToPath as fileURLToPath11 } from "node:url";
55487
+ var ROOT9 = join17(fileURLToPath11(new URL(".", import.meta.url)), "..");
55349
55488
  function checkDependencies() {
55350
55489
  let pkg = {};
55351
55490
  try {
55352
- pkg = JSON.parse(readFileSync20(join16(ROOT9, "package.json"), "utf8"));
55491
+ pkg = JSON.parse(readFileSync21(join17(ROOT9, "package.json"), "utf8"));
55353
55492
  } catch {
55354
55493
  return { install_scripts: [], git_dependencies: [], total_dependencies: 0, safe: false };
55355
55494
  }
@@ -55372,7 +55511,7 @@ function checkDependencies() {
55372
55511
  }
55373
55512
 
55374
55513
  // src/cli.ts
55375
- var ROOT10 = join17(fileURLToPath11(new URL(".", import.meta.url)), "..");
55514
+ var ROOT10 = join18(fileURLToPath12(new URL(".", import.meta.url)), "..");
55376
55515
  function which(cmd) {
55377
55516
  return spawnSync3(`command -v ${cmd}`, { shell: true }).status === 0;
55378
55517
  }
@@ -55407,7 +55546,7 @@ function runDoctor() {
55407
55546
  detail: fofa ? "set" : "FOFA_EMAIL/FOFA_KEY not set",
55408
55547
  fix: fofa ? undefined : "export FOFA_EMAIL=... && export FOFA_KEY=... (enables fofa_search)"
55409
55548
  });
55410
- const dataOk = existsSync9(join17(ROOT10, "chains.json")) && existsSync9(join17(ROOT10, "tools-catalog.json"));
55549
+ const dataOk = existsSync9(join18(ROOT10, "chains.json")) && existsSync9(join18(ROOT10, "tools-catalog.json"));
55411
55550
  issues.push({
55412
55551
  name: "Data layers (chains + tools-catalog)",
55413
55552
  status: dataOk ? "ok" : "fail",
@@ -55449,21 +55588,21 @@ BlitzStrike — ${verdict}`);
55449
55588
  function resolveCommand() {
55450
55589
  if (which("blitzstrike"))
55451
55590
  return { command: "blitzstrike", args: ["serve", "--mcp"] };
55452
- const src = join17(ROOT10, "src", "index.ts");
55591
+ const src = join18(ROOT10, "src", "index.ts");
55453
55592
  if (which("bun") && existsSync9(src))
55454
55593
  return { command: "bun", args: ["run", src, "serve", "--mcp"] };
55455
55594
  if (which("npx"))
55456
55595
  return { command: "npx", args: ["-y", "blitzstrike", "serve", "--mcp"] };
55457
55596
  if (which("bunx"))
55458
55597
  return { command: "bunx", args: ["blitzstrike", "serve", "--mcp"] };
55459
- const dist = join17(ROOT10, "dist", "index.js");
55598
+ const dist = join18(ROOT10, "dist", "index.js");
55460
55599
  if (existsSync9(dist))
55461
55600
  return { command: "bun", args: [dist, "serve", "--mcp"] };
55462
55601
  return { command: "npx", args: ["-y", "blitzstrike", "serve", "--mcp"] };
55463
55602
  }
55464
55603
  function readJson(p) {
55465
55604
  try {
55466
- return JSON.parse(readFileSync21(p, "utf8"));
55605
+ return JSON.parse(readFileSync22(p, "utf8"));
55467
55606
  } catch {
55468
55607
  return null;
55469
55608
  }
@@ -55472,41 +55611,41 @@ function jsonMcpServersWrite(p) {
55472
55611
  return (command, args) => {
55473
55612
  const dir = dirname2(p);
55474
55613
  if (!existsSync9(dir))
55475
- mkdirSync4(dir, { recursive: true });
55614
+ mkdirSync5(dir, { recursive: true });
55476
55615
  const existing = readJson(p) ?? {};
55477
55616
  existing.mcpServers = {
55478
55617
  ...existing.mcpServers ?? {},
55479
55618
  blitzstrike: { command, args }
55480
55619
  };
55481
- writeFileSync6(p, JSON.stringify(existing, null, 2));
55620
+ writeFileSync7(p, JSON.stringify(existing, null, 2));
55482
55621
  };
55483
55622
  }
55484
55623
  function opencodeWrite(p) {
55485
55624
  return (_command, _args) => {
55486
55625
  const dir = dirname2(p);
55487
55626
  if (!existsSync9(dir))
55488
- mkdirSync4(dir, { recursive: true });
55627
+ mkdirSync5(dir, { recursive: true });
55489
55628
  const existing = readJson(p) ?? {};
55490
55629
  existing.mcp = {
55491
55630
  ...existing.mcp ?? {},
55492
55631
  blitzstrike: { type: "local", command: ["npx", "-y", "blitzstrike", "serve", "--mcp"], enabled: true }
55493
55632
  };
55494
- writeFileSync6(p, JSON.stringify(existing, null, 2));
55633
+ writeFileSync7(p, JSON.stringify(existing, null, 2));
55495
55634
  copyOpenCodeAgents();
55496
55635
  };
55497
55636
  }
55498
55637
  function copyOpenCodeAgents() {
55499
- const srcDir = join17(ROOT10, "opencode-agents");
55500
- const dstDir = join17(homedir5(), ".config", "opencode", "agents");
55638
+ const srcDir = join18(ROOT10, "opencode-agents");
55639
+ const dstDir = join18(homedir5(), ".config", "opencode", "agents");
55501
55640
  if (!existsSync9(srcDir))
55502
55641
  return;
55503
55642
  if (!existsSync9(dstDir))
55504
- mkdirSync4(dstDir, { recursive: true });
55643
+ mkdirSync5(dstDir, { recursive: true });
55505
55644
  for (const f of ["Blitz Strike.md", "Blitz.md", "Eagle Eye.md", "Strike.md"]) {
55506
- const src = join17(srcDir, f);
55507
- const dst = join17(dstDir, f);
55645
+ const src = join18(srcDir, f);
55646
+ const dst = join18(dstDir, f);
55508
55647
  if (existsSync9(src)) {
55509
- writeFileSync6(dst, readFileSync21(src, "utf8"));
55648
+ writeFileSync7(dst, readFileSync22(src, "utf8"));
55510
55649
  }
55511
55650
  }
55512
55651
  }
@@ -55514,8 +55653,8 @@ function codexWrite(p) {
55514
55653
  return (command, args) => {
55515
55654
  const dir = dirname2(p);
55516
55655
  if (!existsSync9(dir))
55517
- mkdirSync4(dir, { recursive: true });
55518
- let existing = existsSync9(p) ? readFileSync21(p, "utf8") : "";
55656
+ mkdirSync5(dir, { recursive: true });
55657
+ let existing = existsSync9(p) ? readFileSync22(p, "utf8") : "";
55519
55658
  if (!existing.trimEnd().endsWith(`
55520
55659
  `))
55521
55660
  existing += `
@@ -55526,15 +55665,15 @@ function codexWrite(p) {
55526
55665
  command = "${command}"
55527
55666
  args = ${JSON.stringify(args)}
55528
55667
  `;
55529
- writeFileSync6(p, existing);
55668
+ writeFileSync7(p, existing);
55530
55669
  };
55531
55670
  }
55532
55671
  function hermesWrite(p) {
55533
55672
  return (command, args) => {
55534
55673
  const dir = dirname2(p);
55535
55674
  if (!existsSync9(dir))
55536
- mkdirSync4(dir, { recursive: true });
55537
- let existing = existsSync9(p) ? readFileSync21(p, "utf8") : "";
55675
+ mkdirSync5(dir, { recursive: true });
55676
+ let existing = existsSync9(p) ? readFileSync22(p, "utf8") : "";
55538
55677
  existing = existing.replace(/^ blitzstrike:\n(?: .*\n?)*/m, "");
55539
55678
  if (!existing.trimEnd().endsWith(`
55540
55679
  `))
@@ -55555,80 +55694,80 @@ mcp_servers:
55555
55694
  args: ${JSON.stringify(args)}
55556
55695
  `;
55557
55696
  }
55558
- writeFileSync6(p, existing);
55697
+ writeFileSync7(p, existing);
55559
55698
  };
55560
55699
  }
55561
55700
  function detectAgents() {
55562
55701
  const home = homedir5();
55563
55702
  const agents = [];
55564
- const claudeJson = join17(home, ".claude.json");
55703
+ const claudeJson = join18(home, ".claude.json");
55565
55704
  agents.push({
55566
55705
  name: "Claude Code",
55567
55706
  detectPath: claudeJson,
55568
55707
  installed: () => existsSync9(claudeJson) || which("claude"),
55569
55708
  write: jsonMcpServersWrite(claudeJson)
55570
55709
  });
55571
- const claudeDesktop = join17(home, ".config", "Claude", "claude_desktop_config.json");
55710
+ const claudeDesktop = join18(home, ".config", "Claude", "claude_desktop_config.json");
55572
55711
  agents.push({
55573
55712
  name: "Claude Desktop",
55574
55713
  detectPath: claudeDesktop,
55575
55714
  installed: () => existsSync9(claudeDesktop),
55576
55715
  write: jsonMcpServersWrite(claudeDesktop)
55577
55716
  });
55578
- const cursor = join17(home, ".cursor", "mcp.json");
55717
+ const cursor = join18(home, ".cursor", "mcp.json");
55579
55718
  agents.push({
55580
55719
  name: "Cursor",
55581
55720
  detectPath: cursor,
55582
- installed: () => existsSync9(join17(home, ".cursor")) || which("cursor"),
55721
+ installed: () => existsSync9(join18(home, ".cursor")) || which("cursor"),
55583
55722
  write: jsonMcpServersWrite(cursor)
55584
55723
  });
55585
- const opencode = join17(home, ".config", "opencode", "opencode.jsonc");
55724
+ const opencode = join18(home, ".config", "opencode", "opencode.jsonc");
55586
55725
  agents.push({
55587
55726
  name: "OpenCode",
55588
55727
  detectPath: opencode,
55589
- installed: () => existsSync9(join17(home, ".config", "opencode")) || which("opencode"),
55728
+ installed: () => existsSync9(join18(home, ".config", "opencode")) || which("opencode"),
55590
55729
  write: opencodeWrite(opencode)
55591
55730
  });
55592
- const codex = join17(home, ".codex", "config.toml");
55731
+ const codex = join18(home, ".codex", "config.toml");
55593
55732
  agents.push({
55594
55733
  name: "Codex",
55595
55734
  detectPath: codex,
55596
- installed: () => existsSync9(join17(home, ".codex")) || which("codex"),
55735
+ installed: () => existsSync9(join18(home, ".codex")) || which("codex"),
55597
55736
  write: codexWrite(codex)
55598
55737
  });
55599
- const hermes = join17(home, ".hermes", "config.yaml");
55738
+ const hermes = join18(home, ".hermes", "config.yaml");
55600
55739
  agents.push({
55601
55740
  name: "Hermes",
55602
55741
  detectPath: hermes,
55603
- installed: () => existsSync9(join17(home, ".hermes")) || which("hermes"),
55742
+ installed: () => existsSync9(join18(home, ".hermes")) || which("hermes"),
55604
55743
  write: hermesWrite(hermes)
55605
55744
  });
55606
- const gemini = join17(home, ".gemini", "settings.json");
55745
+ const gemini = join18(home, ".gemini", "settings.json");
55607
55746
  agents.push({
55608
55747
  name: "Gemini CLI",
55609
55748
  detectPath: gemini,
55610
- installed: () => existsSync9(join17(home, ".gemini")) || which("gemini"),
55749
+ installed: () => existsSync9(join18(home, ".gemini")) || which("gemini"),
55611
55750
  write: jsonMcpServersWrite(gemini)
55612
55751
  });
55613
- const windsurf = join17(home, ".codeium", "windsurf", "mcp_config.json");
55752
+ const windsurf = join18(home, ".codeium", "windsurf", "mcp_config.json");
55614
55753
  agents.push({
55615
55754
  name: "Windsurf",
55616
55755
  detectPath: windsurf,
55617
- installed: () => existsSync9(join17(home, ".codeium")) || which("windsurf"),
55756
+ installed: () => existsSync9(join18(home, ".codeium")) || which("windsurf"),
55618
55757
  write: jsonMcpServersWrite(windsurf)
55619
55758
  });
55620
- const copilot = join17(home, ".copilot", "mcp.json");
55759
+ const copilot = join18(home, ".copilot", "mcp.json");
55621
55760
  agents.push({
55622
55761
  name: "Copilot",
55623
55762
  detectPath: copilot,
55624
- installed: () => existsSync9(join17(home, ".copilot")),
55763
+ installed: () => existsSync9(join18(home, ".copilot")),
55625
55764
  write: jsonMcpServersWrite(copilot)
55626
55765
  });
55627
- const cline = join17(home, ".cline", "mcp_settings.json");
55766
+ const cline = join18(home, ".cline", "mcp_settings.json");
55628
55767
  agents.push({
55629
55768
  name: "Cline",
55630
55769
  detectPath: cline,
55631
- installed: () => existsSync9(join17(home, ".cline")),
55770
+ installed: () => existsSync9(join18(home, ".cline")),
55632
55771
  write: jsonMcpServersWrite(cline)
55633
55772
  });
55634
55773
  return agents;
@@ -55672,15 +55811,15 @@ Registered with ${ok}/${installed.length} agent(s).`);
55672
55811
  }
55673
55812
 
55674
55813
  // src/index.ts
55675
- import { readFileSync as readFileSync22, existsSync as existsSync10 } from "node:fs";
55676
- import { join as join18 } from "node:path";
55677
- import { fileURLToPath as fileURLToPath12 } from "node:url";
55678
- var ROOT11 = join18(fileURLToPath12(new URL(".", import.meta.url)), "..");
55814
+ import { readFileSync as readFileSync23, existsSync as existsSync10 } from "node:fs";
55815
+ import { join as join19 } from "node:path";
55816
+ import { fileURLToPath as fileURLToPath13 } from "node:url";
55817
+ var ROOT11 = join19(fileURLToPath13(new URL(".", import.meta.url)), "..");
55679
55818
  function readVersion() {
55680
55819
  try {
55681
- const p = join18(ROOT11, "package.json");
55820
+ const p = join19(ROOT11, "package.json");
55682
55821
  if (existsSync10(p))
55683
- return JSON.parse(readFileSync22(p, "utf8")).version ?? "1.0.0";
55822
+ return JSON.parse(readFileSync23(p, "utf8")).version ?? "1.0.0";
55684
55823
  } catch {}
55685
55824
  return "1.0.0";
55686
55825
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blitzstrike",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
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": {