filex-cli 0.1.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 (42) hide show
  1. package/README.md +72 -0
  2. package/dist/bin/filex.d.ts +3 -0
  3. package/dist/bin/filex.d.ts.map +1 -0
  4. package/dist/bin/filex.js +5 -0
  5. package/dist/bin/filex.js.map +1 -0
  6. package/dist/src/actions/convertImages.d.ts +16 -0
  7. package/dist/src/actions/convertImages.d.ts.map +1 -0
  8. package/dist/src/actions/convertImages.js +101 -0
  9. package/dist/src/actions/convertImages.js.map +1 -0
  10. package/dist/src/actions/mergePdfs.d.ts +12 -0
  11. package/dist/src/actions/mergePdfs.d.ts.map +1 -0
  12. package/dist/src/actions/mergePdfs.js +132 -0
  13. package/dist/src/actions/mergePdfs.js.map +1 -0
  14. package/dist/src/actions/resizeImages.d.ts +11 -0
  15. package/dist/src/actions/resizeImages.d.ts.map +1 -0
  16. package/dist/src/actions/resizeImages.js +71 -0
  17. package/dist/src/actions/resizeImages.js.map +1 -0
  18. package/dist/src/ai/explain.d.ts +15 -0
  19. package/dist/src/ai/explain.d.ts.map +1 -0
  20. package/dist/src/ai/explain.js +75 -0
  21. package/dist/src/ai/explain.js.map +1 -0
  22. package/dist/src/ai/parseIntent.d.ts +18 -0
  23. package/dist/src/ai/parseIntent.d.ts.map +1 -0
  24. package/dist/src/ai/parseIntent.js +145 -0
  25. package/dist/src/ai/parseIntent.js.map +1 -0
  26. package/dist/src/cli.d.ts +2 -0
  27. package/dist/src/cli.d.ts.map +1 -0
  28. package/dist/src/cli.js +149 -0
  29. package/dist/src/cli.js.map +1 -0
  30. package/dist/src/confirm.d.ts +17 -0
  31. package/dist/src/confirm.d.ts.map +1 -0
  32. package/dist/src/confirm.js +69 -0
  33. package/dist/src/confirm.js.map +1 -0
  34. package/dist/src/scan.d.ts +27 -0
  35. package/dist/src/scan.d.ts.map +1 -0
  36. package/dist/src/scan.js +79 -0
  37. package/dist/src/scan.js.map +1 -0
  38. package/dist/src/schema.d.ts +224 -0
  39. package/dist/src/schema.d.ts.map +1 -0
  40. package/dist/src/schema.js +52 -0
  41. package/dist/src/schema.js.map +1 -0
  42. package/package.json +46 -0
@@ -0,0 +1,145 @@
1
+ import chalk from 'chalk';
2
+ import { safeParseAction } from '../schema.js';
3
+ const GEMINI_API_URL = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-3-flash-preview:generateContent';
4
+ const SYSTEM_PROMPT = `You are a file operations assistant. Parse the user's natural language request into a structured JSON action.
5
+
6
+ You MUST respond with ONLY valid JSON matching one of these exact schemas:
7
+
8
+ 1. Convert images:
9
+ {"action": "convert_images", "options": {"from": "<format>", "to": "<format>", "maxWidth": <number>, "quality": <number>}}
10
+ - from: source format to convert (png, jpg, jpeg, webp, gif, tiff, avif) - only if user specifies
11
+ - to: target format (webp, png, jpg) - default "webp" if not specified
12
+
13
+ 2. Resize images:
14
+ {"action": "resize_images", "options": {"maxWidth": <number>}}
15
+
16
+ 3. Merge PDFs:
17
+ {"action": "merge_pdfs", "options": {"outputFile": "<string>", "order": "<order>"}}
18
+ - order can be: "name" (A-Z), "name-desc" (Z-A), "date" (oldest first), "date-desc" (newest first), "random"
19
+
20
+ Rules:
21
+ - Only include options that are explicitly mentioned
22
+ - maxWidth and quality must be positive numbers
23
+ - For convert: if user says "convert webp to jpg" → from: "webp", to: "jpg"
24
+ - For convert: if user says "convert to png" → to: "png" (no from needed)
25
+ - For merge PDFs: if user mentions "by date", "modified", "newest", "oldest" → use appropriate order
26
+ - Do NOT add any text, explanation, or markdown - ONLY the JSON object
27
+ - If user requests delete, remove, watch, copy, move, or any unsupported action → respond with:
28
+ {"error": "unsupported"}
29
+ - If the request is unclear or doesn't match any action, respond with: {"error": "unclear"}`;
30
+ /**
31
+ * Parse natural language input into a structured Action using Gemini AI.
32
+ * Returns a validated Action or an error message.
33
+ */
34
+ export async function parseIntent(input) {
35
+ const apiKey = process.env.GEMINI_API_KEY;
36
+ if (!apiKey) {
37
+ return {
38
+ success: false,
39
+ error: 'GEMINI_API_KEY environment variable is not set. Use manual commands instead.',
40
+ };
41
+ }
42
+ try {
43
+ const response = await fetch(`${GEMINI_API_URL}?key=${apiKey}`, {
44
+ method: 'POST',
45
+ headers: {
46
+ 'Content-Type': 'application/json',
47
+ },
48
+ body: JSON.stringify({
49
+ contents: [
50
+ {
51
+ role: 'user',
52
+ parts: [{ text: `${SYSTEM_PROMPT}\n\nUser request: ${input}` }],
53
+ },
54
+ ],
55
+ generationConfig: {
56
+ temperature: 0.1,
57
+ maxOutputTokens: 256,
58
+ },
59
+ }),
60
+ });
61
+ if (!response.ok) {
62
+ const errorText = await response.text();
63
+ return {
64
+ success: false,
65
+ error: `AI API error: ${response.status} - ${errorText}`,
66
+ };
67
+ }
68
+ const data = await response.json();
69
+ const text = data.candidates?.[0]?.content?.parts?.[0]?.text?.trim();
70
+ if (!text) {
71
+ return {
72
+ success: false,
73
+ error: 'AI returned empty response',
74
+ };
75
+ }
76
+ // Try to parse JSON from the response
77
+ let parsed;
78
+ try {
79
+ // Handle potential markdown code blocks
80
+ const jsonMatch = text.match(/```(?:json)?\s*([\s\S]*?)```/) || [null, text];
81
+ const jsonStr = jsonMatch[1]?.trim() || text;
82
+ parsed = JSON.parse(jsonStr);
83
+ }
84
+ catch {
85
+ return {
86
+ success: false,
87
+ error: 'AI returned invalid JSON. Please use a manual command.',
88
+ };
89
+ }
90
+ // Check for error response from AI
91
+ if (typeof parsed === 'object' && parsed !== null && 'error' in parsed) {
92
+ const errorObj = parsed;
93
+ if (errorObj.error === 'unsupported') {
94
+ return {
95
+ success: false,
96
+ error: "This feature is not supported yet. Let us know and we'll add it!",
97
+ };
98
+ }
99
+ return {
100
+ success: false,
101
+ error: 'Could not understand your request. Please use a manual command.',
102
+ };
103
+ }
104
+ // Validate against schema
105
+ const action = safeParseAction(parsed);
106
+ if (!action) {
107
+ return {
108
+ success: false,
109
+ error: 'AI response did not match expected schema. Please use a manual command.',
110
+ };
111
+ }
112
+ return {
113
+ success: true,
114
+ action,
115
+ };
116
+ }
117
+ catch (error) {
118
+ return {
119
+ success: false,
120
+ error: `AI request failed: ${error.message}`,
121
+ };
122
+ }
123
+ }
124
+ /**
125
+ * Display a helpful message when AI parsing fails.
126
+ */
127
+ export function showAiFailureMessage(error) {
128
+ console.log();
129
+ console.log(chalk.red('Error:'), error);
130
+ console.log();
131
+ console.log(chalk.yellow('Manual commands:'));
132
+ console.log();
133
+ console.log(chalk.cyan(' Convert images:'));
134
+ console.log(chalk.gray(' filex convert ./photos --to webp'));
135
+ console.log(chalk.gray(' filex convert ./photos --from png --to jpg'));
136
+ console.log();
137
+ console.log(chalk.cyan(' Resize images:'));
138
+ console.log(chalk.gray(' filex resize ./photos --max-width 1200'));
139
+ console.log();
140
+ console.log(chalk.cyan(' Merge PDFs:'));
141
+ console.log(chalk.gray(' filex merge-pdfs ./docs'));
142
+ console.log(chalk.gray(' filex merge-pdfs ./docs --order date --output combined.pdf'));
143
+ console.log();
144
+ }
145
+ //# sourceMappingURL=parseIntent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseIntent.js","sourceRoot":"","sources":["../../../src/ai/parseIntent.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function runCli(): void;
2
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"AA8DA,wBAAgB,MAAM,IAAI,IAAI,CAsG7B"}
@@ -0,0 +1,149 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import { convertImages } from './actions/convertImages.js';
4
+ import { resizeImages } from './actions/resizeImages.js';
5
+ import { mergePdfs } from './actions/mergePdfs.js';
6
+ import { parseIntent, showAiFailureMessage } from './ai/parseIntent.js';
7
+ import { formatExplanation } from './ai/explain.js';
8
+ const VERSION = '0.0.1';
9
+ /**
10
+ * Execute an action with the given directory.
11
+ */
12
+ async function executeAction(action, directory) {
13
+ switch (action.action) {
14
+ case 'convert_images':
15
+ await convertImages(directory, {
16
+ from: action.options?.from,
17
+ to: action.options?.to ?? action.options?.format,
18
+ maxWidth: action.options?.maxWidth,
19
+ quality: action.options?.quality,
20
+ });
21
+ break;
22
+ case 'resize_images':
23
+ await resizeImages(directory, {
24
+ maxWidth: action.options.maxWidth,
25
+ });
26
+ break;
27
+ case 'merge_pdfs':
28
+ await mergePdfs(directory, {
29
+ outputFile: action.options?.outputFile,
30
+ order: action.options?.order,
31
+ });
32
+ break;
33
+ }
34
+ }
35
+ /**
36
+ * Handle AI-driven input.
37
+ */
38
+ async function handleAiInput(input, directory) {
39
+ const spinner = (await import('ora')).default('Processing...').start();
40
+ const result = await parseIntent(input);
41
+ if (!result.success) {
42
+ spinner.fail('Could not parse request');
43
+ showAiFailureMessage(result.error);
44
+ process.exit(1);
45
+ }
46
+ spinner.succeed('Request parsed');
47
+ console.log();
48
+ console.log(formatExplanation(result.action));
49
+ console.log();
50
+ await executeAction(result.action, directory);
51
+ }
52
+ export function runCli() {
53
+ const program = new Command();
54
+ program
55
+ .name('filex')
56
+ .description('Terminal CLI for safe file operations with optional AI assistance')
57
+ .version(VERSION);
58
+ // Convert command
59
+ program
60
+ .command('convert')
61
+ .description('Convert images between formats')
62
+ .argument('<directory>', 'Directory containing images')
63
+ .option('--from <format>', 'Source format (png, jpg, webp, gif, tiff, avif)')
64
+ .option('--to <format>', 'Target format (webp, png, jpg)', 'webp')
65
+ .option('--max-width <pixels>', 'Maximum width in pixels', parseInt)
66
+ .option('--quality <percent>', 'Output quality (1-100)', parseInt, 80)
67
+ .action(async (directory, options) => {
68
+ try {
69
+ await convertImages(directory, {
70
+ from: options.from,
71
+ to: (options.to || 'webp'),
72
+ maxWidth: options.maxWidth,
73
+ quality: options.quality,
74
+ });
75
+ }
76
+ catch (error) {
77
+ console.error(chalk.red(`Error: ${error.message}`));
78
+ process.exit(1);
79
+ }
80
+ });
81
+ // Resize command
82
+ program
83
+ .command('resize')
84
+ .description('Resize images to a maximum width')
85
+ .argument('<directory>', 'Directory containing images')
86
+ .requiredOption('--max-width <pixels>', 'Maximum width in pixels', parseInt)
87
+ .action(async (directory, options) => {
88
+ try {
89
+ await resizeImages(directory, {
90
+ maxWidth: options.maxWidth,
91
+ });
92
+ }
93
+ catch (error) {
94
+ console.error(chalk.red(`Error: ${error.message}`));
95
+ process.exit(1);
96
+ }
97
+ });
98
+ // Merge PDFs command
99
+ program
100
+ .command('merge-pdfs')
101
+ .description('Merge all PDFs in a directory')
102
+ .argument('<directory>', 'Directory containing PDFs')
103
+ .option('-o, --output <filename>', 'Output filename', 'merged.pdf')
104
+ .option('--order <type>', 'Sort order: name, name-desc, date, date-desc, random', 'name')
105
+ .action(async (directory, options) => {
106
+ try {
107
+ await mergePdfs(directory, {
108
+ outputFile: options.output,
109
+ order: options.order,
110
+ });
111
+ }
112
+ catch (error) {
113
+ console.error(chalk.red(`Error: ${error.message}`));
114
+ process.exit(1);
115
+ }
116
+ });
117
+ // AI-driven input (default command for quoted strings)
118
+ program
119
+ .argument('[input...]', 'Natural language input for AI parsing')
120
+ .option('-d, --directory <path>', 'Target directory', '.')
121
+ .action(async (input, options) => {
122
+ const inputText = input.join(' ').trim();
123
+ if (!inputText) {
124
+ program.help();
125
+ return;
126
+ }
127
+ // Check if it looks like a command name
128
+ const commands = ['convert', 'resize', 'merge-pdfs', 'help', '--help', '-h', '--version', '-V'];
129
+ if (commands.includes(inputText.toLowerCase())) {
130
+ program.help();
131
+ return;
132
+ }
133
+ try {
134
+ await handleAiInput(inputText, options.directory);
135
+ }
136
+ catch (error) {
137
+ console.error(chalk.red(`Error: ${error.message}`));
138
+ process.exit(1);
139
+ }
140
+ });
141
+ // Handle Ctrl+C
142
+ process.on('SIGINT', () => {
143
+ console.log();
144
+ console.log(chalk.red('Interrupted.'));
145
+ process.exit(0);
146
+ });
147
+ program.parse();
148
+ }
149
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ export interface ConfirmOptions {
2
+ fileCount: number;
3
+ fileType: string;
4
+ actions: string[];
5
+ preserveOriginals?: boolean;
6
+ }
7
+ /**
8
+ * Display planned actions and prompt for user confirmation.
9
+ * Returns true if user confirms, false otherwise.
10
+ * Handles Ctrl+C gracefully.
11
+ */
12
+ export declare function confirmAction(options: ConfirmOptions): Promise<boolean>;
13
+ /**
14
+ * Display a simple message and wait for any key.
15
+ */
16
+ export declare function pressAnyKeyToContinue(message?: string): Promise<void>;
17
+ //# sourceMappingURL=confirm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"confirm.d.ts","sourceRoot":"","sources":["../../src/confirm.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,cAAc;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAgD7E;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CAAC,OAAO,SAAiC,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBnG"}
@@ -0,0 +1,69 @@
1
+ import * as readline from 'node:readline';
2
+ import chalk from 'chalk';
3
+ /**
4
+ * Display planned actions and prompt for user confirmation.
5
+ * Returns true if user confirms, false otherwise.
6
+ * Handles Ctrl+C gracefully.
7
+ */
8
+ export async function confirmAction(options) {
9
+ const { fileCount, fileType, actions, preserveOriginals = true } = options;
10
+ console.log();
11
+ console.log(chalk.cyan(`Found ${chalk.bold(fileCount)} ${fileType}`));
12
+ console.log();
13
+ console.log(chalk.yellow('Planned actions:'));
14
+ for (const action of actions) {
15
+ console.log(chalk.white(` • ${action}`));
16
+ }
17
+ if (preserveOriginals) {
18
+ console.log(chalk.white(' • Originals will remain unchanged'));
19
+ }
20
+ console.log();
21
+ return new Promise((resolve) => {
22
+ const rl = readline.createInterface({
23
+ input: process.stdin,
24
+ output: process.stdout,
25
+ });
26
+ let answered = false;
27
+ // Handle Ctrl+C gracefully
28
+ rl.on('close', () => {
29
+ if (!answered) {
30
+ answered = true;
31
+ console.log();
32
+ console.log(chalk.red('Aborted.'));
33
+ resolve(false);
34
+ }
35
+ });
36
+ rl.question(chalk.green('Continue? (y/N) '), (answer) => {
37
+ answered = true;
38
+ rl.close();
39
+ const normalized = answer.trim().toLowerCase();
40
+ if (normalized === 'y' || normalized === 'yes') {
41
+ resolve(true);
42
+ }
43
+ else {
44
+ console.log(chalk.red('Aborted.'));
45
+ resolve(false);
46
+ }
47
+ });
48
+ });
49
+ }
50
+ /**
51
+ * Display a simple message and wait for any key.
52
+ */
53
+ export async function pressAnyKeyToContinue(message = 'Press any key to continue...') {
54
+ console.log(chalk.gray(message));
55
+ return new Promise((resolve) => {
56
+ const rl = readline.createInterface({
57
+ input: process.stdin,
58
+ output: process.stdout,
59
+ });
60
+ rl.on('close', () => resolve());
61
+ process.stdin.setRawMode?.(true);
62
+ process.stdin.once('data', () => {
63
+ process.stdin.setRawMode?.(false);
64
+ rl.close();
65
+ resolve();
66
+ });
67
+ });
68
+ }
69
+ //# sourceMappingURL=confirm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"confirm.js","sourceRoot":"","sources":["../../src/confirm.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ export interface ScanResult {
2
+ directory: string;
3
+ files: string[];
4
+ count: number;
5
+ }
6
+ /**
7
+ * Scan a directory for files with specific extensions.
8
+ * Returns absolute paths of matching files.
9
+ */
10
+ export declare function scanDirectory(dirPath: string, extensions: string[]): Promise<ScanResult>;
11
+ /**
12
+ * Scan directory for image files (png, jpg, jpeg).
13
+ */
14
+ export declare function scanForImages(dirPath: string): Promise<ScanResult>;
15
+ /**
16
+ * Scan directory for PDF files.
17
+ */
18
+ export declare function scanForPdfs(dirPath: string): Promise<ScanResult>;
19
+ /**
20
+ * Check if a file is writable.
21
+ */
22
+ export declare function isWritable(filePath: string): Promise<boolean>;
23
+ /**
24
+ * Check if the directory is writable.
25
+ */
26
+ export declare function isDirectoryWritable(dirPath: string): Promise<boolean>;
27
+ //# sourceMappingURL=scan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../../src/scan.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,UAAU;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACjB;AAKD;;;GAGG;AACH,wBAAsB,aAAa,CAC/B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAAE,GACrB,OAAO,CAAC,UAAU,CAAC,CAuCrB;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAExE;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAEtE;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOnE;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQ3E"}
@@ -0,0 +1,79 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import * as path from 'node:path';
3
+ const IMAGE_EXTENSIONS = ['.png', '.jpg', '.jpeg'];
4
+ const PDF_EXTENSIONS = ['.pdf'];
5
+ /**
6
+ * Scan a directory for files with specific extensions.
7
+ * Returns absolute paths of matching files.
8
+ */
9
+ export async function scanDirectory(dirPath, extensions) {
10
+ const absolutePath = path.resolve(dirPath);
11
+ try {
12
+ const stat = await fs.stat(absolutePath);
13
+ if (!stat.isDirectory()) {
14
+ throw new Error(`Not a directory: ${absolutePath}`);
15
+ }
16
+ }
17
+ catch (error) {
18
+ if (error.code === 'ENOENT') {
19
+ throw new Error(`Directory not found: ${absolutePath}`);
20
+ }
21
+ throw error;
22
+ }
23
+ const entries = await fs.readdir(absolutePath, { withFileTypes: true });
24
+ const normalizedExtensions = extensions.map(ext => ext.startsWith('.') ? ext.toLowerCase() : `.${ext.toLowerCase()}`);
25
+ const files = [];
26
+ for (const entry of entries) {
27
+ if (entry.isFile()) {
28
+ const ext = path.extname(entry.name).toLowerCase();
29
+ if (normalizedExtensions.includes(ext)) {
30
+ files.push(path.join(absolutePath, entry.name));
31
+ }
32
+ }
33
+ }
34
+ // Sort alphabetically for predictable ordering
35
+ files.sort((a, b) => path.basename(a).localeCompare(path.basename(b)));
36
+ return {
37
+ directory: absolutePath,
38
+ files,
39
+ count: files.length,
40
+ };
41
+ }
42
+ /**
43
+ * Scan directory for image files (png, jpg, jpeg).
44
+ */
45
+ export async function scanForImages(dirPath) {
46
+ return scanDirectory(dirPath, IMAGE_EXTENSIONS);
47
+ }
48
+ /**
49
+ * Scan directory for PDF files.
50
+ */
51
+ export async function scanForPdfs(dirPath) {
52
+ return scanDirectory(dirPath, PDF_EXTENSIONS);
53
+ }
54
+ /**
55
+ * Check if a file is writable.
56
+ */
57
+ export async function isWritable(filePath) {
58
+ try {
59
+ await fs.access(filePath, fs.constants.W_OK);
60
+ return true;
61
+ }
62
+ catch {
63
+ return false;
64
+ }
65
+ }
66
+ /**
67
+ * Check if the directory is writable.
68
+ */
69
+ export async function isDirectoryWritable(dirPath) {
70
+ try {
71
+ const absolutePath = path.resolve(dirPath);
72
+ await fs.access(absolutePath, fs.constants.W_OK);
73
+ return true;
74
+ }
75
+ catch {
76
+ return false;
77
+ }
78
+ }
79
+ //# sourceMappingURL=scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/scan.ts"],"names":[],"mappings":"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"}