appclean 1.8.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 (154) hide show
  1. package/.github/workflows/publish.yml +41 -0
  2. package/.github/workflows/test.yml +37 -0
  3. package/ACTION_CHECKLIST.md +342 -0
  4. package/APPCLEAN_SUMMARY.md +309 -0
  5. package/CHANGELOG.md +205 -0
  6. package/CODE_OF_CONDUCT.md +49 -0
  7. package/CODE_REVIEW_REPORT.md +447 -0
  8. package/COMMUNITY_POSTS.md +307 -0
  9. package/CONTRIBUTING.md +121 -0
  10. package/DEPLOYMENT_GUIDE.md +345 -0
  11. package/DEPLOYMENT_STATUS.md +182 -0
  12. package/EXECUTIVE_REPORT.md +393 -0
  13. package/GITHUB_OPTIMIZATION.md +383 -0
  14. package/INDEX.md +165 -0
  15. package/LICENSE +21 -0
  16. package/MARKETING_SUMMARY.md +352 -0
  17. package/NPM_PACKAGE_OPTIMIZATION.md +281 -0
  18. package/NPM_PUBLISH.md +116 -0
  19. package/PROJECT_SUMMARY.txt +249 -0
  20. package/QUICKSTART.md +219 -0
  21. package/README.md +548 -0
  22. package/SECURITY.md +104 -0
  23. package/SETUP_GITHUB.md +237 -0
  24. package/TESTING_SUMMARY.md +379 -0
  25. package/dist/core/appUpdateChecker.d.ts +23 -0
  26. package/dist/core/appUpdateChecker.d.ts.map +1 -0
  27. package/dist/core/appUpdateChecker.js +159 -0
  28. package/dist/core/appUpdateChecker.js.map +1 -0
  29. package/dist/core/detector.d.ts +13 -0
  30. package/dist/core/detector.d.ts.map +1 -0
  31. package/dist/core/detector.js +99 -0
  32. package/dist/core/detector.js.map +1 -0
  33. package/dist/core/duplicateFileFinder.d.ts +14 -0
  34. package/dist/core/duplicateFileFinder.d.ts.map +1 -0
  35. package/dist/core/duplicateFileFinder.js +80 -0
  36. package/dist/core/duplicateFileFinder.js.map +1 -0
  37. package/dist/core/orphanedDependencyDetector.d.ts +19 -0
  38. package/dist/core/orphanedDependencyDetector.d.ts.map +1 -0
  39. package/dist/core/orphanedDependencyDetector.js +148 -0
  40. package/dist/core/orphanedDependencyDetector.js.map +1 -0
  41. package/dist/core/performanceOptimizer.d.ts +37 -0
  42. package/dist/core/performanceOptimizer.d.ts.map +1 -0
  43. package/dist/core/performanceOptimizer.js +128 -0
  44. package/dist/core/performanceOptimizer.js.map +1 -0
  45. package/dist/core/permissionHandler.d.ts +9 -0
  46. package/dist/core/permissionHandler.d.ts.map +1 -0
  47. package/dist/core/permissionHandler.js +89 -0
  48. package/dist/core/permissionHandler.js.map +1 -0
  49. package/dist/core/pluginSystem.d.ts +39 -0
  50. package/dist/core/pluginSystem.d.ts.map +1 -0
  51. package/dist/core/pluginSystem.js +120 -0
  52. package/dist/core/pluginSystem.js.map +1 -0
  53. package/dist/core/removalRecorder.d.ts +32 -0
  54. package/dist/core/removalRecorder.d.ts.map +1 -0
  55. package/dist/core/removalRecorder.js +79 -0
  56. package/dist/core/removalRecorder.js.map +1 -0
  57. package/dist/core/remover.d.ts +15 -0
  58. package/dist/core/remover.d.ts.map +1 -0
  59. package/dist/core/remover.js +225 -0
  60. package/dist/core/remover.js.map +1 -0
  61. package/dist/core/reportGenerator.d.ts +9 -0
  62. package/dist/core/reportGenerator.d.ts.map +1 -0
  63. package/dist/core/reportGenerator.js +328 -0
  64. package/dist/core/reportGenerator.js.map +1 -0
  65. package/dist/core/scheduledCleanup.d.ts +38 -0
  66. package/dist/core/scheduledCleanup.d.ts.map +1 -0
  67. package/dist/core/scheduledCleanup.js +127 -0
  68. package/dist/core/scheduledCleanup.js.map +1 -0
  69. package/dist/core/serviceFileDetector.d.ts +18 -0
  70. package/dist/core/serviceFileDetector.d.ts.map +1 -0
  71. package/dist/core/serviceFileDetector.js +136 -0
  72. package/dist/core/serviceFileDetector.js.map +1 -0
  73. package/dist/core/verificationModule.d.ts +14 -0
  74. package/dist/core/verificationModule.d.ts.map +1 -0
  75. package/dist/core/verificationModule.js +102 -0
  76. package/dist/core/verificationModule.js.map +1 -0
  77. package/dist/index.d.ts +3 -0
  78. package/dist/index.d.ts.map +1 -0
  79. package/dist/index.js +333 -0
  80. package/dist/index.js.map +1 -0
  81. package/dist/managers/brewManager.d.ts +10 -0
  82. package/dist/managers/brewManager.d.ts.map +1 -0
  83. package/dist/managers/brewManager.js +130 -0
  84. package/dist/managers/brewManager.js.map +1 -0
  85. package/dist/managers/customManager.d.ts +8 -0
  86. package/dist/managers/customManager.d.ts.map +1 -0
  87. package/dist/managers/customManager.js +139 -0
  88. package/dist/managers/customManager.js.map +1 -0
  89. package/dist/managers/linuxManager.d.ts +10 -0
  90. package/dist/managers/linuxManager.d.ts.map +1 -0
  91. package/dist/managers/linuxManager.js +191 -0
  92. package/dist/managers/linuxManager.js.map +1 -0
  93. package/dist/managers/npmManager.d.ts +10 -0
  94. package/dist/managers/npmManager.d.ts.map +1 -0
  95. package/dist/managers/npmManager.js +119 -0
  96. package/dist/managers/npmManager.js.map +1 -0
  97. package/dist/types/index.d.ts +44 -0
  98. package/dist/types/index.d.ts.map +1 -0
  99. package/dist/types/index.js +3 -0
  100. package/dist/types/index.js.map +1 -0
  101. package/dist/ui/guiServer.d.ts +10 -0
  102. package/dist/ui/guiServer.d.ts.map +1 -0
  103. package/dist/ui/guiServer.js +134 -0
  104. package/dist/ui/guiServer.js.map +1 -0
  105. package/dist/ui/menu.d.ts +6 -0
  106. package/dist/ui/menu.d.ts.map +1 -0
  107. package/dist/ui/menu.js +93 -0
  108. package/dist/ui/menu.js.map +1 -0
  109. package/dist/ui/prompts.d.ts +13 -0
  110. package/dist/ui/prompts.d.ts.map +1 -0
  111. package/dist/ui/prompts.js +161 -0
  112. package/dist/ui/prompts.js.map +1 -0
  113. package/dist/utils/filesystem.d.ts +13 -0
  114. package/dist/utils/filesystem.d.ts.map +1 -0
  115. package/dist/utils/filesystem.js +152 -0
  116. package/dist/utils/filesystem.js.map +1 -0
  117. package/dist/utils/logger.d.ts +12 -0
  118. package/dist/utils/logger.d.ts.map +1 -0
  119. package/dist/utils/logger.js +49 -0
  120. package/dist/utils/logger.js.map +1 -0
  121. package/dist/utils/platform.d.ts +9 -0
  122. package/dist/utils/platform.d.ts.map +1 -0
  123. package/dist/utils/platform.js +75 -0
  124. package/dist/utils/platform.js.map +1 -0
  125. package/jest.config.js +20 -0
  126. package/logo.svg +60 -0
  127. package/package.json +55 -0
  128. package/setup-github.sh +125 -0
  129. package/src/core/appUpdateChecker.ts +220 -0
  130. package/src/core/detector.ts +133 -0
  131. package/src/core/duplicateFileFinder.ts +113 -0
  132. package/src/core/orphanedDependencyDetector.ts +195 -0
  133. package/src/core/performanceOptimizer.ts +209 -0
  134. package/src/core/permissionHandler.ts +121 -0
  135. package/src/core/pluginSystem.ts +194 -0
  136. package/src/core/removalRecorder.ts +146 -0
  137. package/src/core/remover.ts +280 -0
  138. package/src/core/reportGenerator.ts +354 -0
  139. package/src/core/scheduledCleanup.ts +204 -0
  140. package/src/core/serviceFileDetector.ts +181 -0
  141. package/src/core/verificationModule.ts +140 -0
  142. package/src/index.ts +449 -0
  143. package/src/managers/brewManager.ts +149 -0
  144. package/src/managers/customManager.ts +167 -0
  145. package/src/managers/linuxManager.ts +210 -0
  146. package/src/managers/npmManager.ts +137 -0
  147. package/src/types/index.ts +59 -0
  148. package/src/ui/guiServer.ts +155 -0
  149. package/src/ui/menu.ts +100 -0
  150. package/src/ui/prompts.ts +177 -0
  151. package/src/utils/filesystem.ts +145 -0
  152. package/src/utils/logger.ts +48 -0
  153. package/src/utils/platform.ts +75 -0
  154. package/tsconfig.json +20 -0
@@ -0,0 +1,167 @@
1
+ import path from 'path';
2
+ import { getHomeDir } from '../utils/platform';
3
+ import {
4
+ pathExists,
5
+ listDirectory,
6
+ readFile,
7
+ listDirectoryDeep,
8
+ } from '../utils/filesystem';
9
+ import { InstalledApp, ArtifactPath } from '../types';
10
+
11
+ export class CustomManager {
12
+ async findCustomInstalledApps(): Promise<InstalledApp[]> {
13
+ const apps: InstalledApp[] = [];
14
+ const home = getHomeDir();
15
+
16
+ const customBinPaths = [
17
+ '/usr/local/bin',
18
+ '/usr/bin',
19
+ `${home}/.local/bin`,
20
+ ];
21
+
22
+ const foundBinaries = new Set<string>();
23
+
24
+ for (const binPath of customBinPaths) {
25
+ if (!pathExists(binPath)) continue;
26
+
27
+ const binaries = listDirectory(binPath);
28
+ for (const binary of binaries) {
29
+ const fullPath = path.join(binPath, binary);
30
+
31
+ // Skip if already found
32
+ if (foundBinaries.has(binary)) continue;
33
+
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
+ });
47
+ }
48
+ }
49
+ }
50
+
51
+ return apps;
52
+ }
53
+
54
+ private async detectInstallMethod(binaryPath: string): Promise<string> {
55
+ try {
56
+ 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
66
+ }
67
+
68
+ if (content.startsWith('#!/bin/bash') || content.startsWith('#!/bin/sh')) {
69
+ return 'custom'; // Shell script
70
+ }
71
+
72
+ return 'custom';
73
+ } catch {
74
+ return 'unknown';
75
+ }
76
+ }
77
+
78
+ async findArtifacts(appName: string): Promise<ArtifactPath[]> {
79
+ const artifacts: ArtifactPath[] = [];
80
+ const home = getHomeDir();
81
+
82
+ // Search in common binary locations
83
+ const binPaths = [
84
+ `/usr/local/bin/${appName}`,
85
+ `/usr/bin/${appName}`,
86
+ `${home}/.local/bin/${appName}`,
87
+ ];
88
+
89
+ for (const binPath of binPaths) {
90
+ if (pathExists(binPath)) {
91
+ artifacts.push({
92
+ path: binPath,
93
+ type: 'binary',
94
+ size: 0,
95
+ description: 'Custom binary',
96
+ });
97
+ }
98
+ }
99
+
100
+ // Search for config files
101
+ const configPatterns = [
102
+ path.join(home, `.config`, appName),
103
+ path.join(home, `.${appName}`),
104
+ path.join(home, `.${appName}rc`),
105
+ path.join(home, `.${appName}rc.json`),
106
+ ];
107
+
108
+ for (const configPath of configPatterns) {
109
+ if (pathExists(configPath)) {
110
+ artifacts.push({
111
+ path: configPath,
112
+ type: 'config',
113
+ size: 0,
114
+ description: 'Configuration',
115
+ });
116
+ }
117
+ }
118
+
119
+ // Search for data files
120
+ const dataPath = path.join(home, `.local`, `share`, appName);
121
+ if (pathExists(dataPath)) {
122
+ artifacts.push({
123
+ path: dataPath,
124
+ type: 'data',
125
+ size: 0,
126
+ description: 'Data directory',
127
+ });
128
+ }
129
+
130
+ // Search for cache
131
+ const cachePath = path.join(home, `.cache`, appName);
132
+ if (pathExists(cachePath)) {
133
+ artifacts.push({
134
+ path: cachePath,
135
+ type: 'cache',
136
+ size: 0,
137
+ description: 'Cache directory',
138
+ });
139
+ }
140
+
141
+ return artifacts;
142
+ }
143
+
144
+ async searchByName(query: string): Promise<string[]> {
145
+ const results: string[] = [];
146
+ const home = getHomeDir();
147
+
148
+ const binPaths = [
149
+ '/usr/local/bin',
150
+ '/usr/bin',
151
+ `${home}/.local/bin`,
152
+ ];
153
+
154
+ for (const binPath of binPaths) {
155
+ if (!pathExists(binPath)) continue;
156
+
157
+ const binaries = listDirectory(binPath);
158
+ for (const binary of binaries) {
159
+ if (binary.toLowerCase().includes(query.toLowerCase())) {
160
+ results.push(binary);
161
+ }
162
+ }
163
+ }
164
+
165
+ return results;
166
+ }
167
+ }
@@ -0,0 +1,210 @@
1
+ import { execSync } from 'child_process';
2
+ import path from 'path';
3
+ import { getHomeDir } from '../utils/platform';
4
+ import { pathExists, listDirectory } from '../utils/filesystem';
5
+ import { InstalledApp, ArtifactPath } from '../types';
6
+ import { Logger } from '../utils/logger';
7
+
8
+ export class LinuxManager {
9
+ private packageManager: 'apt' | 'yum' | 'dnf' | 'unknown' = 'unknown';
10
+
11
+ constructor() {
12
+ this.packageManager = this.detectPackageManager();
13
+ }
14
+
15
+ private detectPackageManager(): 'apt' | 'yum' | 'dnf' | 'unknown' {
16
+ try {
17
+ execSync('which apt', { stdio: 'ignore' });
18
+ return 'apt';
19
+ } catch {}
20
+
21
+ try {
22
+ execSync('which dnf', { stdio: 'ignore' });
23
+ return 'dnf';
24
+ } catch {}
25
+
26
+ try {
27
+ execSync('which yum', { stdio: 'ignore' });
28
+ return 'yum';
29
+ } catch {}
30
+
31
+ return 'unknown';
32
+ }
33
+
34
+ async getInstalledPackages(): Promise<InstalledApp[]> {
35
+ const packages: InstalledApp[] = [];
36
+
37
+ if (this.packageManager === 'unknown') {
38
+ return packages;
39
+ }
40
+
41
+ try {
42
+ let output: string;
43
+
44
+ if (this.packageManager === 'apt') {
45
+ output = execSync('apt list --installed 2>/dev/null').toString();
46
+ const lines = output.split('\n').slice(1); // Skip header
47
+
48
+ for (const line of lines) {
49
+ if (!line.trim()) continue;
50
+ const parts = line.split('/');
51
+ if (parts[0]) {
52
+ packages.push({
53
+ name: parts[0].trim(),
54
+ version: 'unknown',
55
+ installMethod: 'apt',
56
+ mainPath: '',
57
+ installedDate: undefined,
58
+ });
59
+ }
60
+ }
61
+ } else {
62
+ // yum or dnf
63
+ try {
64
+ output = execSync(`${this.packageManager} list installed`).toString();
65
+ const lines = output.split('\n');
66
+
67
+ for (const line of lines) {
68
+ if (!line.trim()) continue;
69
+ const parts = line.split(/\s+/);
70
+ if (parts[0]) {
71
+ packages.push({
72
+ name: parts[0].split('.')[0],
73
+ version: parts[1] || 'unknown',
74
+ installMethod: this.packageManager as 'yum' | 'dnf',
75
+ mainPath: '',
76
+ installedDate: undefined,
77
+ });
78
+ }
79
+ }
80
+ } catch (error) {
81
+ Logger.debug(`Failed to query ${this.packageManager}: ` + (error as Error).message);
82
+ }
83
+ }
84
+ } catch (error) {
85
+ Logger.debug('Failed to get system packages: ' + (error as Error).message);
86
+ }
87
+
88
+ return packages;
89
+ }
90
+
91
+ async findArtifacts(appName: string): Promise<ArtifactPath[]> {
92
+ const artifacts: ArtifactPath[] = [];
93
+ const home = getHomeDir();
94
+
95
+ // Binaries
96
+ const binPaths = [
97
+ `/usr/bin/${appName}`,
98
+ `/usr/local/bin/${appName}`,
99
+ `${home}/.local/bin/${appName}`,
100
+ ];
101
+
102
+ for (const binPath of binPaths) {
103
+ if (pathExists(binPath)) {
104
+ artifacts.push({
105
+ path: binPath,
106
+ type: 'binary',
107
+ size: 0,
108
+ description: 'Binary executable',
109
+ });
110
+ }
111
+ }
112
+
113
+ // Config files
114
+ const configPaths = [
115
+ path.join(home, `.config`, appName),
116
+ path.join(home, `.${appName}rc`),
117
+ path.join(home, `.${appName}`),
118
+ `/etc/${appName}`,
119
+ `/etc/${appName}.conf`,
120
+ ];
121
+
122
+ for (const configPath of configPaths) {
123
+ if (pathExists(configPath)) {
124
+ artifacts.push({
125
+ path: configPath,
126
+ type: 'config',
127
+ size: 0,
128
+ description: 'Configuration directory/file',
129
+ });
130
+ }
131
+ }
132
+
133
+ // Cache files
134
+ const cachePaths = [
135
+ path.join(home, `.cache`, appName),
136
+ path.join(home, `.${appName}_cache`),
137
+ ];
138
+
139
+ for (const cachePath of cachePaths) {
140
+ if (pathExists(cachePath)) {
141
+ artifacts.push({
142
+ path: cachePath,
143
+ type: 'cache',
144
+ size: 0,
145
+ description: 'Cache directory',
146
+ });
147
+ }
148
+ }
149
+
150
+ // Data files
151
+ const dataPath = path.join(home, `.local`, `share`, appName);
152
+ if (pathExists(dataPath)) {
153
+ artifacts.push({
154
+ path: dataPath,
155
+ type: 'data',
156
+ size: 0,
157
+ description: 'Data directory',
158
+ });
159
+ }
160
+
161
+ // Log files
162
+ const logPaths = [
163
+ `/var/log/${appName}`,
164
+ `/var/log/${appName}.log`,
165
+ path.join(home, `.local`, `share`, `log`, appName),
166
+ ];
167
+
168
+ for (const logPath of logPaths) {
169
+ if (pathExists(logPath)) {
170
+ artifacts.push({
171
+ path: logPath,
172
+ type: 'log',
173
+ size: 0,
174
+ description: 'Log file/directory',
175
+ });
176
+ }
177
+ }
178
+
179
+ // Systemd services
180
+ const systemdPath = `/etc/systemd/system/${appName}.service`;
181
+ if (pathExists(systemdPath)) {
182
+ artifacts.push({
183
+ path: systemdPath,
184
+ type: 'service',
185
+ size: 0,
186
+ description: 'Systemd service file',
187
+ });
188
+ }
189
+
190
+ return artifacts;
191
+ }
192
+
193
+ async removePackage(appName: string): Promise<boolean> {
194
+ if (this.packageManager === 'unknown') {
195
+ return false;
196
+ }
197
+
198
+ try {
199
+ const command = this.packageManager === 'apt'
200
+ ? `apt-get remove -y ${appName}`
201
+ : `${this.packageManager} remove -y ${appName}`;
202
+
203
+ execSync(command, { stdio: 'pipe' });
204
+ return true;
205
+ } catch (error) {
206
+ Logger.debug(`Failed to uninstall ${appName}: ` + (error as Error).message);
207
+ return false;
208
+ }
209
+ }
210
+ }
@@ -0,0 +1,137 @@
1
+ import { execSync } from 'child_process';
2
+ import path from 'path';
3
+ import { getHomeDir } from '../utils/platform';
4
+ import { pathExists, listDirectory, readFile } from '../utils/filesystem';
5
+ import { InstalledApp, ArtifactPath } from '../types';
6
+ import { Logger } from '../utils/logger';
7
+
8
+ export class NpmManager {
9
+ private globalNpmPath: string;
10
+
11
+ constructor() {
12
+ this.globalNpmPath = this.getNpmGlobalPath();
13
+ }
14
+
15
+ private getNpmGlobalPath(): string {
16
+ try {
17
+ return execSync('npm config get prefix').toString().trim();
18
+ } catch {
19
+ return path.join(getHomeDir(), '.npm-global');
20
+ }
21
+ }
22
+
23
+ async getInstalledPackages(): Promise<InstalledApp[]> {
24
+ const packages: InstalledApp[] = [];
25
+
26
+ try {
27
+ const output = execSync('npm list -g --json --depth=0').toString();
28
+ const data = JSON.parse(output);
29
+ const dependencies = data.dependencies || {};
30
+
31
+ for (const [name, pkg] of Object.entries(dependencies)) {
32
+ const typedPkg = pkg as any;
33
+ packages.push({
34
+ name,
35
+ version: typedPkg.version || 'unknown',
36
+ installMethod: 'npm',
37
+ mainPath: path.join(this.globalNpmPath, 'lib', 'node_modules', name),
38
+ installedDate: undefined,
39
+ });
40
+ }
41
+ } catch (error) {
42
+ Logger.debug('Failed to get npm packages: ' + (error as Error).message);
43
+ }
44
+
45
+ return packages;
46
+ }
47
+
48
+ async findArtifacts(appName: string): Promise<ArtifactPath[]> {
49
+ const artifacts: ArtifactPath[] = [];
50
+ const home = getHomeDir();
51
+
52
+ // Main package directory
53
+ const packagePath = path.join(
54
+ this.globalNpmPath,
55
+ 'lib',
56
+ 'node_modules',
57
+ appName
58
+ );
59
+ if (pathExists(packagePath)) {
60
+ artifacts.push({
61
+ path: packagePath,
62
+ type: 'other',
63
+ size: 0,
64
+ description: 'Package directory',
65
+ });
66
+ }
67
+
68
+ // Binaries in bin directory
69
+ const binPath = path.join(this.globalNpmPath, 'bin', appName);
70
+ if (pathExists(binPath)) {
71
+ artifacts.push({
72
+ path: binPath,
73
+ type: 'binary',
74
+ size: 0,
75
+ description: 'Executable binary',
76
+ });
77
+ }
78
+
79
+ // Config files
80
+ const configPaths = [
81
+ path.join(home, `.${appName}rc`),
82
+ path.join(home, `.${appName}rc.json`),
83
+ path.join(home, `.config`, appName),
84
+ ];
85
+
86
+ for (const configPath of configPaths) {
87
+ if (pathExists(configPath)) {
88
+ artifacts.push({
89
+ path: configPath,
90
+ type: 'config',
91
+ size: 0,
92
+ description: `${appName} configuration`,
93
+ });
94
+ }
95
+ }
96
+
97
+ // Cache directories
98
+ const cachePaths = [
99
+ path.join(home, `.cache`, appName),
100
+ path.join(home, `Library`, `Caches`, appName),
101
+ ];
102
+
103
+ for (const cachePath of cachePaths) {
104
+ if (pathExists(cachePath)) {
105
+ artifacts.push({
106
+ path: cachePath,
107
+ type: 'cache',
108
+ size: 0,
109
+ description: `${appName} cache`,
110
+ });
111
+ }
112
+ }
113
+
114
+ // Data directories
115
+ const dataPath = path.join(home, `.local`, `share`, appName);
116
+ if (pathExists(dataPath)) {
117
+ artifacts.push({
118
+ path: dataPath,
119
+ type: 'data',
120
+ size: 0,
121
+ description: `${appName} data files`,
122
+ });
123
+ }
124
+
125
+ return artifacts;
126
+ }
127
+
128
+ async removePackage(appName: string): Promise<boolean> {
129
+ try {
130
+ execSync(`npm uninstall -g ${appName}`);
131
+ return true;
132
+ } catch (error) {
133
+ Logger.debug('Failed to uninstall npm package: ' + (error as Error).message);
134
+ return false;
135
+ }
136
+ }
137
+ }
@@ -0,0 +1,59 @@
1
+ export type Platform = 'darwin' | 'linux' | 'win32';
2
+
3
+ export type InstallMethod =
4
+ | 'npm'
5
+ | 'yarn'
6
+ | 'pnpm'
7
+ | 'brew'
8
+ | 'apt'
9
+ | 'yum'
10
+ | 'dnf'
11
+ | 'custom'
12
+ | 'unknown';
13
+
14
+ export interface InstalledApp {
15
+ name: string;
16
+ version: string;
17
+ installMethod: InstallMethod;
18
+ mainPath: string;
19
+ installedDate?: Date;
20
+ size?: number;
21
+ }
22
+
23
+ export interface ArtifactPath {
24
+ path: string;
25
+ type: 'binary' | 'config' | 'cache' | 'log' | 'data' | 'service' | 'other';
26
+ size: number;
27
+ description: string;
28
+ }
29
+
30
+ export interface AppAnalysis {
31
+ app: InstalledApp;
32
+ artifacts: ArtifactPath[];
33
+ totalSize: number;
34
+ dependents?: string[];
35
+ }
36
+
37
+ export interface RemovalOptions {
38
+ dryRun?: boolean;
39
+ createBackup?: boolean;
40
+ backupPath?: string;
41
+ force?: boolean;
42
+ userConsent?: boolean;
43
+ reportFormat?: 'html' | 'text';
44
+ }
45
+
46
+ export interface RemovalResult {
47
+ success: boolean;
48
+ appName: string;
49
+ removedFiles: number;
50
+ freedSpace: number;
51
+ backupPath?: string;
52
+ errors?: string[];
53
+ }
54
+
55
+ export interface SearchOptions {
56
+ query?: string;
57
+ installMethod?: InstallMethod;
58
+ sortBy?: 'name' | 'date' | 'size';
59
+ }