sapper-ai 0.2.2 → 0.4.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.
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAUA,wBAAsB,MAAM,CAAC,IAAI,GAAE,MAAM,EAA0B,GAAG,OAAO,CAAC,MAAM,CAAC,CA2BpF"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAaA,wBAAsB,MAAM,CAAC,IAAI,GAAE,MAAM,EAA0B,GAAG,OAAO,CAAC,MAAM,CAAC,CAiCpF"}
package/dist/cli.js CHANGED
@@ -33,12 +33,17 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  return result;
34
34
  };
35
35
  })();
36
+ var __importDefault = (this && this.__importDefault) || function (mod) {
37
+ return (mod && mod.__esModule) ? mod : { "default": mod };
38
+ };
36
39
  Object.defineProperty(exports, "__esModule", { value: true });
37
40
  exports.runCli = runCli;
38
41
  const node_fs_1 = require("node:fs");
39
42
  const node_child_process_1 = require("node:child_process");
43
+ const node_os_1 = require("node:os");
40
44
  const node_path_1 = require("node:path");
41
45
  const readline = __importStar(require("node:readline"));
46
+ const select_1 = __importDefault(require("@inquirer/select"));
42
47
  const presets_1 = require("./presets");
43
48
  const scan_1 = require("./scan");
44
49
  async function runCli(argv = process.argv.slice(2)) {
@@ -52,7 +57,12 @@ async function runCli(argv = process.argv.slice(2)) {
52
57
  printUsage();
53
58
  return 1;
54
59
  }
55
- return (0, scan_1.runScan)(parsed);
60
+ const scanOptions = await resolveScanOptions(parsed);
61
+ if (!scanOptions) {
62
+ printUsage();
63
+ return 1;
64
+ }
65
+ return (0, scan_1.runScan)(scanOptions);
56
66
  }
57
67
  if (argv[0] === 'dashboard') {
58
68
  return runDashboard();
@@ -69,8 +79,15 @@ function printUsage() {
69
79
  sapper-ai - AI security guardrails
70
80
 
71
81
  Usage:
72
- sapper-ai scan Scan environment for threats
73
- sapper-ai scan --fix Scan and quarantine blocked files
82
+ sapper-ai scan Interactive scan scope (TTY only)
83
+ sapper-ai scan . Current directory only (no subdirectories)
84
+ sapper-ai scan --deep Current directory + subdirectories
85
+ sapper-ai scan --system AI system paths (~/.claude, ~/.cursor, ...)
86
+ sapper-ai scan ./path Scan a specific file/directory
87
+ sapper-ai scan --fix Quarantine blocked files
88
+ sapper-ai scan --ai Deep scan with AI analysis (requires OPENAI_API_KEY)
89
+ sapper-ai scan --report Generate HTML report and open in browser
90
+ sapper-ai scan --no-save Skip saving scan results to ~/.sapperai/scans/
74
91
  sapper-ai init Interactive setup wizard
75
92
  sapper-ai dashboard Launch web dashboard
76
93
  sapper-ai --help Show this help
@@ -81,17 +98,124 @@ Learn more: https://github.com/sapper-ai/sapperai
81
98
  function parseScanArgs(argv) {
82
99
  const targets = [];
83
100
  let fix = false;
101
+ let deep = false;
102
+ let system = false;
103
+ let ai = false;
104
+ let report = false;
105
+ let noSave = false;
84
106
  for (const arg of argv) {
85
107
  if (arg === '--fix') {
86
108
  fix = true;
87
109
  continue;
88
110
  }
111
+ if (arg === '--deep') {
112
+ deep = true;
113
+ continue;
114
+ }
115
+ if (arg === '--system') {
116
+ system = true;
117
+ continue;
118
+ }
119
+ if (arg === '--ai') {
120
+ ai = true;
121
+ continue;
122
+ }
123
+ if (arg === '--report') {
124
+ report = true;
125
+ continue;
126
+ }
127
+ if (arg === '--no-save') {
128
+ noSave = true;
129
+ continue;
130
+ }
89
131
  if (arg.startsWith('-')) {
90
132
  return null;
91
133
  }
92
134
  targets.push(arg);
93
135
  }
94
- return { targets, fix };
136
+ return { targets, fix, deep, system, ai, report, noSave };
137
+ }
138
+ function displayPath(path) {
139
+ const home = (0, node_os_1.homedir)();
140
+ if (path === home)
141
+ return '~';
142
+ return path.startsWith(home + '/') ? `~/${path.slice(home.length + 1)}` : path;
143
+ }
144
+ async function promptScanScope(cwd) {
145
+ const answer = await (0, select_1.default)({
146
+ message: 'Scan scope:',
147
+ choices: [
148
+ { name: `Current directory only ${displayPath(cwd)}`, value: 'shallow' },
149
+ { name: `Current + subdirectories ${displayPath((0, node_path_1.join)(cwd, '**'))}`, value: 'deep' },
150
+ {
151
+ name: 'AI system scan ~/.claude, ~/.cursor, ~/.vscode ...',
152
+ value: 'system',
153
+ },
154
+ ],
155
+ default: 'deep',
156
+ });
157
+ return answer;
158
+ }
159
+ async function promptScanDepth() {
160
+ const answer = await (0, select_1.default)({
161
+ message: 'Scan depth:',
162
+ choices: [
163
+ { name: 'Quick scan (rules only) Fast regex pattern matching', value: false },
164
+ {
165
+ name: 'Deep scan (rules + AI) AI-powered analysis (requires OPENAI_API_KEY)',
166
+ value: true,
167
+ },
168
+ ],
169
+ default: false,
170
+ });
171
+ return answer;
172
+ }
173
+ async function resolveScanOptions(args) {
174
+ const cwd = process.cwd();
175
+ const common = {
176
+ fix: args.fix,
177
+ report: args.report,
178
+ noSave: args.noSave,
179
+ };
180
+ if (args.system) {
181
+ if (args.targets.length > 0) {
182
+ return null;
183
+ }
184
+ return { ...common, system: true, ai: args.ai, scopeLabel: 'AI system scan' };
185
+ }
186
+ if (args.targets.length > 0) {
187
+ if (args.targets.length === 1 && args.targets[0] === '.' && !args.deep) {
188
+ return {
189
+ ...common,
190
+ targets: [cwd],
191
+ deep: false,
192
+ ai: args.ai,
193
+ scopeLabel: 'Current directory only',
194
+ };
195
+ }
196
+ return {
197
+ ...common,
198
+ targets: args.targets,
199
+ deep: true,
200
+ ai: args.ai,
201
+ scopeLabel: args.targets.length === 1 && args.targets[0] === '.' ? 'Current + subdirectories' : 'Custom path',
202
+ };
203
+ }
204
+ if (args.deep) {
205
+ return { ...common, targets: [cwd], deep: true, ai: args.ai, scopeLabel: 'Current + subdirectories' };
206
+ }
207
+ if (process.stdout.isTTY !== true) {
208
+ return { ...common, targets: [cwd], deep: true, ai: false, scopeLabel: 'Current + subdirectories' };
209
+ }
210
+ const scope = await promptScanScope(cwd);
211
+ const ai = args.ai ? true : await promptScanDepth();
212
+ if (scope === 'system') {
213
+ return { ...common, system: true, ai, scopeLabel: 'AI system scan' };
214
+ }
215
+ if (scope === 'shallow') {
216
+ return { ...common, targets: [cwd], deep: false, ai, scopeLabel: 'Current directory only' };
217
+ }
218
+ return { ...common, targets: [cwd], deep: true, ai, scopeLabel: 'Current + subdirectories' };
95
219
  }
96
220
  async function runDashboard() {
97
221
  const configuredPort = process.env.PORT;
@@ -0,0 +1,3 @@
1
+ import type { ScanResult } from './scan';
2
+ export declare function generateHtmlReport(result: ScanResult): string;
3
+ //# sourceMappingURL=report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AA2nBxC,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAoB7D"}