appclean 1.8.0 → 2.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.
Files changed (211) hide show
  1. package/GUI_IMPLEMENTATION_STATUS.md +143 -0
  2. package/MD_Files/INDEX.md +51 -0
  3. package/MD_Files/PUBLICATION_SUCCESS_REPORT.md +227 -0
  4. package/PHASE2_COMPLETION.md +281 -0
  5. package/PHASE3_COMPLETION.md +364 -0
  6. package/README.md +446 -376
  7. package/assets/logo.svg +34 -0
  8. package/dist/core/appUpdateChecker.js +12 -16
  9. package/dist/core/appUpdateChecker.js.map +1 -1
  10. package/dist/core/detector.js +14 -18
  11. package/dist/core/detector.js.map +1 -1
  12. package/dist/core/duplicateFileFinder.js +12 -19
  13. package/dist/core/duplicateFileFinder.js.map +1 -1
  14. package/dist/core/orphanedDependencyDetector.js +19 -26
  15. package/dist/core/orphanedDependencyDetector.js.map +1 -1
  16. package/dist/core/performanceOptimizer.js +6 -10
  17. package/dist/core/performanceOptimizer.js.map +1 -1
  18. package/dist/core/permissionHandler.js +21 -25
  19. package/dist/core/permissionHandler.js.map +1 -1
  20. package/dist/core/pluginSystem.js +9 -13
  21. package/dist/core/pluginSystem.js.map +1 -1
  22. package/dist/core/removalRecorder.js +12 -19
  23. package/dist/core/removalRecorder.js.map +1 -1
  24. package/dist/core/remover.js +59 -66
  25. package/dist/core/remover.js.map +1 -1
  26. package/dist/core/reportGenerator.d.ts +1 -1
  27. package/dist/core/reportGenerator.d.ts.map +1 -1
  28. package/dist/core/reportGenerator.js +27 -34
  29. package/dist/core/reportGenerator.js.map +1 -1
  30. package/dist/core/scheduledCleanup.js +23 -30
  31. package/dist/core/scheduledCleanup.js.map +1 -1
  32. package/dist/core/serviceFileDetector.js +24 -31
  33. package/dist/core/serviceFileDetector.js.map +1 -1
  34. package/dist/core/verificationModule.js +10 -14
  35. package/dist/core/verificationModule.js.map +1 -1
  36. package/dist/index.js +190 -90
  37. package/dist/index.js.map +1 -1
  38. package/dist/managers/brewManager.d.ts.map +1 -1
  39. package/dist/managers/brewManager.js +35 -41
  40. package/dist/managers/brewManager.js.map +1 -1
  41. package/dist/managers/customManager.d.ts +2 -1
  42. package/dist/managers/customManager.d.ts.map +1 -1
  43. package/dist/managers/customManager.js +79 -53
  44. package/dist/managers/customManager.js.map +1 -1
  45. package/dist/managers/linuxManager.js +29 -36
  46. package/dist/managers/linuxManager.js.map +1 -1
  47. package/dist/managers/npmManager.js +27 -34
  48. package/dist/managers/npmManager.js.map +1 -1
  49. package/dist/types/index.js +1 -2
  50. package/dist/ui/client/api/client.d.ts +24 -0
  51. package/dist/ui/client/api/client.d.ts.map +1 -0
  52. package/dist/ui/client/api/client.js +96 -0
  53. package/dist/ui/client/api/client.js.map +1 -0
  54. package/dist/ui/client/app.d.ts +7 -0
  55. package/dist/ui/client/app.d.ts.map +1 -0
  56. package/dist/ui/client/app.js +71 -0
  57. package/dist/ui/client/app.js.map +1 -0
  58. package/dist/ui/client/index.html +107 -0
  59. package/dist/ui/client/pages/appDetails.d.ts +8 -0
  60. package/dist/ui/client/pages/appDetails.d.ts.map +1 -0
  61. package/dist/ui/client/pages/appDetails.js +287 -0
  62. package/dist/ui/client/pages/appDetails.js.map +1 -0
  63. package/dist/ui/client/pages/appSearch.d.ts +2 -0
  64. package/dist/ui/client/pages/appSearch.d.ts.map +1 -0
  65. package/dist/ui/client/pages/appSearch.js +210 -0
  66. package/dist/ui/client/pages/appSearch.js.map +1 -0
  67. package/dist/ui/client/pages/dashboard.d.ts +2 -0
  68. package/dist/ui/client/pages/dashboard.d.ts.map +1 -0
  69. package/dist/ui/client/pages/dashboard.js +154 -0
  70. package/dist/ui/client/pages/dashboard.js.map +1 -0
  71. package/dist/ui/client/pages/settings.d.ts +7 -0
  72. package/dist/ui/client/pages/settings.d.ts.map +1 -0
  73. package/dist/ui/client/pages/settings.js +279 -0
  74. package/dist/ui/client/pages/settings.js.map +1 -0
  75. package/dist/ui/client/state/appStore.d.ts +38 -0
  76. package/dist/ui/client/state/appStore.d.ts.map +1 -0
  77. package/dist/ui/client/state/appStore.js +121 -0
  78. package/dist/ui/client/state/appStore.js.map +1 -0
  79. package/dist/ui/client/state/dashboardStore.d.ts +31 -0
  80. package/dist/ui/client/state/dashboardStore.d.ts.map +1 -0
  81. package/dist/ui/client/state/dashboardStore.js +70 -0
  82. package/dist/ui/client/state/dashboardStore.js.map +1 -0
  83. package/dist/ui/client/state/uiStore.d.ts +43 -0
  84. package/dist/ui/client/state/uiStore.d.ts.map +1 -0
  85. package/dist/ui/client/state/uiStore.js +109 -0
  86. package/dist/ui/client/state/uiStore.js.map +1 -0
  87. package/dist/ui/client/styles/animations.css +327 -0
  88. package/dist/ui/client/styles/base.css +214 -0
  89. package/dist/ui/client/styles/components.css +400 -0
  90. package/dist/ui/client/styles/layout.css +224 -0
  91. package/dist/ui/client/styles/variables.css +140 -0
  92. package/dist/ui/client/utils/events.d.ts +19 -0
  93. package/dist/ui/client/utils/events.d.ts.map +1 -0
  94. package/dist/ui/client/utils/events.js +54 -0
  95. package/dist/ui/client/utils/events.js.map +1 -0
  96. package/dist/ui/client/utils/formatting.d.ts +11 -0
  97. package/dist/ui/client/utils/formatting.d.ts.map +1 -0
  98. package/dist/ui/client/utils/formatting.js +104 -0
  99. package/dist/ui/client/utils/formatting.js.map +1 -0
  100. package/dist/ui/client/utils/router.d.ts +25 -0
  101. package/dist/ui/client/utils/router.d.ts.map +1 -0
  102. package/dist/ui/client/utils/router.js +90 -0
  103. package/dist/ui/client/utils/router.js.map +1 -0
  104. package/dist/ui/guiServer.d.ts +8 -1
  105. package/dist/ui/guiServer.d.ts.map +1 -1
  106. package/dist/ui/guiServer.js +148 -110
  107. package/dist/ui/guiServer.js.map +1 -1
  108. package/dist/ui/menu.js +18 -27
  109. package/dist/ui/menu.js.map +1 -1
  110. package/dist/ui/prompts.js +34 -47
  111. package/dist/ui/prompts.js.map +1 -1
  112. package/dist/ui/server/middleware/errorHandler.d.ts +19 -0
  113. package/dist/ui/server/middleware/errorHandler.d.ts.map +1 -0
  114. package/dist/ui/server/middleware/errorHandler.js +100 -0
  115. package/dist/ui/server/middleware/errorHandler.js.map +1 -0
  116. package/dist/ui/server/routes/apps.d.ts +8 -0
  117. package/dist/ui/server/routes/apps.d.ts.map +1 -0
  118. package/dist/ui/server/routes/apps.js +74 -0
  119. package/dist/ui/server/routes/apps.js.map +1 -0
  120. package/dist/ui/server/routes/dashboard.d.ts +4 -0
  121. package/dist/ui/server/routes/dashboard.d.ts.map +1 -0
  122. package/dist/ui/server/routes/dashboard.js +57 -0
  123. package/dist/ui/server/routes/dashboard.js.map +1 -0
  124. package/dist/ui/server/routes/settings.d.ts +6 -0
  125. package/dist/ui/server/routes/settings.d.ts.map +1 -0
  126. package/dist/ui/server/routes/settings.js +31 -0
  127. package/dist/ui/server/routes/settings.js.map +1 -0
  128. package/dist/ui/server/services/appService.d.ts +45 -0
  129. package/dist/ui/server/services/appService.d.ts.map +1 -0
  130. package/dist/ui/server/services/appService.js +114 -0
  131. package/dist/ui/server/services/appService.js.map +1 -0
  132. package/dist/ui/server/services/removalService.d.ts +24 -0
  133. package/dist/ui/server/services/removalService.d.ts.map +1 -0
  134. package/dist/ui/server/services/removalService.js +83 -0
  135. package/dist/ui/server/services/removalService.js.map +1 -0
  136. package/dist/utils/filesystem.js +32 -49
  137. package/dist/utils/filesystem.js.map +1 -1
  138. package/dist/utils/logger.js +9 -18
  139. package/dist/utils/logger.js.map +1 -1
  140. package/dist/utils/platform.js +10 -22
  141. package/dist/utils/platform.js.map +1 -1
  142. package/dist/utils/upgrade.d.ts +22 -0
  143. package/dist/utils/upgrade.d.ts.map +1 -0
  144. package/dist/utils/upgrade.js +94 -0
  145. package/dist/utils/upgrade.js.map +1 -0
  146. package/package.json +4 -2
  147. package/src/core/appUpdateChecker.ts +1 -1
  148. package/src/core/detector.ts +6 -6
  149. package/src/core/duplicateFileFinder.ts +1 -1
  150. package/src/core/orphanedDependencyDetector.ts +2 -2
  151. package/src/core/performanceOptimizer.ts +1 -1
  152. package/src/core/permissionHandler.ts +2 -2
  153. package/src/core/pluginSystem.ts +1 -1
  154. package/src/core/removalRecorder.ts +2 -2
  155. package/src/core/remover.ts +11 -11
  156. package/src/core/reportGenerator.ts +2 -2
  157. package/src/core/scheduledCleanup.ts +2 -2
  158. package/src/core/serviceFileDetector.ts +2 -2
  159. package/src/core/verificationModule.ts +2 -2
  160. package/src/index.ts +133 -6
  161. package/src/managers/brewManager.ts +11 -9
  162. package/src/managers/customManager.ts +71 -30
  163. package/src/managers/linuxManager.ts +3 -3
  164. package/src/managers/npmManager.ts +3 -3
  165. package/src/ui/client/api/client.ts +163 -0
  166. package/src/ui/client/app.ts +121 -0
  167. package/src/ui/client/index.html +107 -0
  168. package/src/ui/client/pages/appDetails.ts +356 -0
  169. package/src/ui/client/pages/appSearch.ts +270 -0
  170. package/src/ui/client/pages/dashboard.ts +189 -0
  171. package/src/ui/client/pages/settings.ts +342 -0
  172. package/src/ui/client/state/appStore.ts +169 -0
  173. package/src/ui/client/state/dashboardStore.ts +113 -0
  174. package/src/ui/client/state/uiStore.ts +166 -0
  175. package/src/ui/client/styles/animations.css +327 -0
  176. package/src/ui/client/styles/base.css +214 -0
  177. package/src/ui/client/styles/components.css +400 -0
  178. package/src/ui/client/styles/layout.css +224 -0
  179. package/src/ui/client/styles/variables.css +140 -0
  180. package/src/ui/client/utils/events.ts +74 -0
  181. package/src/ui/client/utils/formatting.ts +157 -0
  182. package/src/ui/client/utils/router.ts +161 -0
  183. package/src/ui/guiServer.ts +206 -105
  184. package/src/ui/prompts.ts +1 -1
  185. package/src/ui/server/middleware/errorHandler.ts +174 -0
  186. package/src/ui/server/routes/apps.ts +132 -0
  187. package/src/ui/server/routes/dashboard.ts +93 -0
  188. package/src/ui/server/routes/settings.ts +63 -0
  189. package/src/ui/server/services/appService.ts +184 -0
  190. package/src/ui/server/services/removalService.ts +138 -0
  191. package/src/utils/upgrade.ts +143 -0
  192. package/tsconfig.json +3 -2
  193. package/INDEX.md +0 -165
  194. /package/{ACTION_CHECKLIST.md → MD_Files/ACTION_CHECKLIST.md} +0 -0
  195. /package/{APPCLEAN_SUMMARY.md → MD_Files/APPCLEAN_SUMMARY.md} +0 -0
  196. /package/{CHANGELOG.md → MD_Files/CHANGELOG.md} +0 -0
  197. /package/{CODE_OF_CONDUCT.md → MD_Files/CODE_OF_CONDUCT.md} +0 -0
  198. /package/{CODE_REVIEW_REPORT.md → MD_Files/CODE_REVIEW_REPORT.md} +0 -0
  199. /package/{COMMUNITY_POSTS.md → MD_Files/COMMUNITY_POSTS.md} +0 -0
  200. /package/{DEPLOYMENT_GUIDE.md → MD_Files/DEPLOYMENT_GUIDE.md} +0 -0
  201. /package/{DEPLOYMENT_STATUS.md → MD_Files/DEPLOYMENT_STATUS.md} +0 -0
  202. /package/{EXECUTIVE_REPORT.md → MD_Files/EXECUTIVE_REPORT.md} +0 -0
  203. /package/{GITHUB_OPTIMIZATION.md → MD_Files/GITHUB_OPTIMIZATION.md} +0 -0
  204. /package/{MARKETING_SUMMARY.md → MD_Files/MARKETING_SUMMARY.md} +0 -0
  205. /package/{NPM_PACKAGE_OPTIMIZATION.md → MD_Files/NPM_PACKAGE_OPTIMIZATION.md} +0 -0
  206. /package/{NPM_PUBLISH.md → MD_Files/NPM_PUBLISH.md} +0 -0
  207. /package/{PROJECT_SUMMARY.txt → MD_Files/PROJECT_SUMMARY.txt} +0 -0
  208. /package/{QUICKSTART.md → MD_Files/QUICKSTART.md} +0 -0
  209. /package/{SETUP_GITHUB.md → MD_Files/SETUP_GITHUB.md} +0 -0
  210. /package/{TESTING_SUMMARY.md → MD_Files/TESTING_SUMMARY.md} +0 -0
  211. /package/{setup-github.sh → MD_Files/setup-github.sh} +0 -0
@@ -0,0 +1,94 @@
1
+ import { execSync } from 'child_process';
2
+ import { Logger } from './logger.js';
3
+ export class UpgradeManager {
4
+ constructor() {
5
+ this.packageName = 'appclean';
6
+ this.currentVersion = '1.9.0';
7
+ }
8
+ async getVersionInfo() {
9
+ try {
10
+ const output = execSync(`npm view ${this.packageName} version --json`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
11
+ const latestVersion = output.replace(/"/g, '');
12
+ const isUpdateAvailable = this.compareVersions(this.currentVersion, latestVersion);
13
+ return {
14
+ current: this.currentVersion,
15
+ latest: latestVersion,
16
+ isUpdateAvailable,
17
+ };
18
+ }
19
+ catch (error) {
20
+ Logger.debug('Failed to check npm registry: ' + error.message);
21
+ return {
22
+ current: this.currentVersion,
23
+ latest: this.currentVersion,
24
+ isUpdateAvailable: false,
25
+ };
26
+ }
27
+ }
28
+ compareVersions(current, latest) {
29
+ const currentParts = current.split('.').map(Number);
30
+ const latestParts = latest.split('.').map(Number);
31
+ for (let i = 0; i < 3; i++) {
32
+ const curr = currentParts[i] || 0;
33
+ const lat = latestParts[i] || 0;
34
+ if (lat > curr)
35
+ return true;
36
+ if (lat < curr)
37
+ return false;
38
+ }
39
+ return false;
40
+ }
41
+ async upgrade() {
42
+ try {
43
+ Logger.info('Checking for updates...');
44
+ const versionInfo = await this.getVersionInfo();
45
+ if (!versionInfo.isUpdateAvailable) {
46
+ return {
47
+ success: true,
48
+ message: `AppClean is already up to date (v${versionInfo.current})`,
49
+ };
50
+ }
51
+ Logger.info(`Upgrading from v${versionInfo.current} to v${versionInfo.latest}...`);
52
+ execSync(`npm install -g ${this.packageName}@latest`, {
53
+ stdio: 'inherit',
54
+ });
55
+ return {
56
+ success: true,
57
+ message: `Successfully upgraded to v${versionInfo.latest}`,
58
+ };
59
+ }
60
+ catch (error) {
61
+ const errorMsg = error.message;
62
+ return {
63
+ success: false,
64
+ message: `Upgrade failed: ${errorMsg}`,
65
+ };
66
+ }
67
+ }
68
+ getCurrentVersion() {
69
+ return this.currentVersion;
70
+ }
71
+ async checkForUpdates() {
72
+ return this.getVersionInfo();
73
+ }
74
+ async uninstall() {
75
+ try {
76
+ Logger.info('Uninstalling AppClean...');
77
+ execSync(`npm uninstall -g ${this.packageName}`, {
78
+ stdio: 'inherit',
79
+ });
80
+ return {
81
+ success: true,
82
+ message: `AppClean has been successfully uninstalled from your system.`,
83
+ };
84
+ }
85
+ catch (error) {
86
+ const errorMsg = error.message;
87
+ return {
88
+ success: false,
89
+ message: `Uninstall failed: ${errorMsg}`,
90
+ };
91
+ }
92
+ }
93
+ }
94
+ //# sourceMappingURL=upgrade.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upgrade.js","sourceRoot":"","sources":["../../src/utils/upgrade.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,13 +1,15 @@
1
1
  {
2
2
  "name": "appclean",
3
- "version": "1.8.0",
3
+ "version": "2.0.0",
4
+ "type": "module",
4
5
  "description": "A powerful CLI tool to intelligently find and safely uninstall applications from your system with all their artifacts",
5
6
  "main": "dist/index.js",
6
7
  "bin": {
7
8
  "appclean": "dist/index.js"
8
9
  },
9
10
  "scripts": {
10
- "build": "tsc",
11
+ "build": "tsc && npm run copy-assets",
12
+ "copy-assets": "mkdir -p dist/ui/client/styles && cp src/ui/client/index.html dist/ui/client/ && cp src/ui/client/styles/*.css dist/ui/client/styles/",
11
13
  "dev": "ts-node src/index.ts",
12
14
  "start": "node dist/index.js",
13
15
  "test": "jest",
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import { execSync } from 'child_process';
8
- import { Logger } from '../utils/logger';
8
+ import { Logger } from '../utils/logger.js';
9
9
  import { InstalledApp } from '../types';
10
10
 
11
11
  export interface UpdateInfo {
@@ -1,10 +1,10 @@
1
- import { NpmManager } from '../managers/npmManager';
2
- import { BrewManager } from '../managers/brewManager';
3
- import { LinuxManager } from '../managers/linuxManager';
4
- import { CustomManager } from '../managers/customManager';
5
- import { isMacOS, isLinux } from '../utils/platform';
1
+ import { NpmManager } from '../managers/npmManager.js';
2
+ import { BrewManager } from '../managers/brewManager.js';
3
+ import { LinuxManager } from '../managers/linuxManager.js';
4
+ import { CustomManager } from '../managers/customManager.js';
5
+ import { isMacOS, isLinux } from '../utils/platform.js';
6
6
  import { InstalledApp, ArtifactPath, SearchOptions } from '../types';
7
- import { getFileSize } from '../utils/filesystem';
7
+ import { getFileSize } from '../utils/filesystem.js';
8
8
 
9
9
  export class Detector {
10
10
  private npmManager: NpmManager;
@@ -7,7 +7,7 @@
7
7
  import fs from 'fs';
8
8
  import path from 'path';
9
9
  import crypto from 'crypto';
10
- import { Logger } from '../utils/logger';
10
+ import { Logger } from '../utils/logger.js';
11
11
 
12
12
  export interface DuplicateFile {
13
13
  hash: string;
@@ -5,10 +5,10 @@
5
5
  */
6
6
 
7
7
  import { execSync } from 'child_process';
8
- import { getHomeDir } from '../utils/platform';
8
+ import { getHomeDir } from '../utils/platform.js';
9
9
  import path from 'path';
10
10
  import fs from 'fs';
11
- import { Logger } from '../utils/logger';
11
+ import { Logger } from '../utils/logger.js';
12
12
 
13
13
  export interface OrphanedPackage {
14
14
  name: string;
@@ -4,7 +4,7 @@
4
4
  * v1.8.0 Feature
5
5
  */
6
6
 
7
- import { Logger } from '../utils/logger';
7
+ import { Logger } from '../utils/logger.js';
8
8
 
9
9
  export interface ScanMetrics {
10
10
  startTime: Date;
@@ -1,6 +1,6 @@
1
1
  import { execSync } from 'child_process';
2
- import { isMacOS, isLinux, isWindows } from '../utils/platform';
3
- import { Logger } from '../utils/logger';
2
+ import { isMacOS, isLinux, isWindows } from '../utils/platform.js';
3
+ import { Logger } from '../utils/logger.js';
4
4
 
5
5
  export class PermissionHandler {
6
6
  /**
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import { ArtifactPath } from '../types';
8
- import { Logger } from '../utils/logger';
8
+ import { Logger } from '../utils/logger.js';
9
9
 
10
10
  export interface AppDetectorPlugin {
11
11
  name: string;
@@ -1,8 +1,8 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import { getHomeDir } from '../utils/platform';
3
+ import { getHomeDir } from '../utils/platform.js';
4
4
  import { ArtifactPath } from '../types';
5
- import { formatDate, formatBytes } from '../utils/logger';
5
+ import { formatDate, formatBytes } from '../utils/logger.js';
6
6
 
7
7
  export interface RemovalRecord {
8
8
  timestamp: Date;
@@ -1,22 +1,22 @@
1
1
  import path from 'path';
2
2
  import { execSync } from 'child_process';
3
- import { getHomeDir } from '../utils/platform';
3
+ import { getHomeDir } from '../utils/platform.js';
4
4
  import {
5
5
  deleteFile,
6
6
  deleteDirectory,
7
7
  pathExists,
8
8
  getDirectorySize,
9
- } from '../utils/filesystem';
9
+ } from '../utils/filesystem.js';
10
10
  import { ArtifactPath, RemovalOptions, RemovalResult } from '../types';
11
- import { Logger, formatBytes } from '../utils/logger';
12
- import { NpmManager } from '../managers/npmManager';
13
- import { BrewManager } from '../managers/brewManager';
14
- import { LinuxManager } from '../managers/linuxManager';
15
- import { PermissionHandler } from './permissionHandler';
16
- import { ServiceFileDetector } from './serviceFileDetector';
17
- import { RemovalRecorder, RemovalRecord } from './removalRecorder';
18
- import { ReportGenerator } from './reportGenerator';
19
- import { VerificationModule } from './verificationModule';
11
+ import { Logger, formatBytes } from '../utils/logger.js';
12
+ import { NpmManager } from '../managers/npmManager.js';
13
+ import { BrewManager } from '../managers/brewManager.js';
14
+ import { LinuxManager } from '../managers/linuxManager.js';
15
+ import { PermissionHandler } from './permissionHandler.js';
16
+ import { ServiceFileDetector } from './serviceFileDetector.js';
17
+ import { RemovalRecorder, RemovalRecord } from './removalRecorder.js';
18
+ import { ReportGenerator } from './reportGenerator.js';
19
+ import { VerificationModule } from './verificationModule.js';
20
20
 
21
21
  export class Remover {
22
22
  async previewRemoval(artifacts: ArtifactPath[]): Promise<void> {
@@ -1,8 +1,8 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import chalk from 'chalk';
4
- import { RemovalRecord, DeletedArtifact } from './removalRecorder';
5
- import { formatBytes, formatDate } from '../utils/logger';
4
+ import { RemovalRecord, DeletedArtifact } from './removalRecorder.js';
5
+ import { formatBytes, formatDate } from '../utils/logger.js';
6
6
 
7
7
  export const REMOVAL_DISCLAIMER = `
8
8
  ╔════════════════════════════════════════════════════════════════════════════╗
@@ -4,10 +4,10 @@
4
4
  * v1.6.0 Feature
5
5
  */
6
6
 
7
- import { getHomeDir } from '../utils/platform';
7
+ import { getHomeDir } from '../utils/platform.js';
8
8
  import path from 'path';
9
9
  import fs from 'fs';
10
- import { Logger } from '../utils/logger';
10
+ import { Logger } from '../utils/logger.js';
11
11
 
12
12
  export type CleanupFrequency = 'daily' | 'weekly' | 'monthly' | 'custom';
13
13
 
@@ -1,6 +1,6 @@
1
1
  import path from 'path';
2
- import { isMacOS, isLinux, getHomeDir } from '../utils/platform';
3
- import { pathExists, readFile, listDirectory } from '../utils/filesystem';
2
+ import { isMacOS, isLinux, getHomeDir } from '../utils/platform.js';
3
+ import { pathExists, readFile, listDirectory } from '../utils/filesystem.js';
4
4
  import { ArtifactPath } from '../types';
5
5
 
6
6
  export interface ServiceFile {
@@ -1,6 +1,6 @@
1
1
  import { execSync } from 'child_process';
2
- import { isMacOS, isLinux, isWindows } from '../utils/platform';
3
- import { pathExists } from '../utils/filesystem';
2
+ import { isMacOS, isLinux, isWindows } from '../utils/platform.js';
3
+ import { pathExists } from '../utils/filesystem.js';
4
4
 
5
5
  export type VerificationStatus = 'verified_removed' | 'still_exists' | 'partial_removal' | 'unknown';
6
6
 
package/src/index.ts CHANGED
@@ -3,14 +3,15 @@
3
3
  import { Command } from 'commander';
4
4
  import chalk from 'chalk';
5
5
  import ora from 'ora';
6
- import { Detector } from './core/detector';
7
- import { Remover } from './core/remover';
6
+ import { Detector } from './core/detector.js';
7
+ import { Remover } from './core/remover.js';
8
+ import { GUIServer } from './ui/guiServer.js';
8
9
  import {
9
10
  showMainMenu,
10
11
  showAppMenu,
11
12
  showHeader,
12
13
  showHelp,
13
- } from './ui/menu';
14
+ } from './ui/menu.js';
14
15
  import {
15
16
  promptSearchQuery,
16
17
  promptSelectApp,
@@ -20,11 +21,12 @@ import {
20
21
  promptSortBy,
21
22
  displayAppDetails,
22
23
  promptFinalConfirmation,
23
- } from './ui/prompts';
24
- import { Logger, formatBytes } from './utils/logger';
24
+ } from './ui/prompts.js';
25
+ import { Logger, formatBytes } from './utils/logger.js';
26
+ import { UpgradeManager } from './utils/upgrade.js';
25
27
  import { InstalledApp } from './types';
26
28
 
27
- const VERSION = '1.8.0';
29
+ const VERSION = '2.0.0';
28
30
 
29
31
  async function interactiveMode(): Promise<void> {
30
32
  showHeader();
@@ -424,6 +426,131 @@ async function main(): Promise<void> {
424
426
  }
425
427
  });
426
428
 
429
+ program
430
+ .command('gui')
431
+ .description('Launch AppClean GUI server (opens at http://localhost:3000)')
432
+ .option('--port <number>', 'Port to run GUI server on', '3000')
433
+ .action(async (options) => {
434
+ const port = parseInt(options.port, 10);
435
+
436
+ if (isNaN(port) || port < 1 || port > 65535) {
437
+ Logger.error('Invalid port number. Please use a port between 1 and 65535.');
438
+ process.exit(1);
439
+ }
440
+
441
+ const guiServer = new GUIServer(port);
442
+
443
+ try {
444
+ await guiServer.start();
445
+ Logger.info(`AppClean GUI is running at ${chalk.cyan(`http://localhost:${port}`)}`);
446
+ Logger.info('Press Ctrl+C to stop the server');
447
+
448
+ // Keep the server running - handle graceful shutdown
449
+ process.on('SIGINT', async () => {
450
+ Logger.info('Shutting down...');
451
+ await guiServer.stop();
452
+ process.exit(0);
453
+ });
454
+ } catch (error) {
455
+ Logger.error(`Failed to start GUI server: ${(error as Error).message}`);
456
+ process.exit(1);
457
+ }
458
+ });
459
+
460
+ program
461
+ .command('upgrade')
462
+ .description('Upgrade AppClean to the latest version')
463
+ .action(async () => {
464
+ const upgradeManager = new UpgradeManager();
465
+
466
+ Logger.space();
467
+
468
+ try {
469
+ const result = await upgradeManager.upgrade();
470
+
471
+ if (result.success) {
472
+ Logger.success(result.message);
473
+ } else {
474
+ Logger.warn(result.message);
475
+ }
476
+ } catch (error) {
477
+ Logger.error(`Upgrade failed: ${(error as Error).message}`);
478
+ process.exit(1);
479
+ }
480
+
481
+ Logger.space();
482
+ });
483
+
484
+ program
485
+ .command('check-update')
486
+ .description('Check if a new version of AppClean is available')
487
+ .action(async () => {
488
+ const upgradeManager = new UpgradeManager();
489
+
490
+ Logger.space();
491
+
492
+ try {
493
+ const versionInfo = await upgradeManager.checkForUpdates();
494
+
495
+ Logger.info(`Current version: ${chalk.cyan(`v${versionInfo.current}`)}`);
496
+ Logger.info(`Latest version: ${chalk.cyan(`v${versionInfo.latest}`)}`);
497
+
498
+ if (versionInfo.isUpdateAvailable) {
499
+ Logger.warn(
500
+ `A new version is available! Run ${chalk.bold('appclean upgrade')} to update.`
501
+ );
502
+ } else {
503
+ Logger.success('AppClean is up to date!');
504
+ }
505
+ } catch (error) {
506
+ Logger.error(
507
+ `Failed to check for updates: ${(error as Error).message}`
508
+ );
509
+ process.exit(1);
510
+ }
511
+
512
+ Logger.space();
513
+ });
514
+
515
+ program
516
+ .command('uninstall')
517
+ .description('Uninstall AppClean from your system')
518
+ .option('--force', 'Skip confirmation prompt')
519
+ .action(async (options) => {
520
+ Logger.space();
521
+ Logger.warn('⚠️ This will uninstall AppClean from your system.');
522
+
523
+ // Show confirmation if not using --force
524
+ if (!options.force) {
525
+ const { promptConfirmRemoval } = await import('./ui/prompts');
526
+ const confirmed = await promptConfirmRemoval('AppClean');
527
+
528
+ if (!confirmed) {
529
+ Logger.info('Uninstall cancelled');
530
+ Logger.space();
531
+ return;
532
+ }
533
+ }
534
+
535
+ const upgradeManager = new UpgradeManager();
536
+
537
+ try {
538
+ const result = await upgradeManager.uninstall();
539
+
540
+ if (result.success) {
541
+ Logger.success(result.message);
542
+ Logger.info('Thank you for using AppClean!');
543
+ } else {
544
+ Logger.warn(result.message);
545
+ }
546
+ } catch (error) {
547
+ Logger.error(`Uninstall failed: ${(error as Error).message}`);
548
+ process.exit(1);
549
+ }
550
+
551
+ Logger.space();
552
+ });
553
+
427
554
  program.on('command:*', () => {
428
555
  if (process.argv.length < 3) {
429
556
  interactiveMode().catch((error) => {
@@ -1,9 +1,9 @@
1
1
  import { execSync } from 'child_process';
2
2
  import path from 'path';
3
- import { getHomeDir } from '../utils/platform';
4
- import { pathExists, listDirectory } from '../utils/filesystem';
3
+ import { getHomeDir } from '../utils/platform.js';
4
+ import { pathExists, listDirectory } from '../utils/filesystem.js';
5
5
  import { InstalledApp, ArtifactPath } from '../types';
6
- import { Logger } from '../utils/logger';
6
+ import { Logger } from '../utils/logger.js';
7
7
 
8
8
  export class BrewManager {
9
9
  private brewPrefix: string;
@@ -28,16 +28,18 @@ export class BrewManager {
28
28
  const packages: InstalledApp[] = [];
29
29
 
30
30
  try {
31
- const output = execSync('brew list --json').toString();
32
- const data = JSON.parse(output) as any[];
31
+ // Use 'brew list -1' which outputs one package per line
32
+ const output = execSync('brew list -1').toString();
33
+ const lines = output.split('\n').filter((line) => line.trim().length > 0);
33
34
 
34
- for (const pkg of data) {
35
- const cellPath = path.join(this.brewPrefix, 'Cellar', pkg);
35
+ for (const pkg of lines) {
36
+ const packageName = pkg.trim();
37
+ const cellarPath = path.join(this.brewPrefix, 'Cellar', packageName);
36
38
  packages.push({
37
- name: pkg,
39
+ name: packageName,
38
40
  version: 'unknown',
39
41
  installMethod: 'brew',
40
- mainPath: cellPath,
42
+ mainPath: cellarPath,
41
43
  installedDate: undefined,
42
44
  });
43
45
  }
@@ -1,21 +1,57 @@
1
1
  import path from 'path';
2
- import { getHomeDir } from '../utils/platform';
2
+ import { getHomeDir } from '../utils/platform.js';
3
3
  import {
4
4
  pathExists,
5
5
  listDirectory,
6
6
  readFile,
7
7
  listDirectoryDeep,
8
- } from '../utils/filesystem';
8
+ } from '../utils/filesystem.js';
9
9
  import { InstalledApp, ArtifactPath } from '../types';
10
10
 
11
11
  export class CustomManager {
12
+ // List of known system utilities that should be excluded
13
+ private readonly systemBinaries = new Set([
14
+ '2to3', 'actool', 'afclip', 'arch', 'asa', 'awk', 'bash', 'basename',
15
+ 'bc', 'bdiff', 'bg', 'bison', 'cat', 'cc', 'chgrp', 'chmod', 'chown',
16
+ 'cksum', 'clang', 'cmake', 'codesign', 'comm', 'cp', 'cpp', 'cut',
17
+ 'date', 'dd', 'defaults', 'df', 'diff', 'diffstat', 'dill', 'dirname',
18
+ 'dmesg', 'du', 'ed', 'env', 'expand', 'expr', 'false', 'file', 'find',
19
+ 'flex', 'fold', 'fsync', 'gawk', 'gcc', 'gcov', 'gdb', 'getconf', 'getopt',
20
+ 'git', 'git-receive-pack', 'git-shell', 'git-upload-archive', 'git-upload-pack',
21
+ 'glob', 'gnumake', 'go', 'grep', 'gzip', 'head', 'hexdump', 'hostname',
22
+ 'id', 'install', 'ipcalc', 'join', 'kill', 'killall', 'lame', 'ld',
23
+ 'ldd', 'less', 'lex', 'link', 'ln', 'locale', 'localedef', 'locate',
24
+ 'logger', 'login', 'logname', 'ls', 'lsattr', 'lsof', 'lz4', 'make',
25
+ 'man', 'md5sum', 'mddigest', 'mknod', 'mmap', 'mount', 'mv', 'netstat',
26
+ 'nl', 'nm', 'nohup', 'od', 'openssl', 'paste', 'patch', 'pax', 'pbcopy',
27
+ 'pbpaste', 'perl', 'php', 'ping', 'pkill', 'plutil', 'printf', 'ps',
28
+ 'pwd', 'python', 'python3', 'python2', 'ranlib', 'realpath', 'renice',
29
+ 'rev', 'rm', 'rmdir', 'ruby', 'sed', 'seq', 'sh', 'shasum', 'shell_cmds',
30
+ 'sleep', 'sort', 'split', 'stat', 'strings', 'strip', 'sudo', 'sum',
31
+ 'swift', 'swiftc', 'tail', 'tar', 'tee', 'test', 'tic', 'touch', 'tr',
32
+ 'true', 'tsort', 'tty', 'uname', 'uncompress', 'uniq', 'unlink', 'unzip',
33
+ 'users', 'uuidgen', 'uuencode', 'uudecode', 'vchk', 'vis', 'wc', 'what',
34
+ 'whereis', 'which', 'while', 'whoami', 'whois', 'xargs', 'xcode-select',
35
+ 'xcodebuild', 'xml2-config', 'xmllint', 'xz', 'yes', 'zcat', 'zip', 'zsh',
36
+ // PostScript and Ghostscript utilities
37
+ 'dvipdf', 'eps2eps', 'pdf2ps', 'ps2ascii', 'ps2epsi', 'ps2pdf', 'ps2pdf12',
38
+ 'ps2pdf13', 'ps2pdf14', 'ps2pdfwr', 'ps2ps', 'ps2ps2', 'pf2afm', 'pfbtopfa',
39
+ 'pphs', 'printafm', 'prlcopy', 'prlctl', 'prlexec', 'prlsrvctl', 'prl_convert',
40
+ 'prl_disk_tool', 'prl_perf_ctl',
41
+ // Ghostscript
42
+ 'gsbj', 'gsdj', 'gsdj500', 'gslj', 'gslp', 'gsnd',
43
+ // System scripts (shell scripts)
44
+ 'lprsetup.sh', 'unix-lpr.sh', 'uninstall-container.sh', 'update-container.sh',
45
+ ]);
46
+
12
47
  async findCustomInstalledApps(): Promise<InstalledApp[]> {
13
48
  const apps: InstalledApp[] = [];
14
49
  const home = getHomeDir();
15
50
 
51
+ // Only check user-installed binaries in ~/.local/bin and /usr/local/bin
52
+ // Completely skip /usr/bin as it's all system utilities
16
53
  const customBinPaths = [
17
54
  '/usr/local/bin',
18
- '/usr/bin',
19
55
  `${home}/.local/bin`,
20
56
  ];
21
57
 
@@ -31,47 +67,52 @@ export class CustomManager {
31
67
  // Skip if already found
32
68
  if (foundBinaries.has(binary)) continue;
33
69
 
34
- // Try to detect installation method from the binary
35
- const method = await this.detectInstallMethod(fullPath);
36
-
37
- // Only add if it looks like a custom install (not from system)
38
- if (method === 'custom') {
39
- foundBinaries.add(binary);
40
- apps.push({
41
- name: binary,
42
- version: 'unknown',
43
- installMethod: 'custom',
44
- mainPath: fullPath,
45
- installedDate: undefined,
46
- });
70
+ // Skip known system binaries
71
+ if (this.systemBinaries.has(binary)) continue;
72
+
73
+ // Skip if it looks like a system binary
74
+ if (await this.isSystemBinary(binary, fullPath)) {
75
+ continue;
47
76
  }
77
+
78
+ foundBinaries.add(binary);
79
+ apps.push({
80
+ name: binary,
81
+ version: 'unknown',
82
+ installMethod: 'custom',
83
+ mainPath: fullPath,
84
+ installedDate: undefined,
85
+ });
48
86
  }
49
87
  }
50
88
 
51
89
  return apps;
52
90
  }
53
91
 
54
- private async detectInstallMethod(binaryPath: string): Promise<string> {
92
+ private async isSystemBinary(binaryName: string, binaryPath: string): Promise<boolean> {
55
93
  try {
56
94
  const content = readFile(binaryPath);
57
- if (!content) return 'unknown';
58
-
59
- // Check for common shebangs/markers
60
- if (content.includes('node') || content.includes('#!/usr/bin/env node')) {
61
- return 'custom'; // Custom node script
62
- }
63
-
64
- if (content.includes('python') || content.includes('#!/usr/bin/env python')) {
65
- return 'custom'; // Custom python script
95
+ if (!content) {
96
+ // Binary files don't have text content - likely compiled system binaries
97
+ // We'll only accept text-based scripts (node, python, bash)
98
+ return true;
66
99
  }
67
100
 
68
- if (content.startsWith('#!/bin/bash') || content.startsWith('#!/bin/sh')) {
69
- return 'custom'; // Shell script
101
+ // Check for common shebangs/markers that indicate user scripts
102
+ if (content.includes('#!/usr/bin/env node') ||
103
+ content.includes('#!/usr/bin/env python') ||
104
+ content.includes('#!/bin/bash') ||
105
+ content.includes('#!/bin/sh') ||
106
+ content.includes('#!/usr/bin/env python3') ||
107
+ content.includes('#!/usr/bin/perl')) {
108
+ return false; // It's a user script
70
109
  }
71
110
 
72
- return 'custom';
111
+ // If we can't identify it, assume it's a system binary
112
+ return true;
73
113
  } catch {
74
- return 'unknown';
114
+ // If we can't read it, assume it's a system binary
115
+ return true;
75
116
  }
76
117
  }
77
118
 
@@ -1,9 +1,9 @@
1
1
  import { execSync } from 'child_process';
2
2
  import path from 'path';
3
- import { getHomeDir } from '../utils/platform';
4
- import { pathExists, listDirectory } from '../utils/filesystem';
3
+ import { getHomeDir } from '../utils/platform.js';
4
+ import { pathExists, listDirectory } from '../utils/filesystem.js';
5
5
  import { InstalledApp, ArtifactPath } from '../types';
6
- import { Logger } from '../utils/logger';
6
+ import { Logger } from '../utils/logger.js';
7
7
 
8
8
  export class LinuxManager {
9
9
  private packageManager: 'apt' | 'yum' | 'dnf' | 'unknown' = 'unknown';