clavix 7.2.2 → 7.3.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,56 @@
1
+ /**
2
+ * Cleaner module for executing deletions with safety guards
3
+ */
4
+ import type { BatchInfo, CleanResult } from './types.js';
5
+ /**
6
+ * Cleaner class for executing deletions safely
7
+ */
8
+ export declare class Cleaner {
9
+ /**
10
+ * Clean a batch of targets
11
+ * @param batch - The batch information with targets to delete
12
+ * @param dryRun - If true, only simulate deletions
13
+ * @returns CleanResult with statistics
14
+ */
15
+ static cleanBatch(batch: BatchInfo, dryRun?: boolean): Promise<CleanResult>;
16
+ /**
17
+ * Clean all global Clavix artifacts
18
+ * @param dryRun - If true, only simulate deletions
19
+ * @returns CleanResult with statistics
20
+ */
21
+ static cleanGlobal(dryRun?: boolean): Promise<CleanResult>;
22
+ /**
23
+ * Clean global integrations from config
24
+ */
25
+ private static cleanGlobalIntegrations;
26
+ /**
27
+ * Clean files matching a specific integration pattern
28
+ */
29
+ private static cleanPattern;
30
+ /**
31
+ * Clean files matching a glob pattern
32
+ * @param pattern - Glob pattern with ~ support
33
+ * @param result - Result object to update
34
+ * @param dryRun - If true, only simulate deletions
35
+ */
36
+ private static cleanGlob;
37
+ /**
38
+ * Clean a specific directory
39
+ * @param dirPath - Directory path with ~ support
40
+ * @param result - Result object to update
41
+ * @param dryRun - If true, only simulate deletions
42
+ */
43
+ private static cleanDirectory;
44
+ /**
45
+ * Clean orphaned backup files created by DocInjector
46
+ * @param dryRun - If true, only simulate deletions
47
+ */
48
+ private static cleanBackups;
49
+ /**
50
+ * Recursively find and remove backup files
51
+ * @param dirPath - Directory to search
52
+ * @param dryRun - If true, only simulate deletions
53
+ */
54
+ private static findAndRemoveBackups;
55
+ }
56
+ //# sourceMappingURL=cleaner.d.ts.map
@@ -0,0 +1,266 @@
1
+ /**
2
+ * Cleaner module for executing deletions with safety guards
3
+ */
4
+ import path from 'node:path';
5
+ import os from 'node:os';
6
+ import { DocInjector } from '../core/doc-injector.js';
7
+ import { FileSystem } from '../utils/file-system.js';
8
+ import { CLAVIX_BLOCK_START, CLAVIX_BLOCK_END } from '../constants.js';
9
+ import { Discovery } from './discovery.js';
10
+ /**
11
+ * Cleaner class for executing deletions safely
12
+ */
13
+ export class Cleaner {
14
+ /**
15
+ * Clean a batch of targets
16
+ * @param batch - The batch information with targets to delete
17
+ * @param dryRun - If true, only simulate deletions
18
+ * @returns CleanResult with statistics
19
+ */
20
+ static async cleanBatch(batch, dryRun = false) {
21
+ const result = { deleted: 0, skipped: 0, errors: [] };
22
+ for (const target of batch.targets) {
23
+ try {
24
+ const fullPath = path.resolve(batch.location, target.path);
25
+ if (dryRun) {
26
+ result.skipped++;
27
+ continue;
28
+ }
29
+ if (target.type === 'directory') {
30
+ // Remove directory recursively
31
+ await FileSystem.remove(fullPath);
32
+ result.deleted++;
33
+ }
34
+ else if (target.type === 'file') {
35
+ // Remove file
36
+ await FileSystem.remove(fullPath);
37
+ result.deleted++;
38
+ }
39
+ else if (target.type === 'block') {
40
+ // Remove managed block from file
41
+ await DocInjector.removeBlock(fullPath, CLAVIX_BLOCK_START, CLAVIX_BLOCK_END);
42
+ result.deleted++;
43
+ // Also try to remove any backup files
44
+ const backupPath = `${fullPath}.backup`;
45
+ if (await FileSystem.exists(backupPath)) {
46
+ await FileSystem.remove(backupPath);
47
+ }
48
+ }
49
+ }
50
+ catch (error) {
51
+ const errorMsg = error instanceof Error ? error.message : String(error);
52
+ result.errors.push(`${target.path}: ${errorMsg}`);
53
+ }
54
+ }
55
+ return result;
56
+ }
57
+ /**
58
+ * Clean all global Clavix artifacts
59
+ * @param dryRun - If true, only simulate deletions
60
+ * @returns CleanResult with statistics
61
+ */
62
+ static async cleanGlobal(dryRun = false) {
63
+ const result = { deleted: 0, skipped: 0, errors: [] };
64
+ // 1. Clean agent skills
65
+ await this.cleanGlob('~/.config/agents/skills/clavix-*', result, dryRun);
66
+ // 2. Clean global integrations from config
67
+ await this.cleanGlobalIntegrations(result, dryRun);
68
+ // 3. Remove orphaned backup files
69
+ await this.cleanBackups(dryRun);
70
+ return result;
71
+ }
72
+ /**
73
+ * Clean global integrations from config
74
+ */
75
+ static async cleanGlobalIntegrations(result, dryRun) {
76
+ // Load integration configs
77
+ const fs = await import('node:fs/promises');
78
+ const configPath = path.join(process.cwd(), 'src/config/integrations.json');
79
+ let integrations = [];
80
+ try {
81
+ const config = JSON.parse(await fs.readFile(configPath, 'utf-8'));
82
+ integrations = config.integrations || [];
83
+ }
84
+ catch {
85
+ return;
86
+ }
87
+ for (const integration of integrations) {
88
+ // Only process global integrations
89
+ if (!integration.global)
90
+ continue;
91
+ const targetPath = integration.directory.startsWith('~')
92
+ ? integration.directory.replace('~', os.homedir())
93
+ : path.join(os.homedir(), integration.directory);
94
+ // Find and clean files matching pattern
95
+ await this.cleanPattern(targetPath, integration, result, dryRun);
96
+ }
97
+ }
98
+ /**
99
+ * Clean files matching a specific integration pattern
100
+ */
101
+ static async cleanPattern(dirPath, integration, result, dryRun) {
102
+ const fs = await import('node:fs/promises');
103
+ try {
104
+ const dir = await fs.opendir(dirPath);
105
+ for await (const entry of dir) {
106
+ if (!entry.isFile())
107
+ continue;
108
+ if (Discovery.fileMatchesIntegration(entry.name, integration)) {
109
+ const fullPath = path.join(dirPath, entry.name);
110
+ if (dryRun) {
111
+ result.skipped++;
112
+ }
113
+ else {
114
+ await FileSystem.remove(fullPath);
115
+ result.deleted++;
116
+ }
117
+ }
118
+ }
119
+ }
120
+ catch {
121
+ // Directory doesn't exist
122
+ }
123
+ }
124
+ /**
125
+ * Clean files matching a glob pattern
126
+ * @param pattern - Glob pattern with ~ support
127
+ * @param result - Result object to update
128
+ * @param dryRun - If true, only simulate deletions
129
+ */
130
+ static async cleanGlob(pattern, result, dryRun) {
131
+ const fs = await import('node:fs/promises');
132
+ const expandedPattern = pattern.startsWith('~') ? pattern.replace('~', os.homedir()) : pattern;
133
+ // For simple patterns, just check single file/directory
134
+ try {
135
+ const stats = await fs.stat(expandedPattern);
136
+ if (stats.isDirectory()) {
137
+ const files = await fs.readdir(expandedPattern);
138
+ for (const file of files) {
139
+ const fullPath = path.join(expandedPattern, file);
140
+ if (dryRun) {
141
+ result.skipped++;
142
+ }
143
+ else {
144
+ await FileSystem.remove(fullPath);
145
+ result.deleted++;
146
+ }
147
+ }
148
+ // Also remove the directory if it's empty
149
+ const remainingFiles = await fs.readdir(expandedPattern);
150
+ if (remainingFiles.length === 0 && !dryRun) {
151
+ await fs.rm(expandedPattern, { recursive: true, force: true });
152
+ }
153
+ }
154
+ else {
155
+ if (dryRun) {
156
+ result.skipped++;
157
+ }
158
+ else {
159
+ await FileSystem.remove(expandedPattern);
160
+ result.deleted++;
161
+ }
162
+ }
163
+ }
164
+ catch {
165
+ // Pattern doesn't exist, skip
166
+ }
167
+ // Also handle glob patterns (simplified - check for clavix-* matches)
168
+ if (pattern.includes('*')) {
169
+ const dirPattern = path.dirname(expandedPattern);
170
+ const filePattern = path.basename(expandedPattern.replace('*', ''));
171
+ try {
172
+ const files = await fs.readdir(dirPattern);
173
+ for (const file of files) {
174
+ if (file.startsWith(filePattern) && file !== filePattern) {
175
+ const fullPath = path.join(dirPattern, file);
176
+ if (dryRun) {
177
+ result.skipped++;
178
+ }
179
+ else {
180
+ await FileSystem.remove(fullPath);
181
+ result.deleted++;
182
+ }
183
+ }
184
+ }
185
+ }
186
+ catch {
187
+ // Directory doesn't exist
188
+ }
189
+ }
190
+ }
191
+ /**
192
+ * Clean a specific directory
193
+ * @param dirPath - Directory path with ~ support
194
+ * @param result - Result object to update
195
+ * @param dryRun - If true, only simulate deletions
196
+ */
197
+ static async cleanDirectory(dirPath, result, dryRun) {
198
+ const fs = await import('node:fs/promises');
199
+ const expandedPath = dirPath.startsWith('~') ? dirPath.replace('~', os.homedir()) : dirPath;
200
+ try {
201
+ if (await FileSystem.exists(expandedPath)) {
202
+ // Count items first
203
+ const files = await fs.readdir(expandedPath);
204
+ const itemCount = files.length;
205
+ if (dryRun) {
206
+ result.skipped += itemCount;
207
+ return;
208
+ }
209
+ // Remove recursively
210
+ await FileSystem.remove(expandedPath);
211
+ result.deleted += itemCount;
212
+ }
213
+ }
214
+ catch (error) {
215
+ const errorMsg = error instanceof Error ? error.message : String(error);
216
+ result.errors.push(`${dirPath}: ${errorMsg}`);
217
+ }
218
+ }
219
+ /**
220
+ * Clean orphaned backup files created by DocInjector
221
+ * @param dryRun - If true, only simulate deletions
222
+ */
223
+ static async cleanBackups(dryRun = false) {
224
+ const cleaned = 0;
225
+ // Check common project locations for backup files
226
+ const backupLocations = ['~/Documents', '~/Projects', '~/dev', '~/workspace'];
227
+ for (const location of backupLocations) {
228
+ const expandedDir = location.startsWith('~') ? location.replace('~', os.homedir()) : location;
229
+ try {
230
+ await this.findAndRemoveBackups(expandedDir, dryRun);
231
+ }
232
+ catch {
233
+ // Skip locations we can't access
234
+ }
235
+ }
236
+ return cleaned;
237
+ }
238
+ /**
239
+ * Recursively find and remove backup files
240
+ * @param dirPath - Directory to search
241
+ * @param dryRun - If true, only simulate deletions
242
+ */
243
+ static async findAndRemoveBackups(dirPath, dryRun = false) {
244
+ const fs = await import('node:fs/promises');
245
+ try {
246
+ const entries = await fs.readdir(dirPath, { withFileTypes: true });
247
+ for (const entry of entries) {
248
+ const fullPath = path.join(dirPath, entry.name);
249
+ if (entry.isDirectory()) {
250
+ // Recurse into subdirectories
251
+ await this.findAndRemoveBackups(fullPath, dryRun);
252
+ }
253
+ else if (entry.name.endsWith('.backup')) {
254
+ // Remove backup file
255
+ if (!dryRun) {
256
+ await FileSystem.remove(fullPath);
257
+ }
258
+ }
259
+ }
260
+ }
261
+ catch {
262
+ // Skip directories we can't read
263
+ }
264
+ }
265
+ }
266
+ //# sourceMappingURL=cleaner.js.map
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Discovery module for finding Clavix artifacts
3
+ */
4
+ import type { CleanTarget } from './types.js';
5
+ /**
6
+ * Discovery class for finding Clavix artifacts in a project
7
+ */
8
+ export declare class Discovery {
9
+ /**
10
+ * Find all Clavix artifacts in a specific location
11
+ * @param rootPath - The root path to search
12
+ * @returns Array of CleanTarget objects
13
+ */
14
+ static findArtifacts(rootPath: string): Promise<CleanTarget[]>;
15
+ /**
16
+ * Load integration configs from integrations.json
17
+ */
18
+ private static loadIntegrations;
19
+ /**
20
+ * Find integration generated files dynamically
21
+ */
22
+ private static findIntegrationArtifacts;
23
+ /**
24
+ * Find files matching integration pattern
25
+ */
26
+ private static findMatchingFiles;
27
+ /**
28
+ * Check if filename matches integration's pattern
29
+ */
30
+ static fileMatchesIntegration(filename: string, integration: any): boolean;
31
+ /**
32
+ * Find Clavix skill directories (.skills/clavix-*)
33
+ */
34
+ private static findClavixSkillDirectories;
35
+ /**
36
+ * Find files starting with a specific prefix in a directory
37
+ * @param dirPath - The directory to search
38
+ * @param prefix - The file prefix to match
39
+ * @returns Array of matching file paths
40
+ */
41
+ private static findFilesStartingWith;
42
+ /**
43
+ * Get the size of a file in bytes
44
+ * @param filePath - The file path
45
+ * @returns File size in bytes
46
+ */
47
+ private static getFileSize;
48
+ /**
49
+ * Get the total size of a directory in bytes
50
+ * @param dirPath - The directory path
51
+ * @returns Total size in bytes
52
+ */
53
+ private static getDirectorySize;
54
+ /**
55
+ * Get a description for a file based on its name and location
56
+ * @param filePath - The file path
57
+ * @returns A human-readable description
58
+ */
59
+ private static getFileDescription;
60
+ /**
61
+ * Check if a directory is effectively empty (only contains .clavix)
62
+ * Used to determine if we should also remove empty parent directories
63
+ * @param dirPath - The directory to check
64
+ * @returns true if the directory is considered empty for cleanup purposes
65
+ */
66
+ static isEmptyClavixParent(dirPath: string): Promise<boolean>;
67
+ }
68
+ //# sourceMappingURL=discovery.d.ts.map