@singhaman21/cleansweep 1.0.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.
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ /**
3
+ * Logger utility for cleansweep
4
+ * Handles logging to console and log files with timestamps
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.Logger = void 0;
41
+ const fs = __importStar(require("fs"));
42
+ class Logger {
43
+ constructor(logFile, outputFormat = 'plain') {
44
+ this.logFile = logFile;
45
+ this.outputFormat = outputFormat;
46
+ }
47
+ /**
48
+ * Logs a message with timestamp to both console and log file (if specified)
49
+ * @param message - Message to log
50
+ * @param level - Log level (INFO, WARNING, ERROR)
51
+ */
52
+ log(message, level = 'INFO') {
53
+ const timestamp = new Date().toISOString().replace('T', ' ').slice(0, 19);
54
+ const formattedMessage = `[${timestamp}] [${level}] ${message}`;
55
+ // Print to console
56
+ if (this.outputFormat === 'json') {
57
+ const jsonMessage = JSON.stringify({
58
+ timestamp,
59
+ level,
60
+ message
61
+ });
62
+ console.log(jsonMessage);
63
+ }
64
+ else {
65
+ console.log(formattedMessage);
66
+ }
67
+ // Write to log file if specified
68
+ if (this.logFile) {
69
+ try {
70
+ fs.appendFileSync(this.logFile, formattedMessage + '\n');
71
+ }
72
+ catch (error) {
73
+ console.error(`Failed to write to log file: ${error}`);
74
+ }
75
+ }
76
+ }
77
+ /**
78
+ * Initializes the log file with header information
79
+ * @param config - Configuration object with all settings
80
+ */
81
+ initializeLogFile(config) {
82
+ if (!this.logFile)
83
+ return;
84
+ try {
85
+ const header = [
86
+ '==========================================',
87
+ `Deletion Log - ${new Date().toISOString().replace('T', ' ').slice(0, 19)}`,
88
+ '==========================================',
89
+ `Dry Run: ${config.dryRun}`,
90
+ `Interactive: ${config.interactive}`,
91
+ `Force: ${config.force}`,
92
+ ...(config.filesPattern ? [`Files Pattern: ${config.filesPattern}`] : []),
93
+ ...(config.foldersPattern ? [`Folders Pattern: ${config.foldersPattern}`] : []),
94
+ ...(config.typesPattern ? [`Types Pattern: ${config.typesPattern}`] : []),
95
+ ...(config.excludePatterns.length > 0
96
+ ? [`Exclude Patterns: ${config.excludePatterns.join(', ')}`]
97
+ : []),
98
+ ...(config.maxDepth ? [`Max Depth: ${config.maxDepth}`] : []),
99
+ '==========================================',
100
+ ''
101
+ ].join('\n');
102
+ fs.writeFileSync(this.logFile, header);
103
+ }
104
+ catch (error) {
105
+ console.error(`Failed to initialize log file: ${error}`);
106
+ }
107
+ }
108
+ }
109
+ exports.Logger = Logger;
110
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEH,uCAAyB;AAIzB,MAAa,MAAM;IAIjB,YAAY,OAAgB,EAAE,eAA6B,OAAO;QAChE,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IACnC,CAAC;IAED;;;;OAIG;IACH,GAAG,CAAC,OAAe,EAAE,QAAkB,MAAM;QAC3C,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC1E,MAAM,gBAAgB,GAAG,IAAI,SAAS,MAAM,KAAK,KAAK,OAAO,EAAE,CAAC;QAEhE,mBAAmB;QACnB,IAAI,IAAI,CAAC,YAAY,KAAK,MAAM,EAAE,CAAC;YACjC,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC;gBACjC,SAAS;gBACT,KAAK;gBACL,OAAO;aACR,CAAC,CAAC;YACH,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC3B,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAChC,CAAC;QAED,iCAAiC;QACjC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,IAAI,CAAC;gBACH,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC,CAAC;YAC3D,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,KAAK,CAAC,gCAAgC,KAAK,EAAE,CAAC,CAAC;YACzD,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,iBAAiB,CAAC,MASjB;QACC,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAE1B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG;gBACb,4CAA4C;gBAC5C,kBAAkB,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE;gBAC3E,4CAA4C;gBAC5C,YAAY,MAAM,CAAC,MAAM,EAAE;gBAC3B,gBAAgB,MAAM,CAAC,WAAW,EAAE;gBACpC,UAAU,MAAM,CAAC,KAAK,EAAE;gBACxB,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,kBAAkB,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBACzE,GAAG,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,oBAAoB,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC/E,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,kBAAkB,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBACzE,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC;oBACnC,CAAC,CAAC,CAAC,qBAAqB,MAAM,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC5D,CAAC,CAAC,EAAE,CAAC;gBACP,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,cAAc,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC7D,4CAA4C;gBAC5C,EAAE;aACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAEb,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACzC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,kCAAkC,KAAK,EAAE,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;CACF;AAhFD,wBAgFC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Interactive prompt utilities for cleansweep
3
+ * Handles user confirmation prompts
4
+ */
5
+ /**
6
+ * Prompts the user for confirmation before deleting an item
7
+ * @param item - Path of the item to delete
8
+ * @param force - If true, always returns true without prompting
9
+ * @returns Promise that resolves to true if confirmed, false otherwise
10
+ */
11
+ export declare function confirmDeletion(item: string, force?: boolean): Promise<boolean>;
12
+ /**
13
+ * Prompts the user to proceed with deletion after preview
14
+ * @returns Promise that resolves to true if confirmed, false otherwise
15
+ */
16
+ export declare function confirmProceed(): Promise<boolean>;
17
+ //# sourceMappingURL=prompts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/utils/prompts.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAcH;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,OAAe,GAAG,OAAO,CAAC,OAAO,CAAC,CActF;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CASjD"}
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ /**
3
+ * Interactive prompt utilities for cleansweep
4
+ * Handles user confirmation prompts
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.confirmDeletion = confirmDeletion;
41
+ exports.confirmProceed = confirmProceed;
42
+ const readline = __importStar(require("readline"));
43
+ /**
44
+ * Creates a readline interface for user input
45
+ */
46
+ function createReadlineInterface() {
47
+ return readline.createInterface({
48
+ input: process.stdin,
49
+ output: process.stdout
50
+ });
51
+ }
52
+ /**
53
+ * Prompts the user for confirmation before deleting an item
54
+ * @param item - Path of the item to delete
55
+ * @param force - If true, always returns true without prompting
56
+ * @returns Promise that resolves to true if confirmed, false otherwise
57
+ */
58
+ function confirmDeletion(item, force = false) {
59
+ return new Promise((resolve) => {
60
+ if (force) {
61
+ resolve(true);
62
+ return;
63
+ }
64
+ const rl = createReadlineInterface();
65
+ rl.question(`Delete '${item}'? [y/N]: `, (answer) => {
66
+ rl.close();
67
+ const response = answer.trim().toLowerCase();
68
+ resolve(response === 'y' || response === 'yes');
69
+ });
70
+ });
71
+ }
72
+ /**
73
+ * Prompts the user to proceed with deletion after preview
74
+ * @returns Promise that resolves to true if confirmed, false otherwise
75
+ */
76
+ function confirmProceed() {
77
+ return new Promise((resolve) => {
78
+ const rl = createReadlineInterface();
79
+ rl.question('Proceed with deletion? [y/N]: ', (answer) => {
80
+ rl.close();
81
+ const response = answer.trim().toLowerCase();
82
+ resolve(response === 'y' || response === 'yes');
83
+ });
84
+ });
85
+ }
86
+ //# sourceMappingURL=prompts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../src/utils/prompts.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoBH,0CAcC;AAMD,wCASC;AA/CD,mDAAqC;AAErC;;GAEG;AACH,SAAS,uBAAuB;IAC9B,OAAO,QAAQ,CAAC,eAAe,CAAC;QAC9B,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,MAAM,EAAE,OAAO,CAAC,MAAM;KACvB,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,SAAgB,eAAe,CAAC,IAAY,EAAE,QAAiB,KAAK;IAClE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,CAAC,IAAI,CAAC,CAAC;YACd,OAAO;QACT,CAAC;QAED,MAAM,EAAE,GAAG,uBAAuB,EAAE,CAAC;QACrC,EAAE,CAAC,QAAQ,CAAC,WAAW,IAAI,YAAY,EAAE,CAAC,MAAM,EAAE,EAAE;YAClD,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAC7C,OAAO,CAAC,QAAQ,KAAK,GAAG,IAAI,QAAQ,KAAK,KAAK,CAAC,CAAC;QAClD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,SAAgB,cAAc;IAC5B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,EAAE,GAAG,uBAAuB,EAAE,CAAC;QACrC,EAAE,CAAC,QAAQ,CAAC,gCAAgC,EAAE,CAAC,MAAM,EAAE,EAAE;YACvD,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAC7C,OAAO,CAAC,QAAQ,KAAK,GAAG,IAAI,QAAQ,KAAK,KAAK,CAAC,CAAC;QAClD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@singhaman21/cleansweep",
3
+ "version": "1.0.0",
4
+ "description": "A powerful command-line tool for safely deleting files and folders with comprehensive safety features",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "cleansweep": "./dist/bin/cli.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "start": "node dist/bin/cli.js",
12
+ "dev": "ts-node src/bin/cli.ts",
13
+ "prepublishOnly": "npm run build"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/SinghAman21/cleansweep.git"
18
+ },
19
+ "keywords": [
20
+ "devtool",
21
+ "script",
22
+ "bash",
23
+ "delete",
24
+ "cleanup",
25
+ "files",
26
+ "folders",
27
+ "cli",
28
+ "typescript"
29
+ ],
30
+ "author": "SinghAman21",
31
+ "license": "MIT",
32
+ "bugs": {
33
+ "url": "https://github.com/SinghAman21/cleansweep/issues"
34
+ },
35
+ "homepage": "https://github.com/SinghAman21/cleansweep#readme",
36
+ "engines": {
37
+ "node": ">=14.0.0"
38
+ },
39
+ "devDependencies": {
40
+ "@types/node": "^20.10.0",
41
+ "ts-node": "^10.9.2",
42
+ "typescript": "^5.3.3"
43
+ },
44
+ "dependencies": {
45
+ "commander": "^11.1.0",
46
+ "glob": "^10.3.10",
47
+ "readline": "^1.3.0"
48
+ }
49
+ }
package/src/bin/cli.ts ADDED
@@ -0,0 +1,130 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * cleansweep CLI Entry Point
5
+ * Command-line interface for the cleansweep deletion tool
6
+ */
7
+
8
+ import { Command } from 'commander';
9
+ import { Config } from '../types';
10
+ import { Logger } from '../utils/logger';
11
+ import { DeletionEngine } from '../core/deletionEngine';
12
+
13
+ /**
14
+ * Main function to run the CLI
15
+ */
16
+ async function main() {
17
+ const program = new Command();
18
+
19
+ program
20
+ .name('cleansweep')
21
+ .description('A powerful command-line tool for safely deleting files and folders')
22
+ .version('1.0.0')
23
+ .option('-fi, --files <pattern>', 'Specify file patterns to delete (e.g., "*.tmp", "*.log")')
24
+ .option('-fo, --folders <pattern>', 'Specify folder patterns to delete (e.g., "temp", "cache")')
25
+ .option('-ty, --types <pattern>', 'Specify multiple types of files/directories to delete')
26
+ .option('-ex, --exclude <pattern>', 'Exclude patterns or folders from deletion (can be used multiple times)', (val: string, prev: string[]) => {
27
+ prev.push(val);
28
+ return prev;
29
+ }, [] as string[])
30
+ .option('-d, --depth <number>', 'Limit the search depth in directory structure', parseInt)
31
+ .option('-dr, --dry-run', 'Simulate deletion without actually deleting anything', false)
32
+ .option('-in, --interactive', 'Prompt for confirmation before each deletion', false)
33
+ .option('-pr, --preview', 'Display list of items that will be deleted', false)
34
+ .option('-f, --force', 'Bypass safety checks and interactive warnings', false)
35
+ .option('-lg, --log <file>', 'Generate a log file with timestamps of deletions')
36
+ .option('-fm, --format <format>', 'Output format: plain, json (default: plain)', 'plain')
37
+ .addHelpText('after', `
38
+ Examples:
39
+ npx cleansweep --files "*.tmp" --folders "temp" --exclude "important" --log deletion_log.txt --dry-run
40
+ npx cleansweep -fi "*.tmp" -fo "temp" -ex "important" -lg deletion_log.txt -dr
41
+ npx cleansweep --types "*.log" --depth 2 --interactive
42
+ npx cleansweep -ty "*.log" -d 2 -in
43
+ npx cleansweep --files "*.tmp" --preview
44
+ npx cleansweep -fi "*.tmp" -pr
45
+ npx cleansweep --folders "cache" --force --log cleanup.log
46
+ npx cleansweep -fo "cache" -f -lg cleanup.log
47
+ `);
48
+
49
+ program.parse(process.argv);
50
+ const options = program.opts();
51
+
52
+ // Build configuration
53
+ const config: Config = {
54
+ dryRun: options.dryRun || false,
55
+ interactive: options.interactive || false,
56
+ force: options.force || false,
57
+ preview: options.preview || false,
58
+ logFile: options.log,
59
+ outputFormat: options.format === 'json' ? 'json' : 'plain',
60
+ maxDepth: options.depth,
61
+ filesPattern: options.files,
62
+ foldersPattern: options.folders,
63
+ typesPattern: options.types,
64
+ excludePatterns: options.exclude || []
65
+ };
66
+
67
+ // Validate that at least one pattern is specified
68
+ if (!config.filesPattern && !config.foldersPattern && !config.typesPattern) {
69
+ console.error('Error: At least one of --files, --folders, or --types must be specified');
70
+ program.help();
71
+ process.exit(1);
72
+ }
73
+
74
+ // Validate output format
75
+ if (config.outputFormat !== 'plain' && config.outputFormat !== 'json') {
76
+ console.error("Error: --format must be 'plain' or 'json'");
77
+ process.exit(1);
78
+ }
79
+
80
+ // Validate depth if provided
81
+ if (config.maxDepth !== undefined && (isNaN(config.maxDepth) || config.maxDepth < 1)) {
82
+ console.error('Error: --depth must be a positive number');
83
+ process.exit(1);
84
+ }
85
+
86
+ // Initialize logger
87
+ const logger = new Logger(config.logFile, config.outputFormat);
88
+
89
+ // Initialize log file
90
+ logger.initializeLogFile({
91
+ dryRun: config.dryRun,
92
+ interactive: config.interactive,
93
+ force: config.force,
94
+ filesPattern: config.filesPattern,
95
+ foldersPattern: config.foldersPattern,
96
+ typesPattern: config.typesPattern,
97
+ excludePatterns: config.excludePatterns,
98
+ maxDepth: config.maxDepth
99
+ });
100
+
101
+ // Log start of operation
102
+ logger.log('Starting deletion operation...', 'INFO');
103
+
104
+ try {
105
+ // Create deletion engine
106
+ const engine = new DeletionEngine(config, logger);
107
+
108
+ // Collect items to delete
109
+ await engine.collectItemsToDelete();
110
+
111
+ // Process deletions
112
+ await engine.processDeletions();
113
+
114
+ // Print summary
115
+ engine.printSummary();
116
+
117
+ // Exit with appropriate code
118
+ process.exit(engine.getExitCode());
119
+ } catch (error) {
120
+ logger.log(`Error: ${error instanceof Error ? error.message : String(error)}`, 'ERROR');
121
+ process.exit(1);
122
+ }
123
+ }
124
+
125
+ // Run main function
126
+ main().catch((error) => {
127
+ console.error('Fatal error:', error);
128
+ process.exit(1);
129
+ });
130
+
@@ -0,0 +1,219 @@
1
+ /**
2
+ * Deletion Engine for cleansweep
3
+ * Core logic for collecting and processing deletions
4
+ */
5
+
6
+ import { Config, DeletionResult } from '../types';
7
+ import { Logger } from '../utils/logger';
8
+ import {
9
+ findFiles,
10
+ findDirectories,
11
+ findItems,
12
+ shouldExclude,
13
+ deleteItem,
14
+ isFile,
15
+ isDirectory
16
+ } from '../utils/fileUtils';
17
+ import { confirmDeletion, confirmProceed } from '../utils/prompts';
18
+
19
+ export class DeletionEngine {
20
+ private config: Config;
21
+ private logger: Logger;
22
+ private itemsToDelete: string[] = [];
23
+ private deletedItems: string[] = [];
24
+ private failedDeletions: string[] = [];
25
+
26
+ constructor(config: Config, logger: Logger) {
27
+ this.config = config;
28
+ this.logger = logger;
29
+ }
30
+
31
+ /**
32
+ * Collects all items (files/folders) that match the specified patterns
33
+ * and stores them in the itemsToDelete array
34
+ */
35
+ async collectItemsToDelete(): Promise<void> {
36
+ this.itemsToDelete = [];
37
+ const allItems: string[] = [];
38
+
39
+ // Process file patterns
40
+ if (this.config.filesPattern) {
41
+ const files = await findFiles(
42
+ this.config.filesPattern,
43
+ this.config.maxDepth
44
+ );
45
+ allItems.push(...files);
46
+ }
47
+
48
+ // Process folder patterns
49
+ if (this.config.foldersPattern) {
50
+ const dirs = await findDirectories(
51
+ this.config.foldersPattern,
52
+ this.config.maxDepth
53
+ );
54
+ allItems.push(...dirs);
55
+ }
56
+
57
+ // Process types pattern (can match both files and directories)
58
+ if (this.config.typesPattern) {
59
+ const items = await findItems(
60
+ this.config.typesPattern,
61
+ this.config.maxDepth
62
+ );
63
+ allItems.push(...items);
64
+ }
65
+
66
+ // Filter out excluded items and remove duplicates
67
+ const uniqueItems = [...new Set(allItems)];
68
+ for (const item of uniqueItems) {
69
+ if (!shouldExclude(item, this.config.excludePatterns)) {
70
+ this.itemsToDelete.push(item);
71
+ }
72
+ }
73
+
74
+ // Sort for consistent output
75
+ this.itemsToDelete.sort();
76
+ }
77
+
78
+ /**
79
+ * Displays a preview of all items that will be deleted
80
+ */
81
+ displayPreview(): void {
82
+ if (this.itemsToDelete.length === 0) {
83
+ this.logger.log('No items found matching the specified patterns.', 'INFO');
84
+ return;
85
+ }
86
+
87
+ this.logger.log(
88
+ `Preview: Items that will be deleted (${this.itemsToDelete.length} items):`,
89
+ 'INFO'
90
+ );
91
+
92
+ if (this.config.outputFormat === 'json') {
93
+ const items = this.itemsToDelete.map(path => ({ path }));
94
+ console.log(JSON.stringify({ items }, null, 2));
95
+ } else {
96
+ for (const item of this.itemsToDelete) {
97
+ console.log(` - ${item}`);
98
+ }
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Deletes a single item (file or directory) with appropriate logging
104
+ * @param item - Path of the item to delete
105
+ */
106
+ async deleteItem(item: string): Promise<void> {
107
+ // Skip if dry run
108
+ if (this.config.dryRun) {
109
+ this.logger.log(`Would delete: ${item}`, 'INFO');
110
+ this.deletedItems.push(item);
111
+ return;
112
+ }
113
+
114
+ // Interactive confirmation
115
+ if (this.config.interactive) {
116
+ const confirmed = await confirmDeletion(item, this.config.force);
117
+ if (!confirmed) {
118
+ this.logger.log(`Skipped: ${item}`, 'INFO');
119
+ return;
120
+ }
121
+ }
122
+
123
+ // Perform deletion
124
+ if (isDirectory(item)) {
125
+ if (deleteItem(item)) {
126
+ this.logger.log(`Deleted directory: ${item}`, 'INFO');
127
+ this.deletedItems.push(item);
128
+ } else {
129
+ this.logger.log(`Failed to delete directory: ${item}`, 'ERROR');
130
+ this.failedDeletions.push(item);
131
+ }
132
+ } else if (isFile(item)) {
133
+ if (deleteItem(item)) {
134
+ this.logger.log(`Deleted file: ${item}`, 'INFO');
135
+ this.deletedItems.push(item);
136
+ } else {
137
+ this.logger.log(`Failed to delete file: ${item}`, 'ERROR');
138
+ this.failedDeletions.push(item);
139
+ }
140
+ } else {
141
+ this.logger.log(`Item not found or already deleted: ${item}`, 'WARNING');
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Processes all collected items for deletion based on the current mode
147
+ */
148
+ async processDeletions(): Promise<void> {
149
+ if (this.itemsToDelete.length === 0) {
150
+ this.logger.log('No items to delete.', 'INFO');
151
+ return;
152
+ }
153
+
154
+ // Show preview if requested
155
+ if (this.config.preview) {
156
+ this.displayPreview();
157
+ if (!this.config.dryRun && !this.config.force) {
158
+ const proceed = await confirmProceed();
159
+ if (!proceed) {
160
+ this.logger.log('Deletion cancelled by user.', 'INFO');
161
+ return;
162
+ }
163
+ }
164
+ }
165
+
166
+ // Process each item
167
+ for (const item of this.itemsToDelete) {
168
+ await this.deleteItem(item);
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Gets the deletion summary
174
+ * @returns DeletionResult object with statistics
175
+ */
176
+ getSummary(): DeletionResult {
177
+ return {
178
+ totalItems: this.itemsToDelete.length,
179
+ deleted: this.deletedItems.length,
180
+ failed: this.failedDeletions.length,
181
+ dryRun: this.config.dryRun,
182
+ failedItems: this.failedDeletions.length > 0 ? this.failedDeletions : undefined
183
+ };
184
+ }
185
+
186
+ /**
187
+ * Prints a summary of the deletion operation
188
+ */
189
+ printSummary(): void {
190
+ const summary = this.getSummary();
191
+
192
+ if (this.config.outputFormat === 'json') {
193
+ console.log(JSON.stringify({ summary }, null, 2));
194
+ } else {
195
+ this.logger.log('=== Deletion Summary ===', 'INFO');
196
+ this.logger.log(`Total items found: ${summary.totalItems}`, 'INFO');
197
+ if (summary.dryRun) {
198
+ this.logger.log(`Items that would be deleted: ${summary.deleted}`, 'INFO');
199
+ } else {
200
+ this.logger.log(`Items successfully deleted: ${summary.deleted}`, 'INFO');
201
+ }
202
+ if (summary.failed > 0 && summary.failedItems) {
203
+ this.logger.log(`Failed deletions: ${summary.failed}`, 'ERROR');
204
+ for (const item of summary.failedItems) {
205
+ this.logger.log(` - ${item}`, 'ERROR');
206
+ }
207
+ }
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Gets the exit code based on deletion results
213
+ * @returns 0 for success, 1 for failures
214
+ */
215
+ getExitCode(): number {
216
+ return this.failedDeletions.length > 0 ? 1 : 0;
217
+ }
218
+ }
219
+
package/src/types.ts ADDED
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Type definitions for cleansweep
3
+ */
4
+
5
+ export type LogLevel = 'INFO' | 'WARNING' | 'ERROR';
6
+
7
+ export type OutputFormat = 'plain' | 'json';
8
+
9
+ export interface Config {
10
+ dryRun: boolean;
11
+ interactive: boolean;
12
+ force: boolean;
13
+ preview: boolean;
14
+ logFile?: string;
15
+ outputFormat: OutputFormat;
16
+ maxDepth?: number;
17
+ filesPattern?: string;
18
+ foldersPattern?: string;
19
+ typesPattern?: string;
20
+ excludePatterns: string[];
21
+ }
22
+
23
+ export interface DeletionResult {
24
+ totalItems: number;
25
+ deleted: number;
26
+ failed: number;
27
+ dryRun: boolean;
28
+ failedItems?: string[];
29
+ }
30
+