@rafter-security/cli 0.6.4 → 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.
@@ -0,0 +1,526 @@
1
+ import { Command } from "commander";
2
+ import { readFileSync } from "fs";
3
+ import { dirname, join } from "path";
4
+ import { fileURLToPath } from "url";
5
+ const __dirname = dirname(fileURLToPath(import.meta.url));
6
+ const RESOURCES_DIR = join(__dirname, "..", "..", "resources", "skills");
7
+ function loadSkill(name) {
8
+ const raw = readFileSync(join(RESOURCES_DIR, name, "SKILL.md"), "utf-8");
9
+ // Strip YAML frontmatter
10
+ return raw.replace(/^---[\s\S]*?---\n*/, "").trim();
11
+ }
12
+ function extractSections(content, headings) {
13
+ const lines = content.split("\n");
14
+ const sections = [];
15
+ let capturing = false;
16
+ let captureLevel = 0;
17
+ let inCodeBlock = false;
18
+ for (const line of lines) {
19
+ if (line.trimStart().startsWith("```")) {
20
+ inCodeBlock = !inCodeBlock;
21
+ if (capturing)
22
+ sections.push(line);
23
+ continue;
24
+ }
25
+ if (inCodeBlock) {
26
+ if (capturing)
27
+ sections.push(line);
28
+ continue;
29
+ }
30
+ const headingMatch = line.match(/^(#{1,4})\s+(.*)/);
31
+ if (headingMatch) {
32
+ const level = headingMatch[1].length;
33
+ const title = headingMatch[2].trim();
34
+ if (headings.some((h) => title.toLowerCase().includes(h.toLowerCase()))) {
35
+ capturing = true;
36
+ captureLevel = level;
37
+ sections.push(line);
38
+ continue;
39
+ }
40
+ if (capturing && level <= captureLevel) {
41
+ capturing = false;
42
+ }
43
+ }
44
+ if (capturing) {
45
+ sections.push(line);
46
+ }
47
+ }
48
+ return sections.join("\n").trim();
49
+ }
50
+ function buildTopics() {
51
+ return {
52
+ security: {
53
+ description: "Local agent security — scanning, auditing, risk assessment",
54
+ render: () => loadSkill("rafter-agent-security"),
55
+ },
56
+ scanning: {
57
+ description: "Remote SAST/SCA code analysis via backend API",
58
+ render: () => loadSkill("rafter"),
59
+ },
60
+ commands: {
61
+ description: "Condensed command reference for all rafter commands",
62
+ render: () => {
63
+ const security = loadSkill("rafter-agent-security");
64
+ const backend = loadSkill("rafter");
65
+ const secCmds = extractSections(security, [
66
+ "Commands",
67
+ "/rafter-scan",
68
+ "/rafter-bash",
69
+ "/rafter-audit-skill",
70
+ "/rafter-audit",
71
+ ]);
72
+ const backCmds = extractSections(backend, [
73
+ "Core Commands",
74
+ "Trigger",
75
+ "Get Scan",
76
+ "Check API",
77
+ ]);
78
+ return [
79
+ "# Rafter Command Reference",
80
+ "",
81
+ "## Backend (Remote Code Analysis)",
82
+ "",
83
+ backCmds,
84
+ "",
85
+ "## Agent (Local Security)",
86
+ "",
87
+ secCmds,
88
+ ].join("\n");
89
+ },
90
+ },
91
+ setup: {
92
+ description: "Setup instructions for all supported agent platforms",
93
+ render: () => renderSetupGuide(),
94
+ },
95
+ "setup/claude-code": {
96
+ description: "Setup instructions for Claude Code",
97
+ render: () => renderPlatformSetup("claude-code"),
98
+ },
99
+ "setup/codex": {
100
+ description: "Setup instructions for Codex CLI",
101
+ render: () => renderPlatformSetup("codex"),
102
+ },
103
+ "setup/gemini": {
104
+ description: "Setup instructions for Gemini CLI",
105
+ render: () => renderPlatformSetup("gemini"),
106
+ },
107
+ "setup/cursor": {
108
+ description: "Setup instructions for Cursor",
109
+ render: () => renderPlatformSetup("cursor"),
110
+ },
111
+ "setup/windsurf": {
112
+ description: "Setup instructions for Windsurf",
113
+ render: () => renderPlatformSetup("windsurf"),
114
+ },
115
+ "setup/aider": {
116
+ description: "Setup instructions for Aider",
117
+ render: () => renderPlatformSetup("aider"),
118
+ },
119
+ "setup/openclaw": {
120
+ description: "Setup instructions for OpenClaw",
121
+ render: () => renderPlatformSetup("openclaw"),
122
+ },
123
+ "setup/continue": {
124
+ description: "Setup instructions for Continue.dev",
125
+ render: () => renderPlatformSetup("continue"),
126
+ },
127
+ "setup/generic": {
128
+ description: "Setup instructions for unsupported / generic agents",
129
+ render: () => renderPlatformSetup("generic"),
130
+ },
131
+ pricing: {
132
+ description: "What's free, what's paid, and the philosophy behind it",
133
+ render: () => [
134
+ "# Rafter Pricing",
135
+ "",
136
+ "**Free forever for individuals and open source. No account required. No telemetry.**",
137
+ "",
138
+ "## What's Free",
139
+ "",
140
+ "All local agent security features are free with no limits:",
141
+ "",
142
+ "- Secret scanning (21+ patterns, Gitleaks integration)",
143
+ "- Pre-commit hooks (local and global)",
144
+ "- Command interception with risk-tiered approval",
145
+ "- Skill/extension auditing",
146
+ "- Audit logging",
147
+ "- MCP server for tool integration",
148
+ "- CI/CD pipeline generation",
149
+ "- All supported agent integrations (Claude Code, Codex, Gemini, Cursor, Windsurf, Aider, OpenClaw, Continue.dev)",
150
+ "",
151
+ "No API key. No sign-up. No telemetry. No data collection. No network access required.",
152
+ "Everything runs locally on your machine. MIT licensed.",
153
+ "",
154
+ "## Remote Code Analysis (API)",
155
+ "",
156
+ "Remote SAST/SCA scanning via the Rafter API has a free tier.",
157
+ "Sign up at rafter.so for an API key. Enterprise plans offer higher",
158
+ "limits, dashboards, policy management, and compliance reporting.",
159
+ "",
160
+ "## Philosophy",
161
+ "",
162
+ "Security tooling should be free for the people writing code.",
163
+ "Generous free tiers drive bottom-up adoption. Enterprise value",
164
+ "comes from dashboards, policy, and compliance — not from gating",
165
+ "the tools developers use every day.",
166
+ ].join("\n"),
167
+ },
168
+ all: {
169
+ description: "Everything — full security + scanning + setup briefing",
170
+ render: () => {
171
+ const topics = buildTopics();
172
+ return [
173
+ topics.scanning.render(),
174
+ "",
175
+ "---",
176
+ "",
177
+ topics.security.render(),
178
+ "",
179
+ "---",
180
+ "",
181
+ topics.setup.render(),
182
+ ].join("\n");
183
+ },
184
+ },
185
+ };
186
+ }
187
+ const PLATFORM_GUIDES = {
188
+ "claude-code": `# Rafter Setup — Claude Code
189
+
190
+ Claude Code has native skill support and hook integration.
191
+
192
+ ## Automated Setup
193
+
194
+ \`\`\`bash
195
+ rafter agent init --with-claude-code
196
+ \`\`\`
197
+
198
+ This installs:
199
+ - Skills to \`~/.claude/skills/rafter/\` and \`~/.claude/skills/rafter-agent-security/\`
200
+ - PreToolUse/PostToolUse hooks to \`~/.claude/settings.json\`
201
+
202
+ ## What You Get
203
+
204
+ - **Native skill triggers**: Claude Code will automatically invoke rafter skills
205
+ when security-related topics come up
206
+ - **Automatic command interception**: All Bash tool calls are routed through
207
+ rafter's risk assessment via hooks — no manual invocation needed
208
+ - **Write/Edit scanning**: File writes are checked for secrets before completing
209
+
210
+ ## Manual Setup (if automated init isn't available)
211
+
212
+ 1. Run \`rafter brief security\` and save key concepts to your memory
213
+ 2. Run \`rafter brief scanning\` and save the command reference
214
+ 3. Before commits, run: \`rafter scan local .\`
215
+ 4. For remote analysis: \`rafter run\``,
216
+ codex: `# Rafter Setup — Codex CLI
217
+
218
+ Codex CLI supports skill files in \`~/.agents/skills/\`.
219
+
220
+ ## Automated Setup
221
+
222
+ \`\`\`bash
223
+ rafter agent init --with-codex
224
+ \`\`\`
225
+
226
+ This installs skills to \`~/.agents/skills/rafter/\` and \`~/.agents/skills/rafter-agent-security/\`.
227
+
228
+ ## Manual Setup
229
+
230
+ 1. Copy skill files manually:
231
+ \`\`\`bash
232
+ mkdir -p ~/.agents/skills/rafter
233
+ rafter brief scanning > ~/.agents/skills/rafter/SKILL.md
234
+ rafter brief security > ~/.agents/skills/rafter-agent-security/SKILL.md
235
+ \`\`\`
236
+ 2. Or run \`rafter brief all\` and save to your agent's instructions`,
237
+ gemini: `# Rafter Setup — Gemini CLI
238
+
239
+ Gemini CLI uses MCP for tool integration.
240
+
241
+ ## Automated Setup
242
+
243
+ \`\`\`bash
244
+ rafter agent init --with-gemini
245
+ \`\`\`
246
+
247
+ This registers \`rafter mcp serve\` as an MCP server in Gemini's config.
248
+
249
+ ## Manual Setup
250
+
251
+ Add to your Gemini CLI MCP config:
252
+ \`\`\`json
253
+ {
254
+ "mcpServers": {
255
+ "rafter": {
256
+ "command": "rafter",
257
+ "args": ["mcp", "serve"]
258
+ }
259
+ }
260
+ }
261
+ \`\`\`
262
+
263
+ ## Supplementing with Brief
264
+
265
+ MCP gives you tool access, but not the full context of when/why to scan.
266
+ Run these and save the output to your agent's context:
267
+ \`\`\`bash
268
+ rafter brief security
269
+ rafter brief scanning
270
+ \`\`\``,
271
+ cursor: `# Rafter Setup — Cursor
272
+
273
+ Cursor uses MCP for tool integration.
274
+
275
+ ## Automated Setup
276
+
277
+ \`\`\`bash
278
+ rafter agent init --with-cursor
279
+ \`\`\`
280
+
281
+ This registers \`rafter mcp serve\` in Cursor's MCP config.
282
+
283
+ ## Manual Setup
284
+
285
+ Add to \`~/.cursor/mcp.json\`:
286
+ \`\`\`json
287
+ {
288
+ "mcpServers": {
289
+ "rafter": {
290
+ "command": "rafter",
291
+ "args": ["mcp", "serve"]
292
+ }
293
+ }
294
+ }
295
+ \`\`\`
296
+
297
+ ## Supplementing with Brief
298
+
299
+ \`\`\`bash
300
+ rafter brief security # save to your rules/instructions
301
+ rafter brief commands # command reference
302
+ \`\`\``,
303
+ windsurf: `# Rafter Setup — Windsurf
304
+
305
+ Windsurf uses MCP for tool integration.
306
+
307
+ ## Automated Setup
308
+
309
+ \`\`\`bash
310
+ rafter agent init --with-windsurf
311
+ \`\`\`
312
+
313
+ ## Manual Setup
314
+
315
+ Add to Windsurf's MCP config (\`~/.codeium/windsurf/mcp_config.json\`):
316
+ \`\`\`json
317
+ {
318
+ "mcpServers": {
319
+ "rafter": {
320
+ "command": "rafter",
321
+ "args": ["mcp", "serve"]
322
+ }
323
+ }
324
+ }
325
+ \`\`\``,
326
+ aider: `# Rafter Setup — Aider
327
+
328
+ Aider uses MCP for tool integration.
329
+
330
+ ## Automated Setup
331
+
332
+ \`\`\`bash
333
+ rafter agent init --with-aider
334
+ \`\`\`
335
+
336
+ ## Manual Setup
337
+
338
+ Add to \`~/.aider.conf.yml\`:
339
+ \`\`\`yaml
340
+ mcp-servers:
341
+ - name: rafter
342
+ command: rafter mcp serve
343
+ \`\`\`
344
+
345
+ ## Supplementing with Brief
346
+
347
+ Aider doesn't have persistent memory, so run before each session:
348
+ \`\`\`bash
349
+ rafter brief commands # quick command reference
350
+ \`\`\``,
351
+ openclaw: `# Rafter Setup — OpenClaw
352
+
353
+ OpenClaw has native skill support.
354
+
355
+ ## Automated Setup
356
+
357
+ \`\`\`bash
358
+ rafter agent init --with-openclaw
359
+ \`\`\`
360
+
361
+ This installs the security skill to \`~/.openclaw/skills/rafter-security.md\`.
362
+
363
+ ## Manual Setup
364
+
365
+ \`\`\`bash
366
+ mkdir -p ~/.openclaw/skills
367
+ rafter brief security > ~/.openclaw/skills/rafter-security.md
368
+ \`\`\``,
369
+ continue: `# Rafter Setup — Continue.dev
370
+
371
+ Continue.dev uses MCP for tool integration.
372
+
373
+ ## Automated Setup
374
+
375
+ \`\`\`bash
376
+ rafter agent init --with-continue
377
+ \`\`\`
378
+
379
+ ## Manual Setup
380
+
381
+ Add to Continue.dev's MCP config (\`~/.continue/config.json\`):
382
+ \`\`\`json
383
+ {
384
+ "mcpServers": [{
385
+ "name": "rafter",
386
+ "command": "rafter",
387
+ "args": ["mcp", "serve"]
388
+ }]
389
+ }
390
+ \`\`\``,
391
+ generic: `# Rafter Setup — Generic / Unsupported Agents
392
+
393
+ For agents on platforms rafter doesn't have native integration with.
394
+
395
+ ## If Your Agent Has a Memory / Instructions System
396
+
397
+ Save rafter knowledge to your agent's persistent memory or system prompt:
398
+
399
+ \`\`\`bash
400
+ # Save security knowledge
401
+ rafter brief security
402
+ # -> Copy the output into your agent's memory/instructions
403
+
404
+ # Save command reference
405
+ rafter brief commands
406
+ # -> Copy the output into your agent's memory/instructions
407
+ \`\`\`
408
+
409
+ ## If Your Agent Supports MCP
410
+
411
+ Register rafter as an MCP server:
412
+ \`\`\`json
413
+ {
414
+ "command": "rafter",
415
+ "args": ["mcp", "serve"]
416
+ }
417
+ \`\`\`
418
+
419
+ ## If Your Agent Has Neither
420
+
421
+ Run \`rafter brief\` at the start of each session to load context:
422
+ \`\`\`bash
423
+ rafter brief security # understand the security layer
424
+ rafter brief commands # know what commands are available
425
+ \`\`\`
426
+
427
+ ## Key Commands to Know
428
+
429
+ - \`rafter scan local .\` — scan for secrets locally (no API key needed)
430
+ - \`rafter run\` — trigger remote SAST/SCA analysis (needs API key)
431
+ - \`rafter get <id>\` — retrieve scan results
432
+ - \`rafter agent audit\` — review security event log
433
+ - \`rafter agent exec <cmd>\` — run a command with risk assessment`,
434
+ };
435
+ function renderSetupGuide() {
436
+ const platforms = [
437
+ "claude-code",
438
+ "codex",
439
+ "openclaw",
440
+ "gemini",
441
+ "cursor",
442
+ "windsurf",
443
+ "aider",
444
+ "continue",
445
+ "generic",
446
+ ];
447
+ const parts = [
448
+ "# Rafter Setup Guide",
449
+ "",
450
+ "Platform-specific setup instructions. Use `rafter brief setup/<platform>`",
451
+ "for details on a specific platform.",
452
+ "",
453
+ "## Supported Platforms",
454
+ "",
455
+ "### Skill-Based (native skill file support)",
456
+ "- **Claude Code**: `rafter agent init --with-claude-code` — skills + hooks",
457
+ "- **Codex CLI**: `rafter agent init --with-codex` — skills",
458
+ "- **OpenClaw**: `rafter agent init --with-openclaw` — skills",
459
+ "",
460
+ "### MCP-Based (tool server integration)",
461
+ "- **Gemini CLI**: `rafter agent init --with-gemini`",
462
+ "- **Cursor**: `rafter agent init --with-cursor`",
463
+ "- **Windsurf**: `rafter agent init --with-windsurf`",
464
+ "- **Aider**: `rafter agent init --with-aider`",
465
+ "- **Continue.dev**: `rafter agent init --with-continue`",
466
+ "",
467
+ "### Generic / Unsupported",
468
+ "For any other agent, use `rafter brief` to load context manually.",
469
+ "See `rafter brief setup/generic` for details.",
470
+ "",
471
+ "## Quick Start (Any Platform)",
472
+ "",
473
+ "```bash",
474
+ "# 1. Initialize with your platform",
475
+ "rafter agent init --with-<platform>",
476
+ "",
477
+ "# 2. If your platform doesn't have native integration,",
478
+ "# load knowledge manually:",
479
+ "rafter brief security # understand the security layer",
480
+ "rafter brief scanning # understand remote code analysis",
481
+ "rafter brief commands # full command reference",
482
+ "```",
483
+ ];
484
+ return parts.join("\n");
485
+ }
486
+ function renderPlatformSetup(platform) {
487
+ return PLATFORM_GUIDES[platform] || `Unknown platform: ${platform}`;
488
+ }
489
+ function renderTopicList(topics) {
490
+ const lines = [
491
+ "Available topics:",
492
+ "",
493
+ ];
494
+ for (const [name, entry] of Object.entries(topics)) {
495
+ lines.push(` ${name.padEnd(22)} ${entry.description}`);
496
+ }
497
+ lines.push("");
498
+ lines.push("Usage: rafter brief <topic>");
499
+ lines.push("");
500
+ lines.push("Examples:");
501
+ lines.push(" rafter brief security # local security briefing");
502
+ lines.push(" rafter brief scanning # remote code analysis briefing");
503
+ lines.push(" rafter brief commands # full command reference");
504
+ lines.push(" rafter brief setup/claude-code # Claude Code setup guide");
505
+ lines.push(" rafter brief setup/generic # setup for any agent");
506
+ lines.push(" rafter brief all # everything");
507
+ return lines.join("\n");
508
+ }
509
+ export function createBriefCommand() {
510
+ return new Command("brief")
511
+ .description("Print rafter knowledge for any agent — skills, commands, setup guides")
512
+ .argument("[topic]", "Topic to brief on (omit to list topics)")
513
+ .action((topic) => {
514
+ const topics = buildTopics();
515
+ if (!topic) {
516
+ process.stdout.write(renderTopicList(topics) + "\n");
517
+ return;
518
+ }
519
+ const entry = topics[topic];
520
+ if (!entry) {
521
+ process.stderr.write(`Unknown topic: ${topic}\n\n${renderTopicList(topics)}\n`);
522
+ process.exit(1);
523
+ }
524
+ process.stdout.write(entry.render() + "\n");
525
+ });
526
+ }
@@ -47,7 +47,7 @@ export function createCiInitCommand() {
47
47
  if (platform === "github") {
48
48
  console.log();
49
49
  console.log("Alternatives:");
50
- console.log(" - GitHub Action: uses: Raftersecurity/rafter-cli@v0");
50
+ console.log(" - GitHub Action: uses: Raftersecurity/rafter-cli@v1");
51
51
  console.log(" - Pre-commit: https://github.com/Raftersecurity/rafter-cli#pre-commit-framework");
52
52
  }
53
53
  console.log();
@@ -74,7 +74,7 @@ function generateTemplate(platform, withBackend) {
74
74
  }
75
75
  function githubTemplate(withBackend) {
76
76
  let yaml = `# Generated by: rafter ci init
77
- # Alternative: uses: Raftersecurity/rafter-cli@v0
77
+ # Alternative: uses: Raftersecurity/rafter-cli@v1
78
78
  name: Rafter Security
79
79
 
80
80
  on:
@@ -7,7 +7,7 @@ _rafter_completions() {
7
7
  prev="\${COMP_WORDS[COMP_CWORD-1]}"
8
8
 
9
9
  # Top-level commands
10
- commands="run scan get usage agent ci hook mcp policy completion help"
10
+ commands="run scan get usage agent brief ci hook mcp policy completion help"
11
11
 
12
12
  case "\${prev}" in
13
13
  rafter)
@@ -18,6 +18,10 @@ _rafter_completions() {
18
18
  COMPREPLY=( $(compgen -W "scan init audit config exec audit-skill install-hook verify status update-gitleaks baseline --help" -- "\${cur}") )
19
19
  return 0
20
20
  ;;
21
+ brief)
22
+ COMPREPLY=( $(compgen -W "security scanning commands setup setup/claude-code setup/codex setup/gemini setup/cursor setup/windsurf setup/aider setup/openclaw setup/continue setup/generic all --help" -- "\${cur}") )
23
+ return 0
24
+ ;;
21
25
  config)
22
26
  if [[ "\${COMP_WORDS[1]}" == "agent" ]]; then
23
27
  COMPREPLY=( $(compgen -W "show get set --help" -- "\${cur}") )
@@ -82,6 +86,7 @@ _rafter() {
82
86
  'get:Retrieve scan results'
83
87
  'usage:Check API usage quota'
84
88
  'agent:Agent security commands'
89
+ 'brief:Print rafter knowledge for any agent'
85
90
  'ci:CI/CD pipeline setup'
86
91
  'hook:Git hook handlers'
87
92
  'mcp:MCP server'
@@ -125,6 +130,26 @@ _rafter() {
125
130
  ;;
126
131
  args)
127
132
  case "\$words[1]" in
133
+ brief)
134
+ local -a brief_topics
135
+ brief_topics=(
136
+ 'security:Local agent security briefing'
137
+ 'scanning:Remote code analysis briefing'
138
+ 'commands:Full command reference'
139
+ 'setup:Setup guide for all platforms'
140
+ 'setup/claude-code:Claude Code setup'
141
+ 'setup/codex:Codex CLI setup'
142
+ 'setup/gemini:Gemini CLI setup'
143
+ 'setup/cursor:Cursor setup'
144
+ 'setup/windsurf:Windsurf setup'
145
+ 'setup/aider:Aider setup'
146
+ 'setup/openclaw:OpenClaw setup'
147
+ 'setup/continue:Continue.dev setup'
148
+ 'setup/generic:Generic agent setup'
149
+ 'all:Everything'
150
+ )
151
+ _describe 'topic' brief_topics
152
+ ;;
128
153
  agent)
129
154
  _arguments -C \\
130
155
  '1:subcommand:->subcmd' \\
@@ -260,6 +285,7 @@ complete -c rafter -n '__fish_use_subcommand' -a run -d 'Submit a security scan'
260
285
  complete -c rafter -n '__fish_use_subcommand' -a scan -d 'Alias for run'
261
286
  complete -c rafter -n '__fish_use_subcommand' -a get -d 'Retrieve scan results'
262
287
  complete -c rafter -n '__fish_use_subcommand' -a usage -d 'Check API usage quota'
288
+ complete -c rafter -n '__fish_use_subcommand' -a brief -d 'Print rafter knowledge for any agent'
263
289
  complete -c rafter -n '__fish_use_subcommand' -a agent -d 'Agent security commands'
264
290
  complete -c rafter -n '__fish_use_subcommand' -a ci -d 'CI/CD pipeline setup'
265
291
  complete -c rafter -n '__fish_use_subcommand' -a hook -d 'Git hook handlers'
@@ -284,6 +310,9 @@ complete -c rafter -n '__fish_seen_subcommand_from get' -l quiet -d 'Suppress st
284
310
  # usage options
285
311
  complete -c rafter -n '__fish_seen_subcommand_from usage' -s k -l api-key -d 'API key' -r
286
312
 
313
+ # brief topics
314
+ complete -c rafter -n '__fish_seen_subcommand_from brief' -a 'security scanning commands setup setup/claude-code setup/codex setup/gemini setup/cursor setup/windsurf setup/aider setup/openclaw setup/continue setup/generic all' -d 'Topic'
315
+
287
316
  # agent subcommands
288
317
  complete -c rafter -n '__fish_seen_subcommand_from agent; and not __fish_seen_subcommand_from scan init audit config exec audit-skill install-hook verify status update-gitleaks baseline' -a scan -d 'Scan files for secrets'
289
318
  complete -c rafter -n '__fish_seen_subcommand_from agent; and not __fish_seen_subcommand_from scan init audit config exec audit-skill install-hook verify status update-gitleaks baseline' -a init -d 'Initialize agent security'