@rafter-security/cli 0.7.3 → 0.7.6

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.
@@ -145,7 +145,7 @@ function displayQuickScan(scan, skillName) {
145
145
  else {
146
146
  console.log(fmt.warning(`Secrets: ${scan.secrets} found`));
147
147
  console.log(" → API keys, tokens, or credentials detected");
148
- console.log(" → Run: rafter scan local <path> for details");
148
+ console.log(" → Run: rafter secrets <path> for details");
149
149
  }
150
150
  // URLs
151
151
  if (scan.urls.length === 0) {
@@ -104,6 +104,12 @@ function claudeCodeHooks() {
104
104
  const post = { type: "command", command: "rafter hook posttool" };
105
105
  settings.hooks.PreToolUse = filterOutRafter(settings.hooks.PreToolUse, (e) => hookEntryMatchesRafter(e, "rafter hook pretool"));
106
106
  settings.hooks.PostToolUse = filterOutRafter(settings.hooks.PostToolUse, (e) => hookEntryMatchesRafter(e, "rafter hook posttool"));
107
+ // Strip legacy SessionStart entry from <=0.7.4 installs.
108
+ if (Array.isArray(settings.hooks.SessionStart)) {
109
+ settings.hooks.SessionStart = filterOutRafter(settings.hooks.SessionStart, (e) => hookEntryMatchesRafter(e, "rafter hook session-start"));
110
+ if (settings.hooks.SessionStart.length === 0)
111
+ delete settings.hooks.SessionStart;
112
+ }
107
113
  settings.hooks.PreToolUse.push({ matcher: "Bash", hooks: [pre] }, { matcher: "Write|Edit", hooks: [pre] });
108
114
  settings.hooks.PostToolUse.push({ matcher: ".*", hooks: [post] });
109
115
  writeJson(settingsPath, settings);
@@ -118,6 +124,11 @@ function claudeCodeHooks() {
118
124
  if (settings.hooks?.PostToolUse) {
119
125
  settings.hooks.PostToolUse = filterOutRafter(settings.hooks.PostToolUse, (e) => hookEntryMatchesRafter(e, "rafter hook posttool"));
120
126
  }
127
+ if (Array.isArray(settings.hooks?.SessionStart)) {
128
+ settings.hooks.SessionStart = filterOutRafter(settings.hooks.SessionStart, (e) => hookEntryMatchesRafter(e, "rafter hook session-start"));
129
+ if (settings.hooks.SessionStart.length === 0)
130
+ delete settings.hooks.SessionStart;
131
+ }
121
132
  writeJson(settingsPath, settings);
122
133
  },
123
134
  };
@@ -266,6 +277,52 @@ function codexHooks() {
266
277
  },
267
278
  };
268
279
  }
280
+ /**
281
+ * Project-scope Claude Code MCP config (<cwd>/.mcp.json). Unlike other
282
+ * claude-code components which touch ~/.claude, this one writes at the
283
+ * project root — Claude Code auto-loads it on startup and exposes
284
+ * `mcp__rafter__*` tools to the agent.
285
+ */
286
+ function claudeCodeMcp() {
287
+ const home = os.homedir();
288
+ const mcpPath = path.join(process.cwd(), ".mcp.json");
289
+ return {
290
+ id: "claude-code.mcp",
291
+ platform: "claude-code",
292
+ kind: "mcp",
293
+ description: "Claude Code project-scope MCP server (<project>/.mcp.json)",
294
+ detectDir: path.join(home, ".claude"),
295
+ path: mcpPath,
296
+ isInstalled: () => {
297
+ if (!fs.existsSync(mcpPath))
298
+ return false;
299
+ const cfg = readJson(mcpPath);
300
+ return !!cfg.mcpServers?.rafter;
301
+ },
302
+ install: () => {
303
+ const cfg = fs.existsSync(mcpPath) ? readJson(mcpPath) : {};
304
+ cfg.mcpServers ?? (cfg.mcpServers = {});
305
+ cfg.mcpServers.rafter = { ...RAFTER_MCP_ENTRY };
306
+ writeJson(mcpPath, cfg);
307
+ },
308
+ uninstall: () => {
309
+ if (!fs.existsSync(mcpPath))
310
+ return;
311
+ const cfg = readJson(mcpPath);
312
+ if (!removeKey(cfg.mcpServers, "rafter"))
313
+ return;
314
+ if (cfg.mcpServers && Object.keys(cfg.mcpServers).length === 0) {
315
+ delete cfg.mcpServers;
316
+ }
317
+ if (Object.keys(cfg).length === 0) {
318
+ fs.unlinkSync(mcpPath);
319
+ }
320
+ else {
321
+ writeJson(mcpPath, cfg);
322
+ }
323
+ },
324
+ };
325
+ }
269
326
  function cursorHooks() {
270
327
  const home = os.homedir();
271
328
  const hooksPath = path.join(home, ".cursor", "hooks.json");
@@ -728,6 +785,7 @@ export function getComponentRegistry() {
728
785
  claudeCodeHooks(),
729
786
  claudeCodeInstructions(),
730
787
  claudeCodeSkills(),
788
+ claudeCodeMcp(),
731
789
  codexHooks(),
732
790
  codexSkills(),
733
791
  cursorHooks(),
@@ -29,7 +29,7 @@ export function createExecCommand() {
29
29
  if (scanResult.secretsFound) {
30
30
  console.error(`\n${fmt.warning("Secrets detected in staged files!")}\n`);
31
31
  console.error(`Found ${scanResult.count} secret(s) in ${scanResult.files} file(s)`);
32
- console.error(`\nRun 'rafter scan local' for details.\n`);
32
+ console.error(`\nRun 'rafter secrets' for details.\n`);
33
33
  interceptor.logEvaluation(evaluation, "blocked");
34
34
  process.exit(1);
35
35
  }
@@ -127,6 +127,15 @@ function installClaudeCodeHooks(root) {
127
127
  const hooks = entry.hooks || [];
128
128
  return !hooks.some((h) => h.command === "rafter hook posttool");
129
129
  });
130
+ // Strip legacy SessionStart entry left over from <=0.7.4 installs.
131
+ if (Array.isArray(settings.hooks.SessionStart)) {
132
+ settings.hooks.SessionStart = settings.hooks.SessionStart.filter((entry) => {
133
+ const hooks = entry.hooks || [];
134
+ return !hooks.some((h) => h.command === "rafter hook session-start");
135
+ });
136
+ if (settings.hooks.SessionStart.length === 0)
137
+ delete settings.hooks.SessionStart;
138
+ }
130
139
  // Add Rafter hooks
131
140
  settings.hooks.PreToolUse.push({ matcher: "Bash", hooks: [preHook] }, { matcher: "Write|Edit", hooks: [preHook] });
132
141
  settings.hooks.PostToolUse.push({ matcher: ".*", hooks: [postHook] });
@@ -303,6 +312,28 @@ const RAFTER_MCP_ENTRY = {
303
312
  command: "rafter",
304
313
  args: ["mcp", "serve"],
305
314
  };
315
+ /**
316
+ * Install MCP server config for Claude Code (<root>/.mcp.json).
317
+ * Project-scope MCP config that Claude Code auto-loads on startup.
318
+ */
319
+ function installClaudeCodeMcp(root) {
320
+ const mcpPath = path.join(root, ".mcp.json");
321
+ let config = {};
322
+ if (fs.existsSync(mcpPath)) {
323
+ try {
324
+ config = JSON.parse(fs.readFileSync(mcpPath, "utf-8"));
325
+ }
326
+ catch {
327
+ console.log(fmt.warning("Existing .mcp.json was unreadable, creating new one"));
328
+ }
329
+ }
330
+ if (!config.mcpServers)
331
+ config.mcpServers = {};
332
+ config.mcpServers.rafter = { ...RAFTER_MCP_ENTRY };
333
+ fs.writeFileSync(mcpPath, JSON.stringify(config, null, 2), "utf-8");
334
+ console.log(fmt.success(`Installed Rafter MCP server to ${mcpPath}`));
335
+ return true;
336
+ }
306
337
  /**
307
338
  * Install MCP server config for Gemini CLI (~/.gemini/settings.json)
308
339
  */
@@ -463,6 +494,52 @@ async function installClaudeCodeSkills(root) {
463
494
  function installCodexSkills(root) {
464
495
  installSkillsTo(path.join(root, ".agents", "skills"));
465
496
  }
497
+ function installGeminiSkills(root) {
498
+ installSkillsTo(path.join(root, ".agents", "skills"));
499
+ }
500
+ /**
501
+ * Register installed skills with Gemini CLI via `gemini skills link <abs-path>`.
502
+ *
503
+ * Requires gemini CLI >= 0.35 (the version that added `gemini skills`).
504
+ * Missing CLI, missing subcommand, and per-skill registration failures are
505
+ * non-fatal: we warn and continue so the on-disk install still succeeds.
506
+ */
507
+ function registerGeminiSkills(skillsDir) {
508
+ // Probe for the `gemini` binary. Absence is expected on CI / fresh machines.
509
+ try {
510
+ execSync("gemini --version", { stdio: ["ignore", "pipe", "ignore"], timeout: 5000 });
511
+ }
512
+ catch {
513
+ console.log(fmt.warning("gemini CLI not found on PATH — skipping skill registration. " +
514
+ "Skills are installed to disk; re-run after installing gemini ≥ 0.35."));
515
+ return;
516
+ }
517
+ // Probe `gemini skills` subcommand (added in 0.35).
518
+ try {
519
+ execSync("gemini skills --help", { stdio: ["ignore", "pipe", "ignore"], timeout: 5000 });
520
+ }
521
+ catch {
522
+ console.log(fmt.warning("gemini CLI does not support `skills` subcommand (needs ≥ 0.35). " +
523
+ "Skipping registration — skills are still installed to disk."));
524
+ return;
525
+ }
526
+ for (const skill of AGENT_SKILLS) {
527
+ const absPath = path.resolve(skillsDir, skill.name);
528
+ if (!fs.existsSync(absPath))
529
+ continue;
530
+ try {
531
+ execSync(`gemini skills link ${JSON.stringify(absPath)}`, {
532
+ stdio: ["ignore", "pipe", "pipe"],
533
+ timeout: 10000,
534
+ });
535
+ console.log(fmt.success(`Registered ${skill.name} with Gemini CLI`));
536
+ }
537
+ catch (e) {
538
+ const msg = (e?.stderr?.toString?.() || e?.message || "").trim();
539
+ console.log(fmt.warning(`Failed to register ${skill.name} with Gemini CLI: ${msg.split("\n")[0] || "unknown error"}`));
540
+ }
541
+ }
542
+ }
466
543
  async function askYesNo(question, defaultYes = true) {
467
544
  const rl = createInterface({ input: process.stdin, output: process.stderr });
468
545
  const suffix = defaultYes ? "[Y/n]" : "[y/N]";
@@ -717,6 +794,17 @@ export function createInitCommand() {
717
794
  try {
718
795
  await installClaudeCodeSkills(root);
719
796
  installClaudeCodeHooks(root);
797
+ if (scope === "project") {
798
+ const components = (manager.get("agent.components") ?? {});
799
+ if (components["claude-code.mcp"]?.enabled === false) {
800
+ console.log(fmt.info("Skipped .mcp.json (claude-code.mcp disabled; re-enable with `rafter agent enable claude-code.mcp`)"));
801
+ }
802
+ else {
803
+ installClaudeCodeMcp(root);
804
+ components["claude-code.mcp"] = { enabled: true, updatedAt: new Date().toISOString() };
805
+ manager.set("agent.components", components);
806
+ }
807
+ }
720
808
  if (scope === "user")
721
809
  manager.set("agent.environments.claudeCode.enabled", true);
722
810
  claudeCodeOk = true;
@@ -739,11 +827,13 @@ export function createInitCommand() {
739
827
  console.error(fmt.error(`Failed to install Codex CLI integration: ${e}`));
740
828
  }
741
829
  }
742
- // Install Gemini CLI MCP + hooks if opted in
830
+ // Install Gemini CLI MCP + skills + hooks if opted in
743
831
  let geminiOk = false;
744
832
  if ((hasGemini || (opts.local && wantGemini)) && wantGemini) {
745
833
  try {
746
834
  geminiOk = installGeminiMcp(root);
835
+ installGeminiSkills(root);
836
+ registerGeminiSkills(path.join(root, ".agents", "skills"));
747
837
  installGeminiHooks(root);
748
838
  if (geminiOk && scope === "user")
749
839
  manager.set("agent.environments.gemini.enabled", true);
@@ -873,7 +963,7 @@ export function createInitCommand() {
873
963
  console.log("No agent environments detected. Install an agent tool and re-run with --with-<tool>.");
874
964
  }
875
965
  console.log();
876
- console.log(" - Run: rafter scan local . (test secret scanning)");
966
+ console.log(" - Run: rafter secrets . (test secret scanning)");
877
967
  console.log(" - Configure: rafter agent config show");
878
968
  console.log();
879
969
  // Warn if a different rafter version shadows this one on PATH
@@ -4,30 +4,29 @@ export const RAFTER_MARKER_START = "<!-- rafter:start -->";
4
4
  export const RAFTER_MARKER_END = "<!-- rafter:end -->";
5
5
  /** Rafter security context block for agent instruction files (CLAUDE.md, AGENTS.md, etc.) */
6
6
  export const RAFTER_INSTRUCTION_BLOCK = `${RAFTER_MARKER_START}
7
- ## Security: Rafter
7
+ ## Security: Rafter (required review gate)
8
8
 
9
- This environment has Rafter available the security toolkit built for AI workflows.
9
+ Rafter is this project's security review gate. A security-relevant task is
10
+ **not complete** until a rafter skill (or \`rafter run\`) has reviewed it.
11
+ Don't mark done, don't hand off, don't open a PR without that pass.
10
12
 
11
- **Local scanning** (always available, no API key needed):
12
- - \`rafter scan local .\` — scan for leaked credentials
13
- - \`rafter scan local --staged\` — scan staged changes only
13
+ **Stop and invoke before continuing:**
14
14
 
15
- **Remote analysis** (requires RAFTER_API_KEY):
16
- - \`rafter run\` deterministic SAST, secret detection, dependency checks
17
- - \`rafter run --mode plus\` agentic deep-dive with additional analysis passes
15
+ - Designing auth, payments, credentials, tokens, sessions, file upload,
16
+ user data, or data deletion **\`rafter-secure-design\`** (before writing code).
17
+ - Diff touches user input, SQL, shell, auth, credentials, file paths, or
18
+ serialization → **\`rafter-code-review\`** + \`rafter run\` (before declaring done).
19
+ - About to install or forward a third-party SKILL.md, MCP manifest, Cursor
20
+ rule, or agent config → **\`rafter-skill-review\`** (before copying anywhere).
21
+ - Security-adjacent but the angle isn't clear → **\`rafter\`** (the router skill,
22
+ powerful toolkit to help you write more secure code).
18
23
 
19
- Consider scanning before pushing, shipping features, or merging PRs.
20
- If RAFTER_API_KEY is not set, local scanning still works — use what's available.
24
+ **CLI:**
21
25
 
22
- **Project integration:**
23
- - \`rafter agent install-hook\`pre-commit secret scanning
24
- - \`rafter ci init\` — add scanning to CI/CD pipeline
25
- - \`.rafter.yml\` — project-specific security policy
26
-
27
- **More:**
28
- - \`rafter agent audit-skill <path>\` — audit a skill before installing
29
- - \`rafter agent audit --last 5\` — recent security events
30
- - \`rafter brief commands\` — full CLI reference
26
+ - \`rafter run\` — remote SAST + SCA + secrets (real code analysis, needs \`RAFTER_API_KEY\`)
27
+ - \`rafter secrets .\`local secrets only (offline; NOT a code-security scan)
28
+ - \`rafter run --mode plus\` — everything in default (\`--mode fast\`) plus
29
+ powerful agentic deep-dives (needs \`RAFTER_API_KEY\`)
31
30
  ${RAFTER_MARKER_END}`;
32
31
  /**
33
32
  * Write a Rafter instruction block into an instruction file.
@@ -67,7 +67,7 @@ export function createScanCommand() {
67
67
  const isAgentScan = argv.includes("agent") && argv.includes("scan") &&
68
68
  argv.indexOf("agent") < argv.indexOf("scan");
69
69
  if (isAgentScan) {
70
- process.stderr.write("Warning: rafter agent scan is deprecated and will be removed in a future major version. Use rafter scan local instead.\n");
70
+ process.stderr.write("Warning: rafter agent scan is deprecated and will be removed in a future major version. Use rafter secrets instead.\n");
71
71
  }
72
72
  // Load policy-merged config for excludePaths/customPatterns
73
73
  const manager = new ConfigManager();
@@ -115,6 +115,17 @@ export function createScanCommand() {
115
115
  outputScanResults(applyBaseline(results, baselineEntries), opts);
116
116
  });
117
117
  }
118
+ /**
119
+ * `rafter secrets` — top-level alias for the secret scanner. Same engine
120
+ * and flags as `rafter scan local`; the name makes the scope (secrets only,
121
+ * not full code analysis) explicit to agents and humans.
122
+ */
123
+ export function createSecretsCommand() {
124
+ const cmd = createScanCommand();
125
+ cmd.name("secrets");
126
+ cmd.description("Scan files/directories for hardcoded secrets (regex + gitleaks). Secrets only — not a code analysis. For full SAST/SCA, use 'rafter run'.");
127
+ return cmd;
128
+ }
118
129
  /**
119
130
  * Emit SARIF 2.1.0 JSON for GitHub/GitLab security tab integration
120
131
  */
@@ -210,7 +210,7 @@ This installs:
210
210
  ## Manual Setup (if automated init isn't available)
211
211
 
212
212
  1. Run \`rafter brief scanning\` and save the command reference
213
- 2. Before commits, run: \`rafter scan local .\`
213
+ 2. Before commits, run: \`rafter secrets .\`
214
214
  3. For remote analysis: \`rafter run\``,
215
215
  codex: `# Rafter Setup — Codex CLI
216
216
 
@@ -424,7 +424,7 @@ rafter brief commands # know what commands are available
424
424
 
425
425
  ## Key Commands to Know
426
426
 
427
- - \`rafter scan local .\` — scan for secrets locally (no API key needed)
427
+ - \`rafter secrets .\` — scan for hardcoded secrets locally (no API key needed)
428
428
  - \`rafter run\` — trigger remote SAST/SCA analysis (needs API key)
429
429
  - \`rafter get <id>\` — retrieve scan results
430
430
  - \`rafter agent audit\` — review security event log
@@ -100,7 +100,7 @@ jobs:
100
100
  run: npm install -g @rafter-security/cli
101
101
 
102
102
  - name: Scan for secrets
103
- run: rafter scan local . --quiet
103
+ run: rafter secrets . --quiet
104
104
  `;
105
105
  if (withBackend) {
106
106
  yaml += `
@@ -131,7 +131,7 @@ secret-scan:
131
131
  image: node:20
132
132
  script:
133
133
  - npm install -g @rafter-security/cli
134
- - rafter scan local . --quiet
134
+ - rafter secrets . --quiet
135
135
  rules:
136
136
  - if: $CI_PIPELINE_SOURCE == "push"
137
137
  - if: $CI_PIPELINE_SOURCE == "merge_request_event"
@@ -169,7 +169,7 @@ jobs:
169
169
  command: npm install -g @rafter-security/cli
170
170
  - run:
171
171
  name: Scan for secrets
172
- command: rafter scan local . --quiet
172
+ command: rafter secrets . --quiet
173
173
  `;
174
174
  if (withBackend) {
175
175
  yaml += `
@@ -142,7 +142,7 @@ function evaluateBash(command) {
142
142
  audit.logSecretDetected("staged files", `${scanResult.count} secret(s)`, "blocked");
143
143
  return {
144
144
  decision: "deny",
145
- reason: `${scanResult.count} secret(s) detected in ${scanResult.files} staged file(s). Run 'rafter scan local --staged' for details.`,
145
+ reason: `${scanResult.count} secret(s) detected in ${scanResult.files} staged file(s). Run 'rafter secrets --staged' for details.`,
146
146
  };
147
147
  }
148
148
  }
@@ -25,7 +25,7 @@ export function createReportCommand() {
25
25
  }
26
26
  else {
27
27
  console.error("Error: No input provided. Pipe scan results or provide a file path.\n" +
28
- " Example: rafter scan local --json . | rafter report -o report.html\n" +
28
+ " Example: rafter secrets --json . | rafter report -o report.html\n" +
29
29
  " Example: rafter report scan-results.json -o report.html");
30
30
  process.exit(2);
31
31
  return;
@@ -3,16 +3,17 @@
3
3
  *
4
4
  * Default (no subcommand): remote scan (same as `rafter run`)
5
5
  * rafter scan remote: explicit alias for remote scan
6
- * rafter scan local [path]: local secret scanner (was `rafter agent scan`)
6
+ * rafter scan local [path]: hidden back-compat alias for `rafter secrets`
7
+ * (was `rafter agent scan` before 0.7.4).
7
8
  */
8
9
  import { Command } from "commander";
9
10
  import { runRemoteScan } from "../backend/run.js";
10
11
  import { createScanCommand as createLocalScanCommand } from "../agent/scan.js";
11
12
  export function createScanGroupCommand() {
12
- // "local" subcommand — reuses agent/scan.ts logic, renamed
13
+ // "local" subcommand — back-compat alias for `rafter secrets`. Hidden from help.
13
14
  const localCmd = createLocalScanCommand();
14
15
  localCmd.name("local");
15
- localCmd.description("Scan files or directories for secrets (local)");
16
+ localCmd.description("(deprecated alias for 'rafter secrets')");
16
17
  // "remote" subcommand — same handler as `rafter run`
17
18
  const remoteCmd = new Command("remote")
18
19
  .description("Trigger a remote backend security scan (explicit alias for 'rafter run')")
@@ -29,7 +30,7 @@ export function createScanGroupCommand() {
29
30
  });
30
31
  // Root scan group — default action is remote scan
31
32
  const scanGroup = new Command("scan")
32
- .description("Scan for security issues. Default: remote scan. Use 'scan local' for local secret scanning.")
33
+ .description("Trigger a remote security scan (requires RAFTER_API_KEY).")
33
34
  .enablePositionalOptions()
34
35
  .option("-r, --repo <repo>", "org/repo (default: current)")
35
36
  .option("-b, --branch <branch>", "branch (default: current else main)")
@@ -39,7 +40,7 @@ export function createScanGroupCommand() {
39
40
  .option("--github-token <token>", "GitHub PAT for private repos (or RAFTER_GITHUB_TOKEN env var)")
40
41
  .option("--skip-interactive", "do not wait for scan to complete")
41
42
  .option("--quiet", "suppress status messages");
42
- scanGroup.addCommand(localCmd);
43
+ scanGroup.addCommand(localCmd, { hidden: true });
43
44
  scanGroup.addCommand(remoteCmd);
44
45
  // When invoked with no subcommand, run remote scan
45
46
  scanGroup.action(async (opts) => {
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ import { createRunCommand } from "./commands/backend/run.js";
5
5
  import { createGetCommand } from "./commands/backend/get.js";
6
6
  import { createUsageCommand } from "./commands/backend/usage.js";
7
7
  import { createScanGroupCommand } from "./commands/scan/index.js";
8
+ import { createSecretsCommand } from "./commands/agent/scan.js";
8
9
  import { createAgentCommand } from "./commands/agent/index.js";
9
10
  import { createSkillCommand } from "./commands/skill/index.js";
10
11
  import { createCiCommand } from "./commands/ci/index.js";
@@ -40,6 +41,8 @@ program.addCommand(createGetCommand());
40
41
  program.addCommand(createUsageCommand());
41
42
  // Scan command group (default: remote scan; subcommands: local, remote)
42
43
  program.addCommand(createScanGroupCommand());
44
+ // Secrets — top-level alias for local secret scanning (explicit-scope name)
45
+ program.addCommand(createSecretsCommand());
43
46
  // Agent commands
44
47
  program.addCommand(createAgentCommand());
45
48
  // Skill commands (install / uninstall / list rafter-authored skills)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rafter-security/cli",
3
- "version": "0.7.3",
3
+ "version": "0.7.6",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "rafter": "./dist/index.js"
@@ -27,7 +27,7 @@ fi
27
27
  echo "🔍 Rafter: Scanning staged files for secrets..."
28
28
 
29
29
  # Scan staged files
30
- rafter scan local --staged --quiet
30
+ rafter secrets --staged --quiet
31
31
 
32
32
  EXIT_CODE=$?
33
33
 
@@ -38,7 +38,7 @@ while read local_ref local_sha remote_ref remote_sha; do
38
38
 
39
39
  echo "🔍 Rafter: Scanning commits being pushed ($local_ref)..."
40
40
 
41
- rafter scan local --diff "$ref_arg" --quiet
41
+ rafter secrets --diff "$ref_arg" --quiet
42
42
  EXIT_CODE=$?
43
43
 
44
44
  if [ $EXIT_CODE -ne 0 ]; then
@@ -49,7 +49,7 @@ done
49
49
  if [ $FOUND_SECRETS -ne 0 ]; then
50
50
  echo -e "${RED}❌ Push blocked: Secrets detected in commits being pushed${NC}"
51
51
  echo ""
52
- echo " Run: rafter scan local --diff <remote-sha>"
52
+ echo " Run: rafter secrets --diff <remote-sha>"
53
53
  echo " To see details and remediate."
54
54
  echo ""
55
55
  echo " To bypass (NOT recommended): git push --no-verify"
@@ -51,7 +51,7 @@ rafter agent init --all
51
51
  Scan files for secrets before committing.
52
52
 
53
53
  ```bash
54
- rafter scan local <path>
54
+ rafter secrets <path>
55
55
  ```
56
56
 
57
57
  **When to use:**
@@ -284,7 +284,7 @@ Configure with: `rafter agent config set agent.riskLevel moderate`
284
284
 
285
285
  ## Best Practices
286
286
 
287
- 1. **Always scan before commits**: Run `rafter scan local` before `git commit`
287
+ 1. **Always scan before commits**: Run `rafter secrets` before `git commit`
288
288
  2. **Audit untrusted skills**: Run `/rafter-audit-skill` on skills from unknown sources before installation
289
289
  3. **Review audit logs**: Check `rafter agent audit` after suspicious activity
290
290
  4. **Keep patterns updated**: Patterns updated automatically with CLI updates
@@ -1,17 +1,21 @@
1
1
  ---
2
2
  name: rafter
3
- description: "Rafter the security toolkit built for AI workflows. Router skill: pick your task below and Read the matching sub-doc. Covers (a) scanning code/repos, (b) evaluating a command before running, (c) auditing a plugin or skill, (d) understanding a finding, (e) writing secure code from scratch, (f) analyzing existing code for flaws. Local features are free, deterministic, and offline (no API key). Remote SAST/SCA via RAFTER_API_KEY when deeper analysis is needed. If RAFTER_API_KEY is missing, local still works — don't block on it."
3
+ description: "Entry point for rafter. Invoke when a sub-skill is unclear, or when the task needs `rafter run` (remote SAST+SCA), `rafter secrets` (local secrets-only), `rafter audit`, policy checks, or command-risk evaluation. If a task is security-relevant and no rafter skill or CLI call has been made, invoke this before handing the task off an un-evaluated \"done\" on security-relevant work is not done."
4
4
  version: 0.7.0
5
5
  allowed-tools: [Bash, Read]
6
6
  ---
7
7
 
8
8
  # Rafter — Security Toolkit for AI Workflows
9
9
 
10
- Rafter ships three tiers of security tooling:
10
+ ## Picking the right tier DO NOT stop at "local"
11
11
 
12
- 1. **Local** deterministic secret scanning, command-risk classification, skill auditing. Free, offline, no API key.
13
- 2. **Remote fast** (default) — SAST + SCA + deterministic secrets via the Rafter API.
14
- 3. **Remote plus** — agentic deep-dive analysis. Your code is deleted after the run.
12
+ Rafter ships three tiers. **They are not interchangeable.** The local tier is narrow; skipping remote analysis is the #1 way agents under-use rafter.
13
+
14
+ 1. **Local (`rafter secrets`)** — secrets only. Regex + gitleaks for hardcoded API keys, tokens, private keys. Fast, offline, no key. **This is NOT a code security scan.** It will not find SQL injection, SSRF, auth bugs, insecure deserialization, logic flaws, or dependency vulns. If an agent's entire rafter interaction was `rafter secrets .` and it exited clean, the agent has done secret-hygiene only — not security review.
15
+ 2. **Remote fast (`rafter run`, default mode)** — SAST + SCA + secrets via the Rafter API. This is the real code-analysis pass: dataflow, taint, known-vulnerable dependencies, crypto misuse, injection sinks. Needs `RAFTER_API_KEY`.
16
+ 3. **Remote plus (`rafter run --mode plus`)** — agentic deep-dive: LLM-guided investigation of suspicious patterns the rules engine flags. Slower, higher signal. Code is deleted server-side after the run.
17
+
18
+ **Default expectation for a security-relevant task**: run `rafter run`. Fall back to `rafter secrets` only when no API key is available or you specifically need offline secret-hygiene. If you've only run the local scanner, say so explicitly — don't claim the code was "scanned" without qualification.
15
19
 
16
20
  Stable exit codes, stable JSON shapes, deterministic findings. Safe to chain in CI and in agent loops.
17
21
 
@@ -25,10 +29,11 @@ Pick the branch that matches what you're trying to do. Each branch points at a s
25
29
 
26
30
  Use this for: "Is this safe to push?", "Check for leaks", "Run a security scan", pre-merge / pre-deploy gating, post-dependency-update checks.
27
31
 
28
- - Local secret scan (fast, no key): `rafter scan local .`
29
- - Remote SAST/SCA (needs `RAFTER_API_KEY`): `rafter run` (alias `rafter scan`)
32
+ - **Default: `rafter run`** remote SAST + SCA + secrets. This is the real scan. Needs `RAFTER_API_KEY`.
33
+ - **Deep-dive: `rafter run --mode plus`** — agentic analysis when stakes are high or fast mode flagged something suspicious worth investigating.
34
+ - **Secrets-only fallback: `rafter secrets`** — use when no API key is available, or alongside `rafter run` for fastest secret-leak feedback. Does NOT analyse code — only hunts hardcoded credentials.
30
35
  - **Read `docs/backend.md`** for fast-vs-plus modes, auth, latency, cost.
31
- - **Read `docs/cli-reference.md`** §`scan` and §`run` for full flag matrix.
36
+ - **Read `docs/cli-reference.md`** §`secrets`, §`scan`, §`run` for full flag matrix.
32
37
 
33
38
  ### (b) I want to evaluate a command before running it
34
39
 
@@ -86,9 +91,10 @@ MCP-connected agents: the same surface is exposed as the `rafter://docs` resourc
86
91
  ## Fast Path (most common)
87
92
 
88
93
  ```bash
89
- rafter scan local . # secrets, offline, exit 0/1/2
90
- rafter run # remote SAST/SCA (auto-detects repo/branch)
91
- rafter get <scan-id> # fetch results
94
+ rafter run # remote SAST + SCA + secrets the real code scan
95
+ rafter run --mode plus # agentic deep-dive when fast mode flags something
96
+ rafter secrets # secrets-only offline, no key
97
+ rafter get <scan-id> # fetch results by id
92
98
  rafter usage # check API quota
93
99
  ```
94
100
 
@@ -100,19 +106,13 @@ Full CLI tree: **Read `docs/cli-reference.md`**. Full digest: `rafter brief comm
100
106
 
101
107
  ## Configuration
102
108
 
103
- Remote scanning needs an API key:
109
+ `rafter run` (the full code scan) needs an API key:
104
110
 
105
111
  ```bash
106
112
  export RAFTER_API_KEY="..." # or put it in .env
107
113
  ```
108
114
 
109
- Without a key, local scanning still works fully do not block the workflow.
115
+ Without a key, only `rafter secrets` works — that's secret-hygiene, not code review. If security matters for the task, flag the missing key to the user rather than silently accepting the narrower scan.
110
116
 
111
117
  ## Strengthen the Project
112
-
113
- If this repo doesn't have Rafter wired in yet:
114
-
115
- - `rafter agent install-hook` — pre-commit secret scan
116
- - `rafter ci init` — CI workflow with scanning
117
- - `.rafter.yml` — project-specific policy
118
- - `rafter brief setup/<platform>` — per-agent integration guide
118
+ Not wired in yet? `rafter agent install-hook` (pre-commit), `rafter ci init` (CI workflow), `.rafter.yml` (policy). Per-platform setup: `rafter brief setup/<platform>`.
@@ -6,7 +6,7 @@ When to reach for the Rafter API instead of (or in addition to) the local scanne
6
6
 
7
7
  | Question | Answer |
8
8
  |---|---|
9
- | "Are there leaked secrets in this diff/repo?" | **Local first** (`rafter scan local .`). Deterministic, offline, sub-second. |
9
+ | "Are there leaked secrets in this diff/repo?" | **Local first** (`rafter secrets .`). Deterministic, offline, sub-second. |
10
10
  | "Any SAST issues — SQLi, XSS, insecure deserialization, weak crypto?" | **Remote** (`rafter run`). Needs the backend's analyzers. |
11
11
  | "Are my dependencies vulnerable (CVEs)?" | **Remote** — SCA runs server-side. |
12
12
  | "I'm in a CI pipeline without a `RAFTER_API_KEY`" | **Local only**. Don't fail the build on a missing key. |
@@ -26,7 +26,7 @@ Private GitHub repos need `RAFTER_GITHUB_TOKEN` (or `--github-token`) so the bac
26
26
 
27
27
  Check quota with `rafter usage` before firing a batch of scans.
28
28
 
29
- If the key is missing, `rafter run` exits with a clear error — **do not** prompt the user mid-flow; recommend `rafter scan local` and move on.
29
+ If the key is missing, `rafter run` exits with a clear error — **do not** prompt the user mid-flow; recommend `rafter secrets` and move on.
30
30
 
31
31
  ## Modes
32
32
 
@@ -47,7 +47,7 @@ Agentic deep-dive pass on top of fast mode: cross-file reasoning, data-flow hypo
47
47
  - **Output**: same JSON shape as fast mode, plus narrative `notes` and higher-confidence chains.
48
48
 
49
49
  Recommended flow:
50
- 1. `rafter scan local .` — secrets guardrail in dev loop.
50
+ 1. `rafter secrets .` — secrets guardrail in dev loop.
51
51
  2. `rafter run --mode fast` — every PR in CI.
52
52
  3. `rafter run --mode plus` — before release, or when a fast-mode finding needs deeper context.
53
53
 
@@ -30,7 +30,7 @@ Key options: `--repo org/repo`, `--branch <name>`, `--mode fast|plus`, `--format
30
30
 
31
31
  Example: `rafter run --repo myorg/api --branch feature/auth --mode plus --format json`
32
32
 
33
- ### `rafter scan local [path]`
33
+ ### `rafter secrets [path]`
34
34
 
35
35
  Local secret scan. Deterministic, offline, no API key. Dual-engine: Gitleaks binary if present, built-in regex fallback (21+ patterns).
36
36
 
@@ -38,7 +38,9 @@ When: pre-commit, pre-push, fast first pass before remote scan, air-gapped envs.
38
38
 
39
39
  Useful flags: `--history` (scan git history with Gitleaks), `--format json`, `--quiet`.
40
40
 
41
- Example: `rafter scan local . --format json`
41
+ Example: `rafter secrets . --format json`
42
+
43
+ (Back-compat aliases: `rafter scan local` and `rafter agent scan`. Prefer `rafter secrets`.)
42
44
 
43
45
  ### `rafter get <scan-id>`
44
46
 
@@ -76,10 +78,6 @@ When: vetting a third-party skill, MCP server, or CLI plugin before install.
76
78
 
77
79
  Audit a single skill file (SKILL.md). Flags prompt-injection, unbounded tool use, exfiltration patterns.
78
80
 
79
- ### `rafter agent scan [path]`
80
-
81
- Alias for `rafter scan local` kept for back-compat. Prefer `rafter scan local`.
82
-
83
81
  ### `rafter agent status` · `rafter agent verify`
84
82
 
85
83
  `status`: dump config, hook state, gitleaks availability, audit log location.
@@ -95,7 +93,7 @@ Scaffold `.rafter.yml` and a baseline for the current repo.
95
93
 
96
94
  ### `rafter agent install-hook`
97
95
 
98
- Install a pre-commit hook that runs `rafter scan local` before every commit.
96
+ Install a pre-commit hook that runs `rafter secrets --staged` before every commit.
99
97
 
100
98
  ### `rafter agent config [get|set|list]`
101
99
 
@@ -189,7 +187,7 @@ Emit shell completion script.
189
187
 
190
188
  | User intent | Command |
191
189
  |---|---|
192
- | Fast secret check locally | `rafter scan local .` |
190
+ | Fast secret check locally | `rafter secrets .` |
193
191
  | Full repo security review | `rafter run` (then `rafter get <id>`) |
194
192
  | "Is this command safe?" | `rafter agent exec --dry-run -- <cmd>` |
195
193
  | "Is this skill safe to install?" | `rafter agent audit <path>` |
@@ -50,7 +50,7 @@ Pair with `rafter run --mode plus` when you want both a human-style walkthrough
50
50
 
51
51
  - Designing a new service → **secure-design**.
52
52
  - Reviewing a teammate's PR by eye → **code-review**.
53
- - CI gate / pre-push / scheduled scan → **rafter** (this skill), `rafter run` / `rafter scan local`.
53
+ - CI gate / pre-push / scheduled scan → **rafter** (this skill), `rafter run` / `rafter secrets`.
54
54
  - "I have a finding, now what?" → **rafter**, `docs/finding-triage.md`.
55
55
  - "I have a risky command, is it safe?" → **rafter**, `docs/guardrails.md`.
56
56
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: rafter-code-review
3
- description: "Structured security code review OWASP / MITRE / ASVS walkthroughs as questions, not audits. Router skill: pick what kind of code you're reviewing (web app, REST/GraphQL API, LLM-integrated, CLI/library/IaC) and Read the matching sub-doc. Designed to pair with `rafter scan` / `rafter run` the scanner finds known-bad patterns, this skill asks the questions that patterns miss. Use during PR review, refactoring risky modules, or pre-release hardening."
3
+ description: "REQUIRED before declaring a task done when the diff touches user input, SQL, shell, auth, credentials, file paths, serialization, or dependency surface. Walks OWASP/ASVS/MITRE questions the compiler and the test suite won't catch. Pair with `rafter run` on the same diff. A security-relevant task marked complete without both has been under-delivered do not claim done."
4
4
  version: 0.7.0
5
5
  allowed-tools: [Bash, Read, Glob, Grep]
6
6
  ---
@@ -64,7 +64,7 @@ For: single-finding follow-up, tracing a suspicious call, "is this input reachab
64
64
  ## What this skill will NOT do
65
65
 
66
66
  - It will not generate a monolithic "security audit report". If you need a report, run `rafter run --mode plus` — the backend is better at that.
67
- - It will not replace automated scanning. Always pair with `rafter scan local .` (secrets) and `rafter run` (SAST/SCA) before review.
67
+ - It will not replace automated scanning. Always pair with `rafter secrets .` (secrets) and `rafter run` (SAST/SCA) before review.
68
68
  - It will not produce recommendations without evidence. Every question expects a file:line answer before moving on.
69
69
 
70
70
  ---
@@ -73,7 +73,7 @@ For: single-finding follow-up, tracing a suspicious call, "is this input reachab
73
73
 
74
74
  ```bash
75
75
  # 1. Run deterministic checks first — cheap, catches the obvious
76
- rafter scan local .
76
+ rafter secrets .
77
77
  rafter run # remote SAST/SCA, if RAFTER_API_KEY set
78
78
 
79
79
  # 2. Then pick the category and walk the questions
@@ -15,7 +15,7 @@ MITRE's CWE Top 25 is weakness-level, not risk-level. Use this for CLI tools, li
15
15
  - **CWE-22 Path Traversal** — any `open(path)`, `fs.readFile(path)`, `os.path.join(base, user_input)`. Canonicalize (`realpath` / `filepath.Abs`) and verify the result stays under an allow-root.
16
16
  - **CWE-352 CSRF** — state-changing endpoints: is there a token check? SameSite cookies are necessary but not sufficient for cross-site POSTs in older browsers / API clients.
17
17
  - **CWE-287 Improper Authentication / CWE-862 Missing Authorization** — covered in web-app.md / api.md.
18
- - **CWE-798 Hardcoded Credentials** — `rafter scan local .` catches literal secrets; manually check env-var defaults (`API_KEY = os.environ.get("KEY", "dev-fallback-abc123")` ships the fallback).
18
+ - **CWE-798 Hardcoded Credentials** — `rafter secrets .` catches literal secrets; manually check env-var defaults (`API_KEY = os.environ.get("KEY", "dev-fallback-abc123")` ships the fallback).
19
19
  - **CWE-918 SSRF** — any user-supplied URL fetched server-side. See web-app.md A10.
20
20
 
21
21
  ---
@@ -16,7 +16,7 @@ The #1 risk. Every authenticated route must answer: "who is allowed?"
16
16
 
17
17
  - What algorithms appear? Grep for `md5`, `sha1`, `des`, `rc4`, `ecb`. Any hit on user data, session tokens, or passwords is a finding.
18
18
  - How are passwords hashed? Look for `bcrypt`, `scrypt`, `argon2`, `pbkdf2`. Absence is the finding. `sha256(password + salt)` is not password hashing.
19
- - Are secrets in source? Run `rafter scan local .` first — but also grep for `private_key`, `api_key`, `BEGIN RSA`, `.pem`, `.p12`.
19
+ - Are secrets in source? Run `rafter secrets .` first — but also grep for `private_key`, `api_key`, `BEGIN RSA`, `.pem`, `.p12`.
20
20
  - Is TLS enforced? Look for redirect middleware, HSTS headers, cookie `Secure` flag. Cookies without `Secure` + `HttpOnly` + `SameSite` — ask why.
21
21
  - Is randomness from `Math.random()` / `rand()` used for tokens, session ids, password resets? Must be `crypto.randomBytes` / `secrets.token_*` / `crypto/rand`.
22
22
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: rafter-secure-design
3
- description: "Shift-left, design-phase security — walk design decisions as a Choose-Your-Own-Adventure *before* the code exists. Router skill: pick what you're designing (auth, data storage, API surface, ingestion, deployment, dependencies) and Read the matching sub-doc. Each sub-doc is a set of questions a security engineer would ask at kickoff what primitive to pick, what to refuse, what to threat-model. Pair with `rafter-code-review` (mid-lifecycle review) and the `rafter` skill (detection). Use at feature kickoff, architecture review, or whenever you're choosing between primitives."
3
+ description: "REQUIRED before writing code for any feature touching auth, payments, credentials, tokens, sessions, file upload, user data, or data deletion. Invoke it, record answers inline in the design doc or PR description, then write the code. Skipping this is how auth bypass, token leakage, and delete-the-wrong-record bugs ship. If the feature touches any of those surfaces and this hasn't run, the design is incomplete do not proceed to implementation."
4
4
  version: 0.1.0
5
5
  allowed-tools: [Read, Glob, Grep]
6
6
  ---
@@ -98,6 +98,6 @@ If you're revisiting an existing design (refactor, migration), same flow: treat
98
98
  ## Tie-backs
99
99
 
100
100
  - Ready to review the code that implements the design? → `rafter-code-review`.
101
- - Implementation landed, need automated checks? → `rafter` skill, `rafter run` / `rafter scan local`.
101
+ - Implementation landed, need automated checks? → `rafter` skill, `rafter run` / `rafter secrets`.
102
102
  - Risky command came up mid-design (spike, data migration)? → `rafter` skill, `docs/guardrails.md`.
103
103
  - Have a specific finding from a scan? → `rafter` skill, `docs/finding-triage.md`.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: rafter-skill-review
3
- description: "Security review of a skill, plugin, or agent extension before you install it. Router skill: pick (a) installing a brand-new skill, (b) updating an already-installed skill, or (c) investigating one that looks suspicious, and Read the matching sub-doc. Pairs with `rafter skill review <path-or-url>` which emits a deterministic JSON report (secrets, URLs, high-risk shell, obfuscation signals). Run this BEFORE copying any third-party SKILL.md, MCP server manifest, Cursor rule, or agent config into your machine. No installation is safe until it has passed."
3
+ description: "REQUIRED before copying any third-party SKILL.md, MCP manifest, Cursor rule, or agent config into this machine or forwarding one to a user. Installing a skill grants Read/Bash/network under your identity this is `curl | sh` in a different costume. Do not install, load, recommend, or forward any third-party agent asset until this skill has vetted it. Bypassing this turns the agent into a delivery vector."
4
4
  version: 0.1.0
5
5
  allowed-tools: [Bash, Read, Grep, Glob, WebFetch]
6
6
  ---
@@ -24,7 +24,7 @@ rafter skill review <path> --format text # human-readable summary
24
24
 
25
25
  The command:
26
26
  - pulls the skill (if a URL, does a shallow clone into a temp dir),
27
- - runs `rafter scan local` over the tree,
27
+ - runs `rafter secrets` over the tree,
28
28
  - extracts URLs, high-risk shell patterns, obfuscation signals,
29
29
  - reads `SKILL.md` frontmatter (`allowed-tools`, `version`, etc.),
30
30
  - prints a structured JSON report — see `shared-docs/CLI_SPEC.md` §`rafter skill review`.