ship-safe 6.1.1 → 6.2.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 (47) hide show
  1. package/README.md +735 -641
  2. package/cli/agents/api-fuzzer.js +345 -345
  3. package/cli/agents/auth-bypass-agent.js +348 -348
  4. package/cli/agents/base-agent.js +272 -272
  5. package/cli/agents/cicd-scanner.js +236 -201
  6. package/cli/agents/config-auditor.js +521 -521
  7. package/cli/agents/deep-analyzer.js +6 -2
  8. package/cli/agents/git-history-scanner.js +170 -170
  9. package/cli/agents/html-reporter.js +568 -568
  10. package/cli/agents/index.js +84 -84
  11. package/cli/agents/injection-tester.js +500 -500
  12. package/cli/agents/llm-redteam.js +251 -251
  13. package/cli/agents/mobile-scanner.js +231 -231
  14. package/cli/agents/orchestrator.js +322 -322
  15. package/cli/agents/pii-compliance-agent.js +301 -301
  16. package/cli/agents/scoring-engine.js +248 -248
  17. package/cli/agents/supabase-rls-agent.js +154 -154
  18. package/cli/agents/supply-chain-agent.js +650 -507
  19. package/cli/bin/ship-safe.js +452 -426
  20. package/cli/commands/agent.js +608 -608
  21. package/cli/commands/audit.js +986 -980
  22. package/cli/commands/baseline.js +193 -193
  23. package/cli/commands/ci.js +342 -342
  24. package/cli/commands/deps.js +516 -516
  25. package/cli/commands/doctor.js +159 -159
  26. package/cli/commands/fix.js +218 -218
  27. package/cli/commands/hooks.js +268 -0
  28. package/cli/commands/init.js +407 -407
  29. package/cli/commands/mcp.js +304 -304
  30. package/cli/commands/red-team.js +7 -1
  31. package/cli/commands/remediate.js +798 -798
  32. package/cli/commands/rotate.js +571 -571
  33. package/cli/commands/scan.js +569 -569
  34. package/cli/commands/score.js +449 -449
  35. package/cli/commands/watch.js +281 -281
  36. package/cli/hooks/patterns.js +313 -0
  37. package/cli/hooks/post-tool-use.js +140 -0
  38. package/cli/hooks/pre-tool-use.js +186 -0
  39. package/cli/index.js +73 -69
  40. package/cli/providers/llm-provider.js +397 -287
  41. package/cli/utils/autofix-rules.js +74 -74
  42. package/cli/utils/cache-manager.js +311 -311
  43. package/cli/utils/output.js +230 -230
  44. package/cli/utils/patterns.js +1121 -1121
  45. package/cli/utils/pdf-generator.js +94 -94
  46. package/package.json +69 -69
  47. package/configs/supabase/rls-templates.sql +0 -242
@@ -1,426 +1,452 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Ship Safe CLI
5
- * =============
6
- *
7
- * Security toolkit for vibe coders and indie hackers.
8
- *
9
- * USAGE:
10
- * npx ship-safe scan [path] Scan for secrets in your codebase
11
- * npx ship-safe checklist Run the launch-day security checklist
12
- * npx ship-safe init Initialize security configs in your project
13
- * npx ship-safe fix Generate .env.example from found secrets
14
- * npx ship-safe guard Install pre-push git hook
15
- * npx ship-safe --help Show all commands
16
- */
17
-
18
- import { program } from 'commander';
19
- import chalk from 'chalk';
20
- import { readFileSync } from 'fs';
21
- import { fileURLToPath } from 'url';
22
- import { dirname, join } from 'path';
23
- import { scanCommand } from '../commands/scan.js';
24
- import { checklistCommand } from '../commands/checklist.js';
25
- import { initCommand } from '../commands/init.js';
26
- import { fixCommand } from '../commands/fix.js';
27
- import { guardCommand } from '../commands/guard.js';
28
- import { mcpCommand } from '../commands/mcp.js';
29
- import { remediateCommand } from '../commands/remediate.js';
30
- import { rotateCommand } from '../commands/rotate.js';
31
- import { agentCommand } from '../commands/agent.js';
32
- import { depsCommand } from '../commands/deps.js';
33
- import { scoreCommand } from '../commands/score.js';
34
- import { redTeamCommand } from '../commands/red-team.js';
35
- import { watchCommand } from '../commands/watch.js';
36
- import { auditCommand } from '../commands/audit.js';
37
- import { doctorCommand } from '../commands/doctor.js';
38
- import { baselineCommand } from '../commands/baseline.js';
39
- import { ciCommand } from '../commands/ci.js';
40
- import { diffCommand } from '../commands/diff.js';
41
- import { vibeCheckCommand } from '../commands/vibe-check.js';
42
- import { benchmarkCommand } from '../commands/benchmark.js';
43
- import { openclawCommand } from '../commands/openclaw.js';
44
- import { scanSkillCommand } from '../commands/scan-skill.js';
45
- import { abomCommand } from '../commands/abom.js';
46
- import { updateIntelCommand } from '../commands/update-intel.js';
47
- import { ABOMGenerator } from '../agents/abom-generator.js';
48
- import { PolicyEngine } from '../agents/policy-engine.js';
49
- import { SBOMGenerator } from '../agents/sbom-generator.js';
50
-
51
- // =============================================================================
52
- // CLI CONFIGURATION
53
- // =============================================================================
54
-
55
- const DEFAULT_MODEL = 'claude-haiku-4-5-20251001';
56
-
57
- // Read version from package.json
58
- const __filename = fileURLToPath(import.meta.url); // ship-safe-ignore module's own path via import.meta.url, not user input
59
- const __dirname = dirname(__filename);
60
- const packageJson = JSON.parse(readFileSync(join(__dirname, '../../package.json'), 'utf8'));
61
- const VERSION = packageJson.version;
62
-
63
- // Banner shown on help
64
- const banner = `
65
- ${chalk.cyan('███████╗██╗ ██╗██╗██████╗ ███████╗ █████╗ ███████╗███████╗')}
66
- ${chalk.cyan('██╔════╝██║ ██║██║██╔══██╗ ██╔════╝██╔══██╗██╔════╝██╔════╝')}
67
- ${chalk.cyan('███████╗███████║██║██████╔╝ ███████╗███████║█████╗ █████╗ ')}
68
- ${chalk.cyan('╚════██║██╔══██║██║██╔═══╝ ╚════██║██╔══██║██╔══╝ ██╔══╝ ')}
69
- ${chalk.cyan('███████║██║ ██║██║██║ ███████║██║ ██║██║ ███████╗')}
70
- ${chalk.cyan('╚══════╝╚═╝ ╚═╝╚═╝╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚══════╝')}
71
-
72
- ${chalk.gray('Security toolkit for vibe coders. Secure your MVP in 5 minutes.')}
73
- `;
74
-
75
- // =============================================================================
76
- // PROGRAM SETUP
77
- // =============================================================================
78
-
79
- program
80
- .name('ship-safe')
81
- .description('Security toolkit for vibe coders and indie hackers')
82
- .version(VERSION)
83
- .addHelpText('before', banner);
84
-
85
- // -----------------------------------------------------------------------------
86
- // SCAN COMMAND
87
- // -----------------------------------------------------------------------------
88
- program
89
- .command('scan [path]')
90
- .description('Scan your codebase for leaked secrets (API keys, passwords, etc.)')
91
- .option('-v, --verbose', 'Show all files being scanned')
92
- .option('--no-color', 'Disable colored output')
93
- .option('--json', 'Output results as JSON (useful for CI)')
94
- .option('--sarif', 'Output results in SARIF format (for GitHub Code Scanning)')
95
- .option('--include-tests', 'Also scan test files (excluded by default to reduce false positives)')
96
- .option('--no-cache', 'Force full rescan (ignore cached results)')
97
- .action(scanCommand);
98
-
99
- // -----------------------------------------------------------------------------
100
- // CHECKLIST COMMAND
101
- // -----------------------------------------------------------------------------
102
- program
103
- .command('checklist')
104
- .description('Run through the launch-day security checklist interactively')
105
- .option('--no-interactive', 'Print checklist without prompts')
106
- .action(checklistCommand);
107
-
108
- // -----------------------------------------------------------------------------
109
- // INIT COMMAND
110
- // -----------------------------------------------------------------------------
111
- program
112
- .command('init')
113
- .description('Initialize security configs in your project')
114
- .option('-f, --force', 'Overwrite existing files')
115
- .option('--gitignore', 'Only copy .gitignore')
116
- .option('--headers', 'Only copy security headers config')
117
- .option('--agents', 'Only add security rules to AI agent instruction files (CLAUDE.md, .cursor/rules/, .windsurfrules, copilot-instructions.md)')
118
- .option('--openclaw', 'Generate a hardened openclaw.json template')
119
- .action(initCommand);
120
-
121
- // -----------------------------------------------------------------------------
122
- // FIX COMMAND
123
- // -----------------------------------------------------------------------------
124
- program
125
- .command('fix')
126
- .description('Scan for secrets and generate a .env.example with placeholder values')
127
- .option('--dry-run', 'Preview generated .env.example without writing it')
128
- .action(fixCommand);
129
-
130
- // -----------------------------------------------------------------------------
131
- // GUARD COMMAND
132
- // -----------------------------------------------------------------------------
133
- program
134
- .command('guard [action]')
135
- .description('Install a git hook to block pushes if secrets are found')
136
- .option('--pre-commit', 'Install as pre-commit hook instead of pre-push')
137
- .option('--generate-hooks', 'Generate defensive Claude Code hooks (.claude/settings.json)')
138
- .action(guardCommand);
139
-
140
- // -----------------------------------------------------------------------------
141
- // MCP SERVER COMMAND
142
- // -----------------------------------------------------------------------------
143
- program
144
- .command('mcp')
145
- .description('Start ship-safe as an MCP server (for Claude Desktop, Cursor, Windsurf, etc.)')
146
- .action(mcpCommand);
147
-
148
- // -----------------------------------------------------------------------------
149
- // REMEDIATE COMMAND
150
- // -----------------------------------------------------------------------------
151
- program
152
- .command('remediate [path]')
153
- .description('Auto-fix hardcoded secrets: rewrite source code + write .env + update .gitignore')
154
- .option('--dry-run', 'Preview changes without writing any files')
155
- .option('--yes', 'Apply all fixes without prompting (for CI)')
156
- .option('--stage', 'Also run git add on modified files after fixing')
157
- .option('--all', 'Also fix common agent findings (debug mode, TLS bypass, shell injection)')
158
- .action(remediateCommand);
159
-
160
- // -----------------------------------------------------------------------------
161
- // ROTATE COMMAND
162
- // -----------------------------------------------------------------------------
163
- program
164
- .command('rotate [path]')
165
- .description('Revoke and rotate exposed secrets — opens provider dashboards with step-by-step guide')
166
- .option('--provider <name>', 'Only rotate secrets for a specific provider (e.g. github, stripe, openai)')
167
- .action(rotateCommand);
168
-
169
- // -----------------------------------------------------------------------------
170
- // AGENT COMMAND
171
- // -----------------------------------------------------------------------------
172
- program
173
- .command('agent [path]')
174
- .description('AI-powered security audit: scan, classify with Claude, auto-remediate confirmed secrets')
175
- .option('--dry-run', 'Show classification and plan without writing any files')
176
- .option('--model <model>', `Claude model to use (default: ${DEFAULT_MODEL})`)
177
- .action(agentCommand);
178
-
179
- // -----------------------------------------------------------------------------
180
- // DEPS COMMAND
181
- // -----------------------------------------------------------------------------
182
- program
183
- .command('deps [path]')
184
- .description('Audit dependencies for known CVEs (npm, yarn, pnpm, pip-audit, bundler-audit)')
185
- .option('--fix', 'Run the package manager fix command after auditing')
186
- .action(depsCommand);
187
-
188
- // -----------------------------------------------------------------------------
189
- // SCORE COMMAND
190
- // -----------------------------------------------------------------------------
191
- program
192
- .command('score [path]')
193
- .description('Compute a 0-100 security health score for your project')
194
- .option('--no-deps', 'Skip dependency audit')
195
- .action(scoreCommand);
196
-
197
- // -----------------------------------------------------------------------------
198
- // AUDIT COMMAND (v4.0 — Full Security Audit)
199
- // -----------------------------------------------------------------------------
200
- program
201
- .command('audit [path]')
202
- .description('Full security audit: secrets + 18 agents + deps + score + deep analysis + remediation plan')
203
- .option('--json', 'Output results as JSON')
204
- .option('--sarif', 'Output results in SARIF format')
205
- .option('--csv', 'Output results as CSV')
206
- .option('--md', 'Output results as Markdown')
207
- .option('--html [file]', 'HTML report path (default: ship-safe-report.html)')
208
- .option('--compare', 'Show detailed comparison with last scan')
209
- .option('--timeout <ms>', 'Per-agent timeout in milliseconds (default: 30000)', parseInt)
210
- .option('--no-deps', 'Skip dependency audit')
211
- .option('--no-ai', 'Skip AI classification')
212
- .option('--no-cache', 'Force full rescan (ignore cached results)')
213
- .option('--baseline', 'Only show findings not in the baseline')
214
- .option('--pdf [file]', 'Generate PDF report (requires Chrome/Chromium)')
215
- .option('--deep', 'LLM-powered taint analysis for critical/high findings')
216
- .option('--local', 'Use local Ollama model for deep analysis (default: llama3.2)')
217
- .option('--model <model>', 'LLM model to use for deep/AI analysis')
218
- .option('--budget <cents>', 'Max spend in cents for deep analysis (default: 50)', parseInt)
219
- .option('--verify', 'Check if leaked secrets are still active (probes provider APIs)')
220
- .option('-v, --verbose', 'Verbose output')
221
- .action(auditCommand);
222
-
223
- // -----------------------------------------------------------------------------
224
- // DIFF COMMAND (v6.0 — Scan only changed files)
225
- // -----------------------------------------------------------------------------
226
- program
227
- .command('diff [ref]')
228
- .description('Scan only changed files (git diff) — fast pre-commit & PR scanning')
229
- .option('--staged', 'Scan only staged changes')
230
- .option('--json', 'Output results as JSON')
231
- .option('-p, --path <path>', 'Project path (default: cwd)')
232
- .option('--timeout <ms>', 'Per-agent timeout in milliseconds (default: 30000)', parseInt)
233
- .action(diffCommand);
234
-
235
- // -----------------------------------------------------------------------------
236
- // RED TEAM COMMAND (v4.0 — Multi-Agent Security Audit)
237
- // -----------------------------------------------------------------------------
238
- program
239
- .command('red-team [path]')
240
- .description('Multi-agent security audit: 18 agents scan for 80+ attack classes')
241
- .option('--agents <list>', 'Comma-separated list of agents to run')
242
- .option('--json', 'Output results as JSON')
243
- .option('--sarif', 'Output results in SARIF format')
244
- .option('--html [file]', 'Generate HTML security report')
245
- .option('--sbom [file]', 'Generate CycloneDX SBOM')
246
- .option('--no-deps', 'Skip dependency audit')
247
- .option('--no-ai', 'Skip AI classification')
248
- .option('--deep', 'LLM-powered taint analysis for critical/high findings')
249
- .option('--local', 'Use local Ollama model for deep analysis (default: llama3.2)')
250
- .option('--model <model>', 'LLM model for deep analysis')
251
- .option('--budget <cents>', 'Max spend in cents for deep analysis (default: 50)', parseInt)
252
- .option('-v, --verbose', 'Verbose output')
253
- .action(redTeamCommand);
254
-
255
- // -----------------------------------------------------------------------------
256
- // WATCH COMMAND
257
- // -----------------------------------------------------------------------------
258
- program
259
- .command('watch [path]')
260
- .description('Continuous monitoring: watch files for security issues in real-time')
261
- .option('--poll', 'Use polling mode (for network drives)')
262
- .option('--configs', 'Watch only agent config files (openclaw.json, .cursorrules, mcp.json, etc.)')
263
- .action(watchCommand);
264
-
265
- // -----------------------------------------------------------------------------
266
- // SBOM COMMAND
267
- // -----------------------------------------------------------------------------
268
- program
269
- .command('sbom [path]')
270
- .description('Generate Software Bill of Materials (CycloneDX SBOM)')
271
- .option('-o, --output <file>', 'Output file path', 'sbom.json')
272
- .action((targetPath = '.', options) => {
273
- const absolutePath = join(process.cwd(), targetPath);
274
- const sbom = new SBOMGenerator();
275
- sbom.generateToFile(absolutePath, options.output);
276
- console.log(chalk.green(`✔ SBOM saved to ${options.output}`));
277
- });
278
-
279
- // -----------------------------------------------------------------------------
280
- // POLICY COMMAND
281
- // -----------------------------------------------------------------------------
282
- program
283
- .command('policy <action>')
284
- .description('Manage security policies (init: create policy template)')
285
- .action((action) => {
286
- if (action === 'init') {
287
- const policyPath = PolicyEngine.generateTemplate(process.cwd());
288
- console.log(chalk.green(`✔ Policy template created: ${policyPath}`));
289
- console.log(chalk.gray(' Edit .ship-safe.policy.json to configure your security policy.'));
290
- } else {
291
- console.log(chalk.yellow(`Unknown policy action: ${action}. Use: policy init`));
292
- }
293
- });
294
-
295
- // -----------------------------------------------------------------------------
296
- // BASELINE COMMAND (v4.3)
297
- // -----------------------------------------------------------------------------
298
- program
299
- .command('baseline [path]')
300
- .description('Create/manage a findings baseline — only report new findings on subsequent scans')
301
- .option('--diff', 'Show what changed since baseline')
302
- .option('--clear', 'Remove the baseline')
303
- .action(baselineCommand);
304
-
305
- // -----------------------------------------------------------------------------
306
- // CI COMMAND (v5.0 CI/CD Pipeline Integration)
307
- // -----------------------------------------------------------------------------
308
- program
309
- .command('ci [path]')
310
- .description('CI/CD pipeline mode: scan, score, exit 1 on failure — optimized for automation')
311
- .option('--threshold <score>', 'Minimum passing score (default: 75)', parseInt)
312
- .option('--fail-on <severity>', 'Fail on findings at this severity or above (critical, high, medium)')
313
- .option('--sarif <file>', 'Write SARIF output for GitHub Code Scanning')
314
- .option('--json', 'JSON output')
315
- .option('--no-deps', 'Skip dependency audit')
316
- .option('--baseline', 'Only check new findings (not in baseline)')
317
- .option('--github-pr', 'Post findings as a GitHub PR comment (requires gh CLI)')
318
- .action(ciCommand);
319
-
320
- // -----------------------------------------------------------------------------
321
- // VIBE CHECK COMMAND
322
- // -----------------------------------------------------------------------------
323
- program
324
- .command('vibe-check [path]')
325
- .description('Fun security check with emoji output, shareable score, and badge generator')
326
- .option('--badge', 'Generate a shields.io markdown badge for your README')
327
- .action(vibeCheckCommand);
328
-
329
- // -----------------------------------------------------------------------------
330
- // BENCHMARK COMMAND
331
- // -----------------------------------------------------------------------------
332
- program
333
- .command('benchmark [path]')
334
- .description('Compare your security score against industry averages')
335
- .option('--json', 'Output results as JSON')
336
- .action(benchmarkCommand);
337
-
338
- // -----------------------------------------------------------------------------
339
- // OPENCLAW COMMAND
340
- // -----------------------------------------------------------------------------
341
- program
342
- .command('openclaw [path]')
343
- .description('OpenClaw security scan: agent configs, MCP servers, skills, hooks')
344
- .option('--fix', 'Auto-harden OpenClaw and agent configurations')
345
- .option('--preflight', 'Exit non-zero on critical findings (for CI)')
346
- .option('--red-team', 'Simulate adversarial attacks against agent configs')
347
- .option('--json', 'Output results as JSON')
348
- .action(openclawCommand);
349
-
350
- // -----------------------------------------------------------------------------
351
- // SCAN-SKILL COMMAND
352
- // -----------------------------------------------------------------------------
353
- program
354
- .command('scan-skill [target]')
355
- .description('Analyze an AI agent skill for security issues before installing it')
356
- .option('--all', 'Scan all skills defined in openclaw.json')
357
- .option('--json', 'Output results as JSON')
358
- .action(scanSkillCommand);
359
-
360
- // -----------------------------------------------------------------------------
361
- // ABOM COMMAND
362
- // -----------------------------------------------------------------------------
363
- program
364
- .command('abom [path]')
365
- .description('Generate Agent Bill of Materials (CycloneDX ABOM) — MCP servers, skills, configs, LLM providers')
366
- .option('-o, --output <file>', 'Output file path', 'abom.json')
367
- .option('--json', 'Output to stdout as JSON')
368
- .action(abomCommand);
369
-
370
- // -----------------------------------------------------------------------------
371
- // UPDATE-INTEL COMMAND
372
- // -----------------------------------------------------------------------------
373
- program
374
- .command('update-intel')
375
- .description('Update threat intelligence feed (malicious skill hashes, compromised MCP servers)')
376
- .option('--url <url>', 'Custom feed URL')
377
- .action(updateIntelCommand);
378
-
379
- // -----------------------------------------------------------------------------
380
- // DOCTOR COMMAND
381
- // -----------------------------------------------------------------------------
382
- program
383
- .command('doctor')
384
- .description('Diagnose environment: check Node.js, git, API keys, cache, and dependencies')
385
- .action(doctorCommand);
386
-
387
- // -----------------------------------------------------------------------------
388
- // PARSE AND RUN
389
- // -----------------------------------------------------------------------------
390
-
391
- // Show help if no command provided
392
- if (process.argv.length === 2) {
393
- console.log(banner);
394
- console.log(chalk.yellow('\nQuick start:\n'));
395
- console.log(chalk.cyan.bold(' v6.0 — Full Security Audit'));
396
- console.log(chalk.white(' npx ship-safe audit . ') + chalk.gray('# Full audit: secrets + 18 agents + deps + remediation'));
397
- console.log(chalk.white(' npx ship-safe audit . --deep') + chalk.gray('# LLM-powered taint analysis (Anthropic/Ollama)'));
398
- console.log(chalk.white(' npx ship-safe red-team . ') + chalk.gray('# 18-agent red team scan (80+ attack classes)'));
399
- console.log(chalk.white(' npx ship-safe vibe-check . ') + chalk.gray('# Fun security check with emoji & shareable badge'));
400
- console.log(chalk.white(' npx ship-safe benchmark . ') + chalk.gray('# Compare score against industry averages'));
401
- console.log(chalk.white(' npx ship-safe ci . ') + chalk.gray('# CI/CD mode: scan, score, exit code'));
402
- console.log(chalk.white(' npx ship-safe diff ') + chalk.gray('# Scan only changed files (fast pre-commit)'));
403
- console.log(chalk.white(' npx ship-safe watch . ') + chalk.gray('# Continuous monitoring mode'));
404
- console.log(chalk.white(' npx ship-safe openclaw . ') + chalk.gray('# OpenClaw & agent config security scan'));
405
- console.log(chalk.white(' npx ship-safe scan-skill <u>') + chalk.gray('# Vet a skill before installing'));
406
- console.log(chalk.white(' npx ship-safe abom . ') + chalk.gray('# Agent Bill of Materials (CycloneDX)'));
407
- console.log(chalk.white(' npx ship-safe sbom . ') + chalk.gray('# Generate CycloneDX SBOM (CRA-ready)'));
408
- console.log(chalk.white(' npx ship-safe update-intel ') + chalk.gray('# Update threat intelligence feed'));
409
- console.log(chalk.white(' npx ship-safe policy init ') + chalk.gray('# Create security policy template'));
410
- console.log(chalk.white(' npx ship-safe doctor ') + chalk.gray('# Check environment and configuration'));
411
- console.log();
412
- console.log(chalk.gray(' Core commands:'));
413
- console.log(chalk.white(' npx ship-safe agent . ') + chalk.gray('# AI audit: scan + classify + auto-fix'));
414
- console.log(chalk.white(' npx ship-safe scan . ') + chalk.gray('# Scan for secrets'));
415
- console.log(chalk.white(' npx ship-safe remediate . ') + chalk.gray('# Auto-fix: rewrite code + write .env'));
416
- console.log(chalk.white(' npx ship-safe rotate . ') + chalk.gray('# Revoke exposed keys (provider guides)'));
417
- console.log(chalk.white(' npx ship-safe deps . ') + chalk.gray('# Audit dependencies for CVEs'));
418
- console.log(chalk.white(' npx ship-safe score . ') + chalk.gray('# Security health score (0-100)'));
419
- console.log(chalk.white(' npx ship-safe guard ') + chalk.gray('# Block git push if secrets found'));
420
- console.log(chalk.white(' npx ship-safe init ') + chalk.gray('# Add security configs to your project'));
421
- console.log(chalk.white('\n npx ship-safe --help ') + chalk.gray('# Show all options'));
422
- console.log();
423
- process.exit(0);
424
- }
425
-
426
- program.parse();
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Ship Safe CLI
5
+ * =============
6
+ *
7
+ * Security toolkit for vibe coders and indie hackers.
8
+ *
9
+ * USAGE:
10
+ * npx ship-safe scan [path] Scan for secrets in your codebase
11
+ * npx ship-safe checklist Run the launch-day security checklist
12
+ * npx ship-safe init Initialize security configs in your project
13
+ * npx ship-safe fix Generate .env.example from found secrets
14
+ * npx ship-safe guard Install pre-push git hook
15
+ * npx ship-safe --help Show all commands
16
+ */
17
+
18
+ import { program } from 'commander';
19
+ import chalk from 'chalk';
20
+ import { readFileSync } from 'fs';
21
+ import { fileURLToPath } from 'url';
22
+ import { dirname, join } from 'path';
23
+ import { scanCommand } from '../commands/scan.js';
24
+ import { checklistCommand } from '../commands/checklist.js';
25
+ import { initCommand } from '../commands/init.js';
26
+ import { fixCommand } from '../commands/fix.js';
27
+ import { guardCommand } from '../commands/guard.js';
28
+ import { mcpCommand } from '../commands/mcp.js';
29
+ import { remediateCommand } from '../commands/remediate.js';
30
+ import { rotateCommand } from '../commands/rotate.js';
31
+ import { agentCommand } from '../commands/agent.js';
32
+ import { depsCommand } from '../commands/deps.js';
33
+ import { scoreCommand } from '../commands/score.js';
34
+ import { redTeamCommand } from '../commands/red-team.js';
35
+ import { watchCommand } from '../commands/watch.js';
36
+ import { auditCommand } from '../commands/audit.js';
37
+ import { doctorCommand } from '../commands/doctor.js';
38
+ import { baselineCommand } from '../commands/baseline.js';
39
+ import { ciCommand } from '../commands/ci.js';
40
+ import { diffCommand } from '../commands/diff.js';
41
+ import { vibeCheckCommand } from '../commands/vibe-check.js';
42
+ import { benchmarkCommand } from '../commands/benchmark.js';
43
+ import { openclawCommand } from '../commands/openclaw.js';
44
+ import { scanSkillCommand } from '../commands/scan-skill.js';
45
+ import { abomCommand } from '../commands/abom.js';
46
+ import { updateIntelCommand } from '../commands/update-intel.js';
47
+ import { hooksCommand } from '../commands/hooks.js';
48
+ import { ABOMGenerator } from '../agents/abom-generator.js';
49
+ import { PolicyEngine } from '../agents/policy-engine.js';
50
+ import { SBOMGenerator } from '../agents/sbom-generator.js';
51
+
52
+ // =============================================================================
53
+ // CLI CONFIGURATION
54
+ // =============================================================================
55
+
56
+ const DEFAULT_MODEL = 'claude-haiku-4-5-20251001';
57
+
58
+ // Read version from package.json
59
+ const __filename = fileURLToPath(import.meta.url); // ship-safe-ignore — module's own path via import.meta.url, not user input
60
+ const __dirname = dirname(__filename);
61
+ const packageJson = JSON.parse(readFileSync(join(__dirname, '../../package.json'), 'utf8'));
62
+ const VERSION = packageJson.version;
63
+
64
+ // Banner shown on help
65
+ const banner = `
66
+ ${chalk.cyan('███████╗██╗ ██╗██╗██████╗ ███████╗ █████╗ ███████╗███████╗')}
67
+ ${chalk.cyan('██╔════╝██║ ██║██║██╔══██╗ ██╔════╝██╔══██╗██╔════╝██╔════╝')}
68
+ ${chalk.cyan('███████╗███████║██║██████╔╝ ███████╗███████║█████╗ █████╗ ')}
69
+ ${chalk.cyan('╚════██║██╔══██║██║██╔═══╝ ╚════██║██╔══██║██╔══╝ ██╔══╝ ')}
70
+ ${chalk.cyan('███████║██║ ██║██║██║ ███████║██║ ██║██║ ███████╗')}
71
+ ${chalk.cyan('╚══════╝╚═╝ ╚═╝╚═╝╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚══════╝')}
72
+
73
+ ${chalk.gray('Security toolkit for vibe coders. Secure your MVP in 5 minutes.')}
74
+ `;
75
+
76
+ // =============================================================================
77
+ // PROGRAM SETUP
78
+ // =============================================================================
79
+
80
+ program
81
+ .name('ship-safe')
82
+ .description('Security toolkit for vibe coders and indie hackers')
83
+ .version(VERSION)
84
+ .addHelpText('before', banner);
85
+
86
+ // -----------------------------------------------------------------------------
87
+ // SCAN COMMAND
88
+ // -----------------------------------------------------------------------------
89
+ program
90
+ .command('scan [path]')
91
+ .description('Scan your codebase for leaked secrets (API keys, passwords, etc.)')
92
+ .option('-v, --verbose', 'Show all files being scanned')
93
+ .option('--no-color', 'Disable colored output')
94
+ .option('--json', 'Output results as JSON (useful for CI)')
95
+ .option('--sarif', 'Output results in SARIF format (for GitHub Code Scanning)')
96
+ .option('--include-tests', 'Also scan test files (excluded by default to reduce false positives)')
97
+ .option('--no-cache', 'Force full rescan (ignore cached results)')
98
+ .action(scanCommand);
99
+
100
+ // -----------------------------------------------------------------------------
101
+ // CHECKLIST COMMAND
102
+ // -----------------------------------------------------------------------------
103
+ program
104
+ .command('checklist')
105
+ .description('Run through the launch-day security checklist interactively')
106
+ .option('--no-interactive', 'Print checklist without prompts')
107
+ .action(checklistCommand);
108
+
109
+ // -----------------------------------------------------------------------------
110
+ // INIT COMMAND
111
+ // -----------------------------------------------------------------------------
112
+ program
113
+ .command('init')
114
+ .description('Initialize security configs in your project')
115
+ .option('-f, --force', 'Overwrite existing files')
116
+ .option('--gitignore', 'Only copy .gitignore')
117
+ .option('--headers', 'Only copy security headers config')
118
+ .option('--agents', 'Only add security rules to AI agent instruction files (CLAUDE.md, .cursor/rules/, .windsurfrules, copilot-instructions.md)')
119
+ .option('--openclaw', 'Generate a hardened openclaw.json template')
120
+ .action(initCommand);
121
+
122
+ // -----------------------------------------------------------------------------
123
+ // FIX COMMAND
124
+ // -----------------------------------------------------------------------------
125
+ program
126
+ .command('fix')
127
+ .description('Scan for secrets and generate a .env.example with placeholder values')
128
+ .option('--dry-run', 'Preview generated .env.example without writing it')
129
+ .action(fixCommand);
130
+
131
+ // -----------------------------------------------------------------------------
132
+ // GUARD COMMAND
133
+ // -----------------------------------------------------------------------------
134
+ program
135
+ .command('guard [action]')
136
+ .description('Install a git hook to block pushes if secrets are found')
137
+ .option('--pre-commit', 'Install as pre-commit hook instead of pre-push')
138
+ .option('--generate-hooks', 'Generate defensive Claude Code hooks (.claude/settings.json)')
139
+ .action(guardCommand);
140
+
141
+ // -----------------------------------------------------------------------------
142
+ // MCP SERVER COMMAND
143
+ // -----------------------------------------------------------------------------
144
+ program
145
+ .command('mcp')
146
+ .description('Start ship-safe as an MCP server (for Claude Desktop, Cursor, Windsurf, etc.)')
147
+ .action(mcpCommand);
148
+
149
+ // -----------------------------------------------------------------------------
150
+ // REMEDIATE COMMAND
151
+ // -----------------------------------------------------------------------------
152
+ program
153
+ .command('remediate [path]')
154
+ .description('Auto-fix hardcoded secrets: rewrite source code + write .env + update .gitignore')
155
+ .option('--dry-run', 'Preview changes without writing any files')
156
+ .option('--yes', 'Apply all fixes without prompting (for CI)')
157
+ .option('--stage', 'Also run git add on modified files after fixing')
158
+ .option('--all', 'Also fix common agent findings (debug mode, TLS bypass, shell injection)')
159
+ .action(remediateCommand);
160
+
161
+ // -----------------------------------------------------------------------------
162
+ // ROTATE COMMAND
163
+ // -----------------------------------------------------------------------------
164
+ program
165
+ .command('rotate [path]')
166
+ .description('Revoke and rotate exposed secrets opens provider dashboards with step-by-step guide')
167
+ .option('--provider <name>', 'Only rotate secrets for a specific provider (e.g. github, stripe, openai)')
168
+ .action(rotateCommand);
169
+
170
+ // -----------------------------------------------------------------------------
171
+ // AGENT COMMAND
172
+ // -----------------------------------------------------------------------------
173
+ program
174
+ .command('agent [path]')
175
+ .description('AI-powered security audit: scan, classify with Claude, auto-remediate confirmed secrets')
176
+ .option('--dry-run', 'Show classification and plan without writing any files')
177
+ .option('--model <model>', `Claude model to use (default: ${DEFAULT_MODEL})`)
178
+ .action(agentCommand);
179
+
180
+ // -----------------------------------------------------------------------------
181
+ // DEPS COMMAND
182
+ // -----------------------------------------------------------------------------
183
+ program
184
+ .command('deps [path]')
185
+ .description('Audit dependencies for known CVEs (npm, yarn, pnpm, pip-audit, bundler-audit)')
186
+ .option('--fix', 'Run the package manager fix command after auditing')
187
+ .action(depsCommand);
188
+
189
+ // -----------------------------------------------------------------------------
190
+ // SCORE COMMAND
191
+ // -----------------------------------------------------------------------------
192
+ program
193
+ .command('score [path]')
194
+ .description('Compute a 0-100 security health score for your project')
195
+ .option('--no-deps', 'Skip dependency audit')
196
+ .action(scoreCommand);
197
+
198
+ // -----------------------------------------------------------------------------
199
+ // AUDIT COMMAND (v4.0 — Full Security Audit)
200
+ // -----------------------------------------------------------------------------
201
+ program
202
+ .command('audit [path]')
203
+ .description('Full security audit: secrets + 18 agents + deps + score + deep analysis + remediation plan')
204
+ .option('--json', 'Output results as JSON')
205
+ .option('--sarif', 'Output results in SARIF format')
206
+ .option('--csv', 'Output results as CSV')
207
+ .option('--md', 'Output results as Markdown')
208
+ .option('--html [file]', 'HTML report path (default: ship-safe-report.html)')
209
+ .option('--compare', 'Show detailed comparison with last scan')
210
+ .option('--timeout <ms>', 'Per-agent timeout in milliseconds (default: 30000)', parseInt)
211
+ .option('--no-deps', 'Skip dependency audit')
212
+ .option('--no-ai', 'Skip AI classification')
213
+ .option('--no-cache', 'Force full rescan (ignore cached results)')
214
+ .option('--baseline', 'Only show findings not in the baseline')
215
+ .option('--pdf [file]', 'Generate PDF report (requires Chrome/Chromium)')
216
+ .option('--deep', 'LLM-powered taint analysis for critical/high findings')
217
+ .option('--local', 'Use local Ollama model for deep analysis (default: llama3.2)')
218
+ .option('--model <model>', 'LLM model to use for deep/AI analysis')
219
+ .option('--provider <name>', 'LLM provider: anthropic, openai, google, ollama, groq, together, mistral, cohere, deepseek, xai, lmstudio')
220
+ .option('--base-url <url>', 'Custom OpenAI-compatible endpoint (e.g. http://localhost:1234/v1/chat/completions)')
221
+ .option('--budget <cents>', 'Max spend in cents for deep analysis (default: 50)', parseInt)
222
+ .option('--verify', 'Check if leaked secrets are still active (probes provider APIs)')
223
+ .option('-v, --verbose', 'Verbose output')
224
+ .action(auditCommand);
225
+
226
+ // -----------------------------------------------------------------------------
227
+ // DIFF COMMAND (v6.0 — Scan only changed files)
228
+ // -----------------------------------------------------------------------------
229
+ program
230
+ .command('diff [ref]')
231
+ .description('Scan only changed files (git diff) — fast pre-commit & PR scanning')
232
+ .option('--staged', 'Scan only staged changes')
233
+ .option('--json', 'Output results as JSON')
234
+ .option('-p, --path <path>', 'Project path (default: cwd)')
235
+ .option('--timeout <ms>', 'Per-agent timeout in milliseconds (default: 30000)', parseInt)
236
+ .action(diffCommand);
237
+
238
+ // -----------------------------------------------------------------------------
239
+ // RED TEAM COMMAND (v4.0 — Multi-Agent Security Audit)
240
+ // -----------------------------------------------------------------------------
241
+ program
242
+ .command('red-team [path]')
243
+ .description('Multi-agent security audit: 18 agents scan for 80+ attack classes')
244
+ .option('--agents <list>', 'Comma-separated list of agents to run')
245
+ .option('--json', 'Output results as JSON')
246
+ .option('--sarif', 'Output results in SARIF format')
247
+ .option('--html [file]', 'Generate HTML security report')
248
+ .option('--sbom [file]', 'Generate CycloneDX SBOM')
249
+ .option('--no-deps', 'Skip dependency audit')
250
+ .option('--no-ai', 'Skip AI classification')
251
+ .option('--deep', 'LLM-powered taint analysis for critical/high findings')
252
+ .option('--local', 'Use local Ollama model for deep analysis (default: llama3.2)')
253
+ .option('--model <model>', 'LLM model for deep analysis')
254
+ .option('--provider <name>', 'LLM provider: anthropic, openai, google, ollama, groq, together, mistral, cohere, deepseek, xai, lmstudio')
255
+ .option('--base-url <url>', 'Custom OpenAI-compatible endpoint (e.g. http://localhost:1234/v1/chat/completions)')
256
+ .option('--budget <cents>', 'Max spend in cents for deep analysis (default: 50)', parseInt)
257
+ .option('-v, --verbose', 'Verbose output')
258
+ .action(redTeamCommand);
259
+
260
+ // -----------------------------------------------------------------------------
261
+ // WATCH COMMAND
262
+ // -----------------------------------------------------------------------------
263
+ program
264
+ .command('watch [path]')
265
+ .description('Continuous monitoring: watch files for security issues in real-time')
266
+ .option('--poll', 'Use polling mode (for network drives)')
267
+ .option('--configs', 'Watch only agent config files (openclaw.json, .cursorrules, mcp.json, etc.)')
268
+ .action(watchCommand);
269
+
270
+ // -----------------------------------------------------------------------------
271
+ // SBOM COMMAND
272
+ // -----------------------------------------------------------------------------
273
+ program
274
+ .command('sbom [path]')
275
+ .description('Generate Software Bill of Materials (CycloneDX SBOM)')
276
+ .option('-o, --output <file>', 'Output file path', 'sbom.json')
277
+ .action((targetPath = '.', options) => {
278
+ const absolutePath = join(process.cwd(), targetPath);
279
+ const sbom = new SBOMGenerator();
280
+ sbom.generateToFile(absolutePath, options.output);
281
+ console.log(chalk.green(`✔ SBOM saved to ${options.output}`));
282
+ });
283
+
284
+ // -----------------------------------------------------------------------------
285
+ // POLICY COMMAND
286
+ // -----------------------------------------------------------------------------
287
+ program
288
+ .command('policy <action>')
289
+ .description('Manage security policies (init: create policy template)')
290
+ .action((action) => {
291
+ if (action === 'init') {
292
+ const policyPath = PolicyEngine.generateTemplate(process.cwd());
293
+ console.log(chalk.green(`✔ Policy template created: ${policyPath}`));
294
+ console.log(chalk.gray(' Edit .ship-safe.policy.json to configure your security policy.'));
295
+ } else {
296
+ console.log(chalk.yellow(`Unknown policy action: ${action}. Use: policy init`));
297
+ }
298
+ });
299
+
300
+ // -----------------------------------------------------------------------------
301
+ // BASELINE COMMAND (v4.3)
302
+ // -----------------------------------------------------------------------------
303
+ program
304
+ .command('baseline [path]')
305
+ .description('Create/manage a findings baseline — only report new findings on subsequent scans')
306
+ .option('--diff', 'Show what changed since baseline')
307
+ .option('--clear', 'Remove the baseline')
308
+ .action(baselineCommand);
309
+
310
+ // -----------------------------------------------------------------------------
311
+ // CI COMMAND (v5.0 CI/CD Pipeline Integration)
312
+ // -----------------------------------------------------------------------------
313
+ program
314
+ .command('ci [path]')
315
+ .description('CI/CD pipeline mode: scan, score, exit 1 on failure — optimized for automation')
316
+ .option('--threshold <score>', 'Minimum passing score (default: 75)', parseInt)
317
+ .option('--fail-on <severity>', 'Fail on findings at this severity or above (critical, high, medium)')
318
+ .option('--sarif <file>', 'Write SARIF output for GitHub Code Scanning')
319
+ .option('--json', 'JSON output')
320
+ .option('--no-deps', 'Skip dependency audit')
321
+ .option('--baseline', 'Only check new findings (not in baseline)')
322
+ .option('--github-pr', 'Post findings as a GitHub PR comment (requires gh CLI)')
323
+ .action(ciCommand);
324
+
325
+ // -----------------------------------------------------------------------------
326
+ // VIBE CHECK COMMAND
327
+ // -----------------------------------------------------------------------------
328
+ program
329
+ .command('vibe-check [path]')
330
+ .description('Fun security check with emoji output, shareable score, and badge generator')
331
+ .option('--badge', 'Generate a shields.io markdown badge for your README')
332
+ .action(vibeCheckCommand);
333
+
334
+ // -----------------------------------------------------------------------------
335
+ // BENCHMARK COMMAND
336
+ // -----------------------------------------------------------------------------
337
+ program
338
+ .command('benchmark [path]')
339
+ .description('Compare your security score against industry averages')
340
+ .option('--json', 'Output results as JSON')
341
+ .action(benchmarkCommand);
342
+
343
+ // -----------------------------------------------------------------------------
344
+ // OPENCLAW COMMAND
345
+ // -----------------------------------------------------------------------------
346
+ program
347
+ .command('openclaw [path]')
348
+ .description('OpenClaw security scan: agent configs, MCP servers, skills, hooks')
349
+ .option('--fix', 'Auto-harden OpenClaw and agent configurations')
350
+ .option('--preflight', 'Exit non-zero on critical findings (for CI)')
351
+ .option('--red-team', 'Simulate adversarial attacks against agent configs')
352
+ .option('--json', 'Output results as JSON')
353
+ .action(openclawCommand);
354
+
355
+ // -----------------------------------------------------------------------------
356
+ // SCAN-SKILL COMMAND
357
+ // -----------------------------------------------------------------------------
358
+ program
359
+ .command('scan-skill [target]')
360
+ .description('Analyze an AI agent skill for security issues before installing it')
361
+ .option('--all', 'Scan all skills defined in openclaw.json')
362
+ .option('--json', 'Output results as JSON')
363
+ .action(scanSkillCommand);
364
+
365
+ // -----------------------------------------------------------------------------
366
+ // ABOM COMMAND
367
+ // -----------------------------------------------------------------------------
368
+ program
369
+ .command('abom [path]')
370
+ .description('Generate Agent Bill of Materials (CycloneDX ABOM) — MCP servers, skills, configs, LLM providers')
371
+ .option('-o, --output <file>', 'Output file path', 'abom.json')
372
+ .option('--json', 'Output to stdout as JSON')
373
+ .action(abomCommand);
374
+
375
+ // -----------------------------------------------------------------------------
376
+ // HOOKS COMMAND (Claude Code PreToolUse / PostToolUse integration)
377
+ // -----------------------------------------------------------------------------
378
+ program
379
+ .command('hooks [action]')
380
+ .description('Manage Claude Code hooks — real-time security gate on every Write, Edit, and Bash call')
381
+ .addHelpText('after', `
382
+ Actions:
383
+ install Register ship-safe as PreToolUse + PostToolUse hooks in ~/.claude/settings.json
384
+ remove Unregister ship-safe hooks
385
+ status Show whether hooks are installed
386
+
387
+ How it works:
388
+ PreToolUse — blocks Write/Edit if critical secrets are in the new content;
389
+ blocks dangerous Bash patterns (curl|bash, credential exfiltration)
390
+ PostToolUse — scans the file after it is written; injects advisory findings
391
+ into Claude's context so issues are caught immediately
392
+ `)
393
+ .action(hooksCommand);
394
+
395
+ // -----------------------------------------------------------------------------
396
+ // UPDATE-INTEL COMMAND
397
+ // -----------------------------------------------------------------------------
398
+ program
399
+ .command('update-intel')
400
+ .description('Update threat intelligence feed (malicious skill hashes, compromised MCP servers)')
401
+ .option('--url <url>', 'Custom feed URL')
402
+ .action(updateIntelCommand);
403
+
404
+ // -----------------------------------------------------------------------------
405
+ // DOCTOR COMMAND
406
+ // -----------------------------------------------------------------------------
407
+ program
408
+ .command('doctor')
409
+ .description('Diagnose environment: check Node.js, git, API keys, cache, and dependencies')
410
+ .action(doctorCommand);
411
+
412
+ // -----------------------------------------------------------------------------
413
+ // PARSE AND RUN
414
+ // -----------------------------------------------------------------------------
415
+
416
+ // Show help if no command provided
417
+ if (process.argv.length === 2) {
418
+ console.log(banner);
419
+ console.log(chalk.yellow('\nQuick start:\n'));
420
+ console.log(chalk.cyan.bold(' v6.0 Full Security Audit'));
421
+ console.log(chalk.white(' npx ship-safe audit . ') + chalk.gray('# Full audit: secrets + 18 agents + deps + remediation'));
422
+ console.log(chalk.white(' npx ship-safe audit . --deep') + chalk.gray('# LLM-powered taint analysis (Anthropic/Ollama)'));
423
+ console.log(chalk.white(' npx ship-safe red-team . ') + chalk.gray('# 18-agent red team scan (80+ attack classes)'));
424
+ console.log(chalk.white(' npx ship-safe vibe-check . ') + chalk.gray('# Fun security check with emoji & shareable badge'));
425
+ console.log(chalk.white(' npx ship-safe benchmark . ') + chalk.gray('# Compare score against industry averages'));
426
+ console.log(chalk.white(' npx ship-safe ci . ') + chalk.gray('# CI/CD mode: scan, score, exit code'));
427
+ console.log(chalk.white(' npx ship-safe diff ') + chalk.gray('# Scan only changed files (fast pre-commit)'));
428
+ console.log(chalk.white(' npx ship-safe watch . ') + chalk.gray('# Continuous monitoring mode'));
429
+ console.log(chalk.white(' npx ship-safe openclaw . ') + chalk.gray('# OpenClaw & agent config security scan'));
430
+ console.log(chalk.white(' npx ship-safe scan-skill <u>') + chalk.gray('# Vet a skill before installing'));
431
+ console.log(chalk.white(' npx ship-safe abom . ') + chalk.gray('# Agent Bill of Materials (CycloneDX)'));
432
+ console.log(chalk.white(' npx ship-safe sbom . ') + chalk.gray('# Generate CycloneDX SBOM (CRA-ready)'));
433
+ console.log(chalk.white(' npx ship-safe update-intel ') + chalk.gray('# Update threat intelligence feed'));
434
+ console.log(chalk.white(' npx ship-safe policy init ') + chalk.gray('# Create security policy template'));
435
+ console.log(chalk.white(' npx ship-safe doctor ') + chalk.gray('# Check environment and configuration'));
436
+ console.log();
437
+ console.log(chalk.gray(' Core commands:'));
438
+ console.log(chalk.white(' npx ship-safe agent . ') + chalk.gray('# AI audit: scan + classify + auto-fix'));
439
+ console.log(chalk.white(' npx ship-safe scan . ') + chalk.gray('# Scan for secrets'));
440
+ console.log(chalk.white(' npx ship-safe remediate . ') + chalk.gray('# Auto-fix: rewrite code + write .env'));
441
+ console.log(chalk.white(' npx ship-safe rotate . ') + chalk.gray('# Revoke exposed keys (provider guides)'));
442
+ console.log(chalk.white(' npx ship-safe deps . ') + chalk.gray('# Audit dependencies for CVEs'));
443
+ console.log(chalk.white(' npx ship-safe score . ') + chalk.gray('# Security health score (0-100)'));
444
+ console.log(chalk.white(' npx ship-safe hooks install ') + chalk.gray('# Real-time security gate inside Claude Code (PreToolUse/PostToolUse)'));
445
+ console.log(chalk.white(' npx ship-safe guard ') + chalk.gray('# Block git push if secrets found'));
446
+ console.log(chalk.white(' npx ship-safe init ') + chalk.gray('# Add security configs to your project'));
447
+ console.log(chalk.white('\n npx ship-safe --help ') + chalk.gray('# Show all options'));
448
+ console.log();
449
+ process.exit(0);
450
+ }
451
+
452
+ program.parse();