ferret-scan 1.0.6 → 1.0.7

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.
@@ -19,6 +19,6 @@ interface DiscoveryResult {
19
19
  /**
20
20
  * Main file discovery function
21
21
  */
22
- export declare function discoverFiles(paths: string[], options: DiscoveryOptions): DiscoveryResult;
22
+ export declare function discoverFiles(paths: string[], options: DiscoveryOptions): Promise<DiscoveryResult>;
23
23
  export default discoverFiles;
24
24
  //# sourceMappingURL=FileDiscovery.d.ts.map
@@ -3,7 +3,8 @@
3
3
  * Scans directories for skills, agents, hooks, MCP configs, rules files, and other AI CLI files
4
4
  * Supports: Claude Code, Cursor, Windsurf, Continue, Aider, Cline, and generic AI configs
5
5
  */
6
- import { readdirSync, statSync, existsSync } from 'node:fs';
6
+ import { readdir, stat, access } from 'node:fs/promises';
7
+ import { constants } from 'node:fs';
7
8
  import { resolve, extname, basename, relative } from 'node:path';
8
9
  import { createIgnoreFilter, shouldIgnore } from '../utils/ignore.js';
9
10
  import logger from '../utils/logger.js';
@@ -136,10 +137,10 @@ function isAnalyzableFile(filePath) {
136
137
  /**
137
138
  * Recursively discover files in a directory
138
139
  */
139
- function discoverFilesInDirectory(dir, baseDir, ig, options, result) {
140
+ async function discoverFilesInDirectory(dir, baseDir, ig, options, result) {
140
141
  let entries;
141
142
  try {
142
- entries = readdirSync(dir);
143
+ entries = await readdir(dir);
143
144
  }
144
145
  catch (error) {
145
146
  const message = error instanceof Error ? error.message : String(error);
@@ -158,7 +159,7 @@ function discoverFilesInDirectory(dir, baseDir, ig, options, result) {
158
159
  }
159
160
  let stats;
160
161
  try {
161
- stats = statSync(fullPath);
162
+ stats = await stat(fullPath);
162
163
  }
163
164
  catch (error) {
164
165
  const message = error instanceof Error ? error.message : String(error);
@@ -167,7 +168,7 @@ function discoverFilesInDirectory(dir, baseDir, ig, options, result) {
167
168
  }
168
169
  if (stats.isDirectory()) {
169
170
  // Recurse into directory
170
- discoverFilesInDirectory(fullPath, baseDir, ig, options, result);
171
+ await discoverFilesInDirectory(fullPath, baseDir, ig, options, result);
171
172
  }
172
173
  else if (stats.isFile()) {
173
174
  // Check if file should be analyzed
@@ -199,17 +200,29 @@ function discoverFilesInDirectory(dir, baseDir, ig, options, result) {
199
200
  }
200
201
  }
201
202
  }
203
+ /**
204
+ * Check if path exists
205
+ */
206
+ async function pathExists(filePath) {
207
+ try {
208
+ await access(filePath, constants.F_OK);
209
+ return true;
210
+ }
211
+ catch {
212
+ return false;
213
+ }
214
+ }
202
215
  /**
203
216
  * Discover a single file
204
217
  */
205
- function discoverSingleFile(filePath, options, result) {
206
- if (!existsSync(filePath)) {
218
+ async function discoverSingleFile(filePath, options, result) {
219
+ if (!(await pathExists(filePath))) {
207
220
  result.errors.push({ path: filePath, error: 'File does not exist' });
208
221
  return;
209
222
  }
210
223
  let stats;
211
224
  try {
212
- stats = statSync(filePath);
225
+ stats = await stat(filePath);
213
226
  }
214
227
  catch (error) {
215
228
  const message = error instanceof Error ? error.message : String(error);
@@ -242,7 +255,7 @@ function discoverSingleFile(filePath, options, result) {
242
255
  /**
243
256
  * Main file discovery function
244
257
  */
245
- export function discoverFiles(paths, options) {
258
+ export async function discoverFiles(paths, options) {
246
259
  const result = {
247
260
  files: [],
248
261
  skipped: 0,
@@ -254,18 +267,18 @@ export function discoverFiles(paths, options) {
254
267
  }
255
268
  for (const inputPath of paths) {
256
269
  const resolvedPath = resolve(inputPath);
257
- if (!existsSync(resolvedPath)) {
270
+ if (!(await pathExists(resolvedPath))) {
258
271
  logger.warn(`Path does not exist: ${resolvedPath}`);
259
272
  result.errors.push({ path: resolvedPath, error: 'Path does not exist' });
260
273
  continue;
261
274
  }
262
- const stats = statSync(resolvedPath);
275
+ const stats = await stat(resolvedPath);
263
276
  if (stats.isDirectory()) {
264
277
  const ig = createIgnoreFilter(resolvedPath, options.ignore);
265
- discoverFilesInDirectory(resolvedPath, resolvedPath, ig, options, result);
278
+ await discoverFilesInDirectory(resolvedPath, resolvedPath, ig, options, result);
266
279
  }
267
280
  else if (stats.isFile()) {
268
- discoverSingleFile(resolvedPath, options, result);
281
+ await discoverSingleFile(resolvedPath, options, result);
269
282
  }
270
283
  }
271
284
  // Sort files by component type, then by path
@@ -188,7 +188,7 @@ export async function scan(config) {
188
188
  if (showProgress) {
189
189
  spinner = ora('Discovering files...').start();
190
190
  }
191
- const discovery = discoverFiles(config.paths, {
191
+ const discovery = await discoverFiles(config.paths, {
192
192
  maxFileSize: config.maxFileSize,
193
193
  ignore: config.ignore,
194
194
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ferret-scan",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "Security scanner for AI CLI configurations - detect prompt injections, credential leaks, and malicious patterns in AI agent configs",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",