@rafter-security/cli 0.6.5 → 0.6.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.
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # @rafter-security/cli
2
2
 
3
- Node.js CLI for [Rafter](https://rafter.so) — zero-setup security for AI builders. This is the **full-featured package** with both backend scanning and agent security.
3
+ Node.js CLI for [Rafter](https://rafter.so) — the security toolkit for developers. This is the **full-featured package** with both local security and remote code analysis.
4
4
 
5
- **Backend scanning** — Remote SAST/SCA via Rafter API. Trigger scans, retrieve structured vulnerability reports, pipe to any tool.
5
+ **Local security toolkit** — Fast, deterministic secret scanning (21+ patterns, Gitleaks), policy enforcement with risk-tiered rules, pre-commit hooks, extension auditing, custom rule authoring, and full audit logging. Works with Claude Code, Codex CLI, OpenClaw, and 5 more platforms. No API key required. No data leaves your machine.
6
6
 
7
- **Agent security** — Local-first protection for autonomous AI agents. Secret scanning (21+ patterns, Gitleaks), command interception with risk-tiered approval, pre-commit hooks, skill/extension auditing, and full audit logging. Works with Claude Code, Codex CLI, and OpenClaw. No API key required.
7
+ **Remote code analysis** — Deep security audits that combine agentic analysis with a full SAST/SCA toolchain. The engine examines your codebase the way a professional cybersecurity auditor would — tracing data flows, reasoning about business logic, and surfacing vulnerabilities that static rules alone miss — then cross-references findings with industry-standard static analysis and dependency scanning. Structured JSON reports with documented exit codes. Your code is deleted immediately after analysis completes.
8
8
 
9
9
  ## Installation
10
10
 
@@ -23,7 +23,7 @@ yarn global add @rafter-security/cli
23
23
 
24
24
  ### Getting an API Key
25
25
 
26
- To use backend scanning features, you'll need a Rafter API key:
26
+ To use backend code analysis features, you'll need a Rafter API key:
27
27
 
28
28
  1. **Sign up**: Create an account at [rafter.so](https://rafter.so)
29
29
  2. **Get API key**: Navigate to Dashboard → Settings → API Keys
@@ -36,9 +36,9 @@ To use backend scanning features, you'll need a Rafter API key:
36
36
  echo "RAFTER_API_KEY=your-api-key-here" >> .env
37
37
  ```
38
38
 
39
- **Note**: Agent security features (secret scanning, command execution) work **without an API key**. Only backend scanning requires authentication.
39
+ **Note**: Agent security features (secret scanning, command execution) work **without an API key**. Only backend code analysis requires authentication.
40
40
 
41
- ### Backend Scanning
41
+ ### Backend Code Analysis
42
42
 
43
43
  ```bash
44
44
  # Set your API key (from above)
@@ -54,10 +54,10 @@ rafter get <scan-id>
54
54
  rafter usage
55
55
  ```
56
56
 
57
- ### Agent Security
57
+ ### Local Security
58
58
 
59
59
  ```bash
60
- # Initialize agent security
60
+ # Initialize local security
61
61
  rafter agent init
62
62
 
63
63
  # Scan files for secrets
@@ -77,7 +77,7 @@ rafter agent config show
77
77
 
78
78
  | Flag | Description |
79
79
  |------|-------------|
80
- | `-a, --agent` | Plain output for AI agents (no colors, no emoji) |
80
+ | `-a, --agent` | Plain output (no colors, no emoji) |
81
81
  | `-V, --version` | Print version |
82
82
  | `-h, --help` | Show help |
83
83
 
@@ -92,7 +92,7 @@ Trigger a new security scan for your repository.
92
92
  - `-r, --repo <repo>` - Repository in format `org/repo` (default: auto-detected)
93
93
  - `-b, --branch <branch>` - Branch name (default: auto-detected)
94
94
  - `-k, --api-key <key>` - API key (or set `RAFTER_API_KEY` env var)
95
- - `-f, --format <format>` - Output format: `json` or `md` (default: `json`)
95
+ - `-f, --format <format>` - Output format: `json` or `md` (default: `md`)
96
96
  - `--skip-interactive` - Don't wait for scan completion
97
97
  - `--quiet` - Suppress status messages
98
98
 
@@ -116,7 +116,7 @@ Retrieve results from a completed scan.
116
116
 
117
117
  **Options:**
118
118
  - `-k, --api-key <key>` - API key (or set `RAFTER_API_KEY` env var)
119
- - `-f, --format <format>` - Output format: `json` or `md` (default: `json`)
119
+ - `-f, --format <format>` - Output format: `json` or `md` (default: `md`)
120
120
  - `--interactive` - Poll until scan completes
121
121
  - `--quiet` - Suppress status messages
122
122
 
@@ -143,13 +143,13 @@ rafter usage
143
143
 
144
144
  ---
145
145
 
146
- ## Agent Security Commands
146
+ ## Local Security Commands
147
147
 
148
- Rafter provides local security features for autonomous agents (OpenClaw, Claude Code) to prevent secrets leakage and dangerous operations.
148
+ Rafter is a **security primitive** that any developer or tool can call and trust. Stable exit codes, deterministic findings, and structured output mean any workflow can integrate Rafter without reading prose.
149
149
 
150
150
  ### `rafter agent init [options]`
151
151
 
152
- Initialize agent security system.
152
+ Initialize local security system.
153
153
 
154
154
  **Options:**
155
155
  - `--risk-level <level>` - Set risk level: `minimal`, `moderate`, or `aggressive` (default: `moderate`)
@@ -167,7 +167,7 @@ Initialize agent security system.
167
167
  **What it does:**
168
168
  - Creates `~/.rafter/config.json` configuration
169
169
  - Initializes directory structure
170
- - Detects available agent environments
170
+ - Detects installed platforms
171
171
  - Installs opted-in integrations (skills, hooks, MCP servers)
172
172
  - Sets up audit logging
173
173
 
@@ -562,9 +562,9 @@ The CLI automatically detects your repository and branch from the current Git re
562
562
 
563
563
  **Note**: The CLI only scans remote repositories, not your current local branch.
564
564
 
565
- ### Agent Security Configuration
565
+ ### Local Security Configuration
566
566
 
567
- Agent security settings are stored in `~/.rafter/config.json`. Key settings:
567
+ Security settings are stored in `~/.rafter/config.json`. Key settings:
568
568
 
569
569
  **Risk Levels:**
570
570
  - `minimal` - Basic guidance only, most commands allowed
@@ -584,7 +584,7 @@ Agent security settings are stored in `~/.rafter/config.json`. Key settings:
584
584
 
585
585
  ## OpenClaw Integration
586
586
 
587
- Rafter integrates seamlessly with [OpenClaw](https://openclaw.com) autonomous agents.
587
+ Rafter integrates seamlessly with [OpenClaw](https://openclaw.com).
588
588
 
589
589
  ### Setup
590
590
 
@@ -624,7 +624,7 @@ When OpenClaw is detected, `rafter agent init` automatically installs a skill to
624
624
 
625
625
  Rafter provides TWO skills for Claude Code:
626
626
 
627
- ### 1. Backend Scanning Skill (Core Feature)
627
+ ### 1. Backend Code Analysis Skill (Core Feature)
628
628
 
629
629
  **Automatic Integration** - Claude can proactively suggest security scans
630
630
 
@@ -650,7 +650,7 @@ Claude will automatically suggest Rafter scans when you mention security, vulner
650
650
  Can you run a Rafter security scan on this repo?
651
651
  ```
652
652
 
653
- ### 2. Agent Security Skill
653
+ ### 2. Local Security Skill
654
654
 
655
655
  **User-Invoked** - Requires explicit commands for safety
656
656
 
@@ -680,10 +680,10 @@ Explicitly invoke commands:
680
680
 
681
681
  ### Why Two Skills?
682
682
 
683
- - **Backend skill** - Safe for Claude to auto-invoke (read-only API calls)
683
+ - **Backend code analysis skill** - Safe for Claude to auto-invoke (read-only API calls)
684
684
  - **Agent security skill** - Requires user permission (local file access, command execution)
685
685
 
686
- This separation emphasizes Rafter's core backend scanning capabilities while keeping local security features safely behind user control.
686
+ This separation emphasizes Rafter's core backend code analysis capabilities while keeping local security features safely behind user control.
687
687
 
688
688
  ## Documentation
689
689
 
@@ -2,6 +2,7 @@ import { Command } from "commander";
2
2
  import { createAuditCommand } from "./audit.js";
3
3
  import { createScanCommand } from "./scan.js";
4
4
  import { createInitCommand } from "./init.js";
5
+ import { createInitProjectCommand } from "./init-project.js";
5
6
  import { createConfigCommand } from "./config.js";
6
7
  import { createExecCommand } from "./exec.js";
7
8
  import { createAuditSkillCommand } from "./audit-skill.js";
@@ -15,6 +16,7 @@ export function createAgentCommand() {
15
16
  .description("Agent security features");
16
17
  // Add subcommands
17
18
  agent.addCommand(createInitCommand());
19
+ agent.addCommand(createInitProjectCommand());
18
20
  agent.addCommand(createScanCommand());
19
21
  agent.addCommand(createExecCommand());
20
22
  agent.addCommand(createConfigCommand());
@@ -0,0 +1,164 @@
1
+ import { Command } from "commander";
2
+ import { injectInstructionFile } from "./instruction-block.js";
3
+ import { fmt } from "../../utils/formatter.js";
4
+ import fs from "fs";
5
+ import path from "path";
6
+ import { execSync } from "child_process";
7
+ /** Find the git root directory, or null if not in a git repo */
8
+ function findGitRoot() {
9
+ try {
10
+ return execSync("git rev-parse --show-toplevel", {
11
+ encoding: "utf-8",
12
+ timeout: 5000,
13
+ stdio: ["pipe", "pipe", "ignore"],
14
+ }).trim();
15
+ }
16
+ catch {
17
+ return null;
18
+ }
19
+ }
20
+ /**
21
+ * All project-level instruction file targets.
22
+ *
23
+ * These are files that AI agents read at session start when working in a project.
24
+ * Unlike global files (~/.claude/CLAUDE.md), these live in the repo and are
25
+ * committed alongside the code so every agent session sees them.
26
+ */
27
+ function getProjectTargets(projectRoot) {
28
+ return [
29
+ {
30
+ platform: "Claude Code",
31
+ filePath: path.join(projectRoot, ".claude", "CLAUDE.md"),
32
+ description: "Claude Code project instructions",
33
+ },
34
+ {
35
+ platform: "Codex CLI",
36
+ filePath: path.join(projectRoot, "AGENTS.md"),
37
+ description: "Codex CLI project instructions",
38
+ },
39
+ {
40
+ platform: "Gemini CLI",
41
+ filePath: path.join(projectRoot, "GEMINI.md"),
42
+ description: "Gemini CLI project instructions",
43
+ },
44
+ {
45
+ platform: "Cursor",
46
+ filePath: path.join(projectRoot, ".cursor", "rules", "rafter-security.mdc"),
47
+ description: "Cursor project rules",
48
+ },
49
+ {
50
+ platform: "Windsurf",
51
+ filePath: path.join(projectRoot, ".windsurfrules"),
52
+ description: "Windsurf project rules",
53
+ },
54
+ {
55
+ platform: "Continue.dev",
56
+ filePath: path.join(projectRoot, ".continuerules"),
57
+ description: "Continue.dev project rules",
58
+ },
59
+ {
60
+ platform: "Aider",
61
+ filePath: path.join(projectRoot, ".aider", "conventions.md"),
62
+ description: "Aider project conventions",
63
+ },
64
+ ];
65
+ }
66
+ export function createInitProjectCommand() {
67
+ return new Command("init-project")
68
+ .description("Generate project-level instruction files so AI agents discover Rafter at session start")
69
+ .option("--only <platforms>", "Comma-separated list of platforms (claude-code,codex,gemini,cursor,windsurf,continue,aider)")
70
+ .option("--list", "List which files would be created without writing them")
71
+ .action(async (opts) => {
72
+ const gitRoot = findGitRoot();
73
+ if (!gitRoot) {
74
+ console.error(fmt.error("Not in a git repository. Run this command from inside a project."));
75
+ process.exit(1);
76
+ }
77
+ console.log(fmt.header("Rafter Project Setup"));
78
+ console.log(fmt.info(`Project root: ${gitRoot}`));
79
+ console.log();
80
+ const allTargets = getProjectTargets(gitRoot);
81
+ // Filter by --only if specified
82
+ let targets = allTargets;
83
+ if (opts.only) {
84
+ const platformMap = {
85
+ "claude-code": "Claude Code",
86
+ "claude": "Claude Code",
87
+ "codex": "Codex CLI",
88
+ "gemini": "Gemini CLI",
89
+ "cursor": "Cursor",
90
+ "windsurf": "Windsurf",
91
+ "continue": "Continue.dev",
92
+ "aider": "Aider",
93
+ };
94
+ const requested = opts.only.split(",").map((s) => s.trim().toLowerCase());
95
+ const platformNames = requested.map(r => platformMap[r]).filter(Boolean);
96
+ if (platformNames.length === 0) {
97
+ console.error(fmt.error(`Unknown platforms: ${opts.only}`));
98
+ console.error("Valid: claude-code, codex, gemini, cursor, windsurf, continue, aider");
99
+ process.exit(1);
100
+ }
101
+ targets = allTargets.filter(t => platformNames.includes(t.platform));
102
+ }
103
+ // --list mode: show what would be created
104
+ if (opts.list) {
105
+ for (const target of targets) {
106
+ const exists = fs.existsSync(target.filePath);
107
+ const hasMarker = exists && fs.readFileSync(target.filePath, "utf-8").includes("<!-- rafter:start -->");
108
+ const status = hasMarker ? "update" : exists ? "append" : "create";
109
+ const rel = path.relative(gitRoot, target.filePath);
110
+ console.log(` [${status}] ${rel} — ${target.description}`);
111
+ }
112
+ console.log();
113
+ console.log(fmt.info("Run without --list to write files."));
114
+ return;
115
+ }
116
+ // Write instruction files
117
+ let created = 0;
118
+ let updated = 0;
119
+ let failed = 0;
120
+ for (const target of targets) {
121
+ const rel = path.relative(gitRoot, target.filePath);
122
+ const existed = fs.existsSync(target.filePath);
123
+ const hadMarker = existed && fs.readFileSync(target.filePath, "utf-8").includes("<!-- rafter:start -->");
124
+ try {
125
+ injectInstructionFile(target.filePath);
126
+ if (hadMarker) {
127
+ console.log(fmt.success(`[updated] ${rel}`));
128
+ updated++;
129
+ }
130
+ else {
131
+ console.log(fmt.success(`[created] ${rel}`));
132
+ created++;
133
+ }
134
+ }
135
+ catch (e) {
136
+ console.log(fmt.error(`[failed] ${rel} — ${e}`));
137
+ failed++;
138
+ }
139
+ }
140
+ // Check for .rafter.yml
141
+ const policyPath = path.join(gitRoot, ".rafter.yml");
142
+ if (!fs.existsSync(policyPath)) {
143
+ console.log(fmt.info(`[skipped] .rafter.yml — not found (optional: create one for project-specific policy)`));
144
+ }
145
+ // Check for pre-commit hook
146
+ const hookPath = path.join(gitRoot, ".git", "hooks", "pre-commit");
147
+ const hasRafterHook = fs.existsSync(hookPath) &&
148
+ fs.readFileSync(hookPath, "utf-8").includes("rafter");
149
+ if (!hasRafterHook) {
150
+ console.log(fmt.info(`[hint] Run \`rafter agent install-hook\` to add pre-commit secret scanning`));
151
+ }
152
+ console.log();
153
+ if (created > 0 || updated > 0) {
154
+ console.log(fmt.success(`Done: ${created} created, ${updated} updated${failed > 0 ? `, ${failed} failed` : ""}`));
155
+ console.log();
156
+ console.log("Agents starting sessions in this project will now see Rafter security context.");
157
+ console.log("Consider committing these files so all contributors benefit.");
158
+ }
159
+ else if (failed > 0) {
160
+ console.log(fmt.error(`All ${failed} files failed to write.`));
161
+ }
162
+ console.log();
163
+ });
164
+ }