@skaleagents/swarm 0.7.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -21,6 +21,8 @@ the MCP tool for a structured review.
21
21
 
22
22
  `npx -y @skaleagents/swarm scan [paths...]` scans IaC, CI, Dockerfile and package files on your machine. It needs no account and sends nothing over the network. Use `--changed` for files changed against HEAD, `--fail-on high` to exit 1, and `--json` for machine output.
23
23
 
24
+ Adopt it on an existing repo: `npx @skaleagents/swarm scan --write-baseline .skaleagents-baseline.json`, commit the file, and later scans (hook and Action included) show only new findings. Findings match on rule, file and resource, not line number.
25
+
24
26
  Suppress an intended setting with a comment on the flagged line or the line above: `# skaleagents-ignore DATA001 public asset bucket`.
25
27
 
26
28
  **Claude Code plugin.** It adds a stop hook that scans changed files and asks the agent to fix `high` and `critical` findings, plus a skill and the hosted MCP server:
@@ -33,7 +35,7 @@ Suppress an intended setting with a comment on the flagged line or the line abov
33
35
  **Cursor.** Add `.cursor/hooks.json`:
34
36
 
35
37
  ```json
36
- { "version": 1, "hooks": { "stop": [{ "command": "npx -y --registry=https://registry.npmjs.org @skaleagents/swarm@0.7.0 scan --hook" }] } }
38
+ { "version": 1, "hooks": { "stop": [{ "command": "npx -y --registry=https://registry.npmjs.org @skaleagents/swarm@0.7.1 scan --hook" }] } }
37
39
  ```
38
40
 
39
41
  **GitHub Action.** It posts one PR comment and fails on `critical` by default:
@@ -47,7 +49,7 @@ jobs:
47
49
  steps:
48
50
  - uses: actions/checkout@v4
49
51
  with: { fetch-depth: 0 }
50
- - uses: SkaleAgents/mcp-server@v0.7.0
52
+ - uses: SkaleAgents/mcp-server@v0.7.1
51
53
  with: { fail-on: high }
52
54
  ```
53
55
 
package/dist/cli.d.ts CHANGED
@@ -8,5 +8,7 @@ export declare function scanFiles(files: string[], minSeverity?: FindingSeverity
8
8
  suppressed: number;
9
9
  warnings: string[];
10
10
  };
11
+ export declare const baselineKey: (f: FileFinding) => string;
12
+ export declare function readBaseline(path: string): Set<string>;
11
13
  export declare function formatText(findings: FileFinding[], suppressed: number): string;
12
14
  export declare function runScan(argv: string[]): Promise<number>;
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { execFileSync } from "node:child_process";
2
- import { existsSync, lstatSync, readdirSync, readFileSync } from "node:fs";
2
+ import { existsSync, lstatSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
3
3
  import { basename, dirname, join, relative } from "node:path";
4
4
  import { scanIac, severityRank } from "./iac/scan.js";
5
5
  const usage = `Usage: skaleagents-swarm scan [paths...] [options]
@@ -9,6 +9,8 @@ Scans IaC, CI and package files locally. No account or network needed.
9
9
  --changed Scan files changed against HEAD plus untracked files
10
10
  --min-severity <s> Hide findings below info|low|medium|high|critical (default low)
11
11
  --fail-on <s> Exit 1 when a finding is at or above this severity
12
+ --baseline <file> Hide findings already listed in this baseline file (.skaleagents-baseline.json is used automatically)
13
+ --write-baseline <file> Write current findings to a baseline file and exit 0
12
14
  --json Print JSON instead of text
13
15
  --hook Agent stop hook mode for Claude Code and Cursor (implies --changed)
14
16
 
@@ -91,6 +93,16 @@ export function scanFiles(files, minSeverity = "low") {
91
93
  }
92
94
  // Names come from scanned files, so they are flattened and capped before reaching an agent or PR comment.
93
95
  const clean = (value, max = 160) => (value ?? "").replace(/[\x00-\x1f\x7f`]+/g, " ").slice(0, max);
96
+ // Line numbers are left out so unrelated edits above a finding do not make it look new.
97
+ export const baselineKey = (f) => `${f.ruleId}|${f.file.replaceAll("\\", "/")}|${f.resource ?? ""}`;
98
+ export function readBaseline(path) {
99
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
100
+ const keys = parsed?.findings;
101
+ if (!Array.isArray(keys) || keys.some((key) => typeof key !== "string")) {
102
+ throw new Error(`${path} is not a SkaleAgents baseline file`);
103
+ }
104
+ return new Set(keys);
105
+ }
94
106
  export function formatText(findings, suppressed) {
95
107
  if (findings.length === 0)
96
108
  return `SkaleAgents: no findings${suppressed ? ` (${suppressed} suppressed)` : ""}.`;
@@ -110,6 +122,8 @@ export async function runScan(argv) {
110
122
  let hook = false;
111
123
  let minSeverity = "low";
112
124
  let failOn;
125
+ let baselinePath;
126
+ let writeBaseline;
113
127
  for (let i = 0; i < argv.length; i++) {
114
128
  const arg = argv[i];
115
129
  if (arg === "--") {
@@ -124,6 +138,15 @@ export async function runScan(argv) {
124
138
  hook = changed = true;
125
139
  else if (arg === "--min-severity")
126
140
  minSeverity = severityArg(argv[++i], arg);
141
+ else if (arg === "--baseline" || arg === "--write-baseline") {
142
+ const value = argv[++i];
143
+ if (!value)
144
+ throw new Error(`${arg} needs a file path`);
145
+ if (arg === "--baseline")
146
+ baselinePath = value;
147
+ else
148
+ writeBaseline = value;
149
+ }
127
150
  else if (arg === "--fail-on")
128
151
  failOn = severityArg(argv[++i], arg);
129
152
  else if (arg === "-h" || arg === "--help") {
@@ -149,7 +172,18 @@ export async function runScan(argv) {
149
172
  }
150
173
  for (const path of paths.length || changed ? paths : ["."])
151
174
  walk(path, files);
152
- const { findings, suppressed, warnings } = scanFiles([...new Set(files)], minSeverity);
175
+ const scanned = scanFiles([...new Set(files)], minSeverity);
176
+ const { suppressed, warnings } = scanned;
177
+ if (writeBaseline) {
178
+ const keys = [...new Set(scanned.findings.map(baselineKey))].sort();
179
+ writeFileSync(writeBaseline, `${JSON.stringify({ version: 1, findings: keys }, null, 2)}\n`);
180
+ console.log(`SkaleAgents: wrote ${keys.length} findings to ${writeBaseline}.`);
181
+ return 0;
182
+ }
183
+ // A committed baseline at the repo root applies automatically, so hooks and the Action need no flag.
184
+ baselinePath ??= existsSync(".skaleagents-baseline.json") ? ".skaleagents-baseline.json" : undefined;
185
+ const known = baselinePath ? readBaseline(baselinePath) : new Set();
186
+ const findings = scanned.findings.filter((f) => !known.has(baselineKey(f)));
153
187
  const failing = failOn ? findings.filter((f) => severityRank[f.severity] >= severityRank[failOn]) : [];
154
188
  if (hook) {
155
189
  if (failing.length) {
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "0.7.0";
1
+ export declare const VERSION = "0.7.1";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = "0.7.0";
1
+ export const VERSION = "0.7.1";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skaleagents/swarm",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -53,5 +53,6 @@
53
53
  "devDependencies": {
54
54
  "@types/node": "^22.20.1",
55
55
  "tsx": "^4.23.12"
56
- }
56
+ },
57
+ "mcpName": "io.github.SkaleAgents/swarm"
57
58
  }