@compilr-dev/agents 0.0.1

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 (160) hide show
  1. package/README.md +1277 -0
  2. package/dist/agent.d.ts +1272 -0
  3. package/dist/agent.js +1912 -0
  4. package/dist/anchors/builtin.d.ts +24 -0
  5. package/dist/anchors/builtin.js +61 -0
  6. package/dist/anchors/index.d.ts +6 -0
  7. package/dist/anchors/index.js +5 -0
  8. package/dist/anchors/manager.d.ts +115 -0
  9. package/dist/anchors/manager.js +412 -0
  10. package/dist/anchors/types.d.ts +168 -0
  11. package/dist/anchors/types.js +10 -0
  12. package/dist/context/index.d.ts +12 -0
  13. package/dist/context/index.js +10 -0
  14. package/dist/context/manager.d.ts +224 -0
  15. package/dist/context/manager.js +770 -0
  16. package/dist/context/types.d.ts +377 -0
  17. package/dist/context/types.js +7 -0
  18. package/dist/costs/index.d.ts +8 -0
  19. package/dist/costs/index.js +7 -0
  20. package/dist/costs/tracker.d.ts +121 -0
  21. package/dist/costs/tracker.js +295 -0
  22. package/dist/costs/types.d.ts +157 -0
  23. package/dist/costs/types.js +8 -0
  24. package/dist/errors.d.ts +178 -0
  25. package/dist/errors.js +249 -0
  26. package/dist/guardrails/builtin.d.ts +27 -0
  27. package/dist/guardrails/builtin.js +223 -0
  28. package/dist/guardrails/index.d.ts +6 -0
  29. package/dist/guardrails/index.js +5 -0
  30. package/dist/guardrails/manager.d.ts +117 -0
  31. package/dist/guardrails/manager.js +288 -0
  32. package/dist/guardrails/types.d.ts +159 -0
  33. package/dist/guardrails/types.js +7 -0
  34. package/dist/hooks/index.d.ts +31 -0
  35. package/dist/hooks/index.js +29 -0
  36. package/dist/hooks/manager.d.ts +147 -0
  37. package/dist/hooks/manager.js +600 -0
  38. package/dist/hooks/types.d.ts +368 -0
  39. package/dist/hooks/types.js +12 -0
  40. package/dist/index.d.ts +45 -0
  41. package/dist/index.js +73 -0
  42. package/dist/mcp/client.d.ts +93 -0
  43. package/dist/mcp/client.js +287 -0
  44. package/dist/mcp/errors.d.ts +60 -0
  45. package/dist/mcp/errors.js +78 -0
  46. package/dist/mcp/index.d.ts +43 -0
  47. package/dist/mcp/index.js +45 -0
  48. package/dist/mcp/manager.d.ts +120 -0
  49. package/dist/mcp/manager.js +276 -0
  50. package/dist/mcp/tools.d.ts +54 -0
  51. package/dist/mcp/tools.js +99 -0
  52. package/dist/mcp/types.d.ts +150 -0
  53. package/dist/mcp/types.js +40 -0
  54. package/dist/memory/index.d.ts +8 -0
  55. package/dist/memory/index.js +7 -0
  56. package/dist/memory/loader.d.ts +114 -0
  57. package/dist/memory/loader.js +463 -0
  58. package/dist/memory/types.d.ts +182 -0
  59. package/dist/memory/types.js +8 -0
  60. package/dist/messages/index.d.ts +82 -0
  61. package/dist/messages/index.js +155 -0
  62. package/dist/permissions/index.d.ts +5 -0
  63. package/dist/permissions/index.js +4 -0
  64. package/dist/permissions/manager.d.ts +125 -0
  65. package/dist/permissions/manager.js +379 -0
  66. package/dist/permissions/types.d.ts +162 -0
  67. package/dist/permissions/types.js +7 -0
  68. package/dist/providers/claude.d.ts +90 -0
  69. package/dist/providers/claude.js +348 -0
  70. package/dist/providers/index.d.ts +8 -0
  71. package/dist/providers/index.js +11 -0
  72. package/dist/providers/mock.d.ts +133 -0
  73. package/dist/providers/mock.js +204 -0
  74. package/dist/providers/types.d.ts +168 -0
  75. package/dist/providers/types.js +4 -0
  76. package/dist/rate-limit/index.d.ts +45 -0
  77. package/dist/rate-limit/index.js +47 -0
  78. package/dist/rate-limit/limiter.d.ts +104 -0
  79. package/dist/rate-limit/limiter.js +326 -0
  80. package/dist/rate-limit/provider-wrapper.d.ts +112 -0
  81. package/dist/rate-limit/provider-wrapper.js +201 -0
  82. package/dist/rate-limit/retry.d.ts +108 -0
  83. package/dist/rate-limit/retry.js +287 -0
  84. package/dist/rate-limit/types.d.ts +181 -0
  85. package/dist/rate-limit/types.js +22 -0
  86. package/dist/rehearsal/file-analyzer.d.ts +22 -0
  87. package/dist/rehearsal/file-analyzer.js +351 -0
  88. package/dist/rehearsal/git-analyzer.d.ts +22 -0
  89. package/dist/rehearsal/git-analyzer.js +472 -0
  90. package/dist/rehearsal/index.d.ts +35 -0
  91. package/dist/rehearsal/index.js +36 -0
  92. package/dist/rehearsal/manager.d.ts +100 -0
  93. package/dist/rehearsal/manager.js +290 -0
  94. package/dist/rehearsal/types.d.ts +235 -0
  95. package/dist/rehearsal/types.js +8 -0
  96. package/dist/skills/index.d.ts +160 -0
  97. package/dist/skills/index.js +282 -0
  98. package/dist/state/agent-state.d.ts +41 -0
  99. package/dist/state/agent-state.js +88 -0
  100. package/dist/state/checkpointer.d.ts +110 -0
  101. package/dist/state/checkpointer.js +362 -0
  102. package/dist/state/errors.d.ts +66 -0
  103. package/dist/state/errors.js +88 -0
  104. package/dist/state/index.d.ts +35 -0
  105. package/dist/state/index.js +37 -0
  106. package/dist/state/serializer.d.ts +55 -0
  107. package/dist/state/serializer.js +172 -0
  108. package/dist/state/types.d.ts +312 -0
  109. package/dist/state/types.js +14 -0
  110. package/dist/tools/builtin/bash-output.d.ts +61 -0
  111. package/dist/tools/builtin/bash-output.js +90 -0
  112. package/dist/tools/builtin/bash.d.ts +150 -0
  113. package/dist/tools/builtin/bash.js +354 -0
  114. package/dist/tools/builtin/edit.d.ts +50 -0
  115. package/dist/tools/builtin/edit.js +215 -0
  116. package/dist/tools/builtin/glob.d.ts +62 -0
  117. package/dist/tools/builtin/glob.js +244 -0
  118. package/dist/tools/builtin/grep.d.ts +74 -0
  119. package/dist/tools/builtin/grep.js +363 -0
  120. package/dist/tools/builtin/index.d.ts +44 -0
  121. package/dist/tools/builtin/index.js +69 -0
  122. package/dist/tools/builtin/kill-shell.d.ts +44 -0
  123. package/dist/tools/builtin/kill-shell.js +80 -0
  124. package/dist/tools/builtin/read-file.d.ts +57 -0
  125. package/dist/tools/builtin/read-file.js +184 -0
  126. package/dist/tools/builtin/shell-manager.d.ts +176 -0
  127. package/dist/tools/builtin/shell-manager.js +337 -0
  128. package/dist/tools/builtin/task.d.ts +202 -0
  129. package/dist/tools/builtin/task.js +350 -0
  130. package/dist/tools/builtin/todo.d.ts +207 -0
  131. package/dist/tools/builtin/todo.js +453 -0
  132. package/dist/tools/builtin/utils.d.ts +27 -0
  133. package/dist/tools/builtin/utils.js +70 -0
  134. package/dist/tools/builtin/web-fetch.d.ts +96 -0
  135. package/dist/tools/builtin/web-fetch.js +290 -0
  136. package/dist/tools/builtin/write-file.d.ts +54 -0
  137. package/dist/tools/builtin/write-file.js +147 -0
  138. package/dist/tools/define.d.ts +60 -0
  139. package/dist/tools/define.js +65 -0
  140. package/dist/tools/index.d.ts +10 -0
  141. package/dist/tools/index.js +37 -0
  142. package/dist/tools/registry.d.ts +79 -0
  143. package/dist/tools/registry.js +151 -0
  144. package/dist/tools/types.d.ts +59 -0
  145. package/dist/tools/types.js +4 -0
  146. package/dist/tracing/hooks.d.ts +58 -0
  147. package/dist/tracing/hooks.js +377 -0
  148. package/dist/tracing/index.d.ts +51 -0
  149. package/dist/tracing/index.js +55 -0
  150. package/dist/tracing/logging.d.ts +78 -0
  151. package/dist/tracing/logging.js +310 -0
  152. package/dist/tracing/manager.d.ts +160 -0
  153. package/dist/tracing/manager.js +468 -0
  154. package/dist/tracing/otel.d.ts +102 -0
  155. package/dist/tracing/otel.js +246 -0
  156. package/dist/tracing/types.d.ts +346 -0
  157. package/dist/tracing/types.js +38 -0
  158. package/dist/utils/index.d.ts +23 -0
  159. package/dist/utils/index.js +44 -0
  160. package/package.json +79 -0
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Rate Limiting & Retry Types
3
+ *
4
+ * Provides types for rate limiting and automatic retry functionality.
5
+ */
6
+ /**
7
+ * Configuration for rate limiter
8
+ */
9
+ export interface RateLimiterConfig {
10
+ /**
11
+ * Maximum requests per minute
12
+ * @default 60
13
+ */
14
+ requestsPerMinute?: number;
15
+ /**
16
+ * Maximum tokens per minute (for token-based limiting)
17
+ * Set to 0 to disable token-based limiting
18
+ * @default 0
19
+ */
20
+ tokensPerMinute?: number;
21
+ /**
22
+ * Maximum concurrent requests
23
+ * Set to 0 for unlimited
24
+ * @default 0
25
+ */
26
+ maxConcurrent?: number;
27
+ /**
28
+ * Whether to throw immediately when rate limited instead of waiting
29
+ * @default false
30
+ */
31
+ throwOnLimit?: boolean;
32
+ }
33
+ /**
34
+ * Rate limiter statistics
35
+ */
36
+ export interface RateLimiterStats {
37
+ /**
38
+ * Current number of available request tokens
39
+ */
40
+ availableRequests: number;
41
+ /**
42
+ * Current number of available LLM tokens
43
+ */
44
+ availableTokens: number;
45
+ /**
46
+ * Current number of concurrent requests
47
+ */
48
+ currentConcurrent: number;
49
+ /**
50
+ * Total requests made
51
+ */
52
+ totalRequests: number;
53
+ /**
54
+ * Total tokens consumed
55
+ */
56
+ totalTokens: number;
57
+ /**
58
+ * Number of times rate limit was hit
59
+ */
60
+ rateLimitHits: number;
61
+ /**
62
+ * Total time spent waiting due to rate limits (ms)
63
+ */
64
+ totalWaitTimeMs: number;
65
+ }
66
+ /**
67
+ * Configuration for automatic retry
68
+ */
69
+ export interface RetryConfig {
70
+ /**
71
+ * Maximum number of retry attempts
72
+ * @default 3
73
+ */
74
+ maxRetries?: number;
75
+ /**
76
+ * Base delay between retries in milliseconds
77
+ * @default 1000
78
+ */
79
+ baseDelayMs?: number;
80
+ /**
81
+ * Maximum delay between retries in milliseconds
82
+ * @default 60000
83
+ */
84
+ maxDelayMs?: number;
85
+ /**
86
+ * Multiplier for exponential backoff
87
+ * @default 2
88
+ */
89
+ backoffMultiplier?: number;
90
+ /**
91
+ * Whether to add random jitter to delays
92
+ * @default true
93
+ */
94
+ jitter?: boolean;
95
+ /**
96
+ * Custom function to determine if an error is retryable
97
+ * If not provided, uses default logic (429, 5xx, connection errors)
98
+ */
99
+ isRetryable?: (error: Error) => boolean;
100
+ /**
101
+ * Callback invoked before each retry attempt
102
+ */
103
+ onRetry?: (attempt: number, error: Error, delayMs: number) => void;
104
+ }
105
+ /**
106
+ * Combined rate limit and retry configuration
107
+ */
108
+ export interface RateLimitRetryConfig {
109
+ /**
110
+ * Rate limiter configuration
111
+ */
112
+ rateLimit?: RateLimiterConfig;
113
+ /**
114
+ * Retry configuration
115
+ */
116
+ retry?: RetryConfig;
117
+ }
118
+ /**
119
+ * Result of acquiring a rate limit token
120
+ */
121
+ export interface AcquireResult {
122
+ /**
123
+ * Whether the acquisition was successful
124
+ */
125
+ acquired: boolean;
126
+ /**
127
+ * Time waited to acquire the token (ms)
128
+ */
129
+ waitedMs: number;
130
+ /**
131
+ * If not acquired, the estimated wait time (ms)
132
+ */
133
+ estimatedWaitMs?: number;
134
+ }
135
+ /**
136
+ * Rate limiter interface
137
+ */
138
+ export interface RateLimiter {
139
+ /**
140
+ * Acquire permission to make a request
141
+ * Waits if necessary (unless throwOnLimit is true)
142
+ *
143
+ * @param estimatedTokens - Estimated tokens for the request (optional)
144
+ * @returns Acquire result
145
+ */
146
+ acquire(estimatedTokens?: number): Promise<AcquireResult>;
147
+ /**
148
+ * Release a concurrent request slot
149
+ */
150
+ release(): void;
151
+ /**
152
+ * Report actual token usage after request completes
153
+ * Used to update token bucket
154
+ *
155
+ * @param tokens - Actual tokens used
156
+ */
157
+ reportUsage(tokens: number): void;
158
+ /**
159
+ * Get current rate limiter statistics
160
+ */
161
+ getStats(): RateLimiterStats;
162
+ /**
163
+ * Reset the rate limiter
164
+ */
165
+ reset(): void;
166
+ /**
167
+ * Check if a request can be made immediately
168
+ */
169
+ canAcquire(estimatedTokens?: number): boolean;
170
+ }
171
+ /**
172
+ * Error thrown when rate limit is exceeded and throwOnLimit is true
173
+ */
174
+ export declare class RateLimitExceededError extends Error {
175
+ readonly estimatedWaitMs: number;
176
+ constructor(message: string, estimatedWaitMs: number);
177
+ }
178
+ /**
179
+ * Check if an error is a RateLimitExceededError
180
+ */
181
+ export declare function isRateLimitExceededError(error: unknown): error is RateLimitExceededError;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Rate Limiting & Retry Types
3
+ *
4
+ * Provides types for rate limiting and automatic retry functionality.
5
+ */
6
+ /**
7
+ * Error thrown when rate limit is exceeded and throwOnLimit is true
8
+ */
9
+ export class RateLimitExceededError extends Error {
10
+ estimatedWaitMs;
11
+ constructor(message, estimatedWaitMs) {
12
+ super(message);
13
+ this.estimatedWaitMs = estimatedWaitMs;
14
+ this.name = 'RateLimitExceededError';
15
+ }
16
+ }
17
+ /**
18
+ * Check if an error is a RateLimitExceededError
19
+ */
20
+ export function isRateLimitExceededError(error) {
21
+ return error instanceof RateLimitExceededError;
22
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * File Rehearsal Analyzer
3
+ *
4
+ * Analyzes destructive file operations and provides impact assessment.
5
+ * Handles: rm, unlink, rmdir, file deletions, overwrites
6
+ */
7
+ import type { RehearsalAnalyzer, RehearsalResult, RehearsalContext } from './types.js';
8
+ /**
9
+ * File Rehearsal Analyzer
10
+ */
11
+ export declare class FileRehearsalAnalyzer implements RehearsalAnalyzer {
12
+ readonly id = "file-analyzer";
13
+ readonly name = "File Operations Analyzer";
14
+ readonly category: "file";
15
+ readonly patterns: RegExp[];
16
+ canAnalyze(operation: string): boolean;
17
+ analyze(operation: string, context: RehearsalContext): Promise<RehearsalResult>;
18
+ }
19
+ /**
20
+ * Create a File rehearsal analyzer
21
+ */
22
+ export declare function createFileAnalyzer(): FileRehearsalAnalyzer;
@@ -0,0 +1,351 @@
1
+ /**
2
+ * File Rehearsal Analyzer
3
+ *
4
+ * Analyzes destructive file operations and provides impact assessment.
5
+ * Handles: rm, unlink, rmdir, file deletions, overwrites
6
+ */
7
+ import * as fs from 'node:fs';
8
+ import * as path from 'node:path';
9
+ /**
10
+ * Patterns for destructive file operations
11
+ */
12
+ const FILE_DESTRUCTIVE_PATTERNS = [
13
+ // rm commands
14
+ /^rm\s+/i,
15
+ /^rm\s+-rf?\s+/i,
16
+ /^rm\s+-fr?\s+/i,
17
+ // rmdir
18
+ /^rmdir\s+/i,
19
+ // unlink (Node.js)
20
+ /fs\.unlink/i,
21
+ /fs\.rm/i,
22
+ /fs\.rmdir/i,
23
+ // Truncate operations
24
+ />\s*[^|]/, // redirect that truncates
25
+ /truncate\s+/i,
26
+ // Move/overwrite
27
+ /^mv\s+.*\s+\S+$/i, // mv can overwrite
28
+ /^cp\s+.*-f/i, // cp -f overwrites
29
+ ];
30
+ /**
31
+ * Get file stats safely
32
+ */
33
+ function getFileStats(filePath) {
34
+ try {
35
+ return fs.statSync(filePath);
36
+ }
37
+ catch {
38
+ return null;
39
+ }
40
+ }
41
+ /**
42
+ * Count lines in a file
43
+ */
44
+ function countLines(filePath) {
45
+ try {
46
+ const content = fs.readFileSync(filePath, 'utf-8');
47
+ return content.split('\n').length;
48
+ }
49
+ catch {
50
+ return 0;
51
+ }
52
+ }
53
+ /**
54
+ * Get all files in a directory recursively
55
+ */
56
+ function getFilesRecursively(dirPath, maxDepth = 10) {
57
+ const files = [];
58
+ function walk(currentPath, depth) {
59
+ if (depth > maxDepth)
60
+ return;
61
+ try {
62
+ const entries = fs.readdirSync(currentPath, { withFileTypes: true });
63
+ for (const entry of entries) {
64
+ const fullPath = path.join(currentPath, entry.name);
65
+ if (entry.isDirectory()) {
66
+ // Skip node_modules and .git for performance
67
+ if (entry.name === 'node_modules' || entry.name === '.git') {
68
+ continue;
69
+ }
70
+ walk(fullPath, depth + 1);
71
+ }
72
+ else {
73
+ files.push(fullPath);
74
+ }
75
+ }
76
+ }
77
+ catch {
78
+ // Ignore permission errors
79
+ }
80
+ }
81
+ walk(dirPath, 0);
82
+ return files;
83
+ }
84
+ /**
85
+ * Parse file paths from rm command
86
+ */
87
+ function parseRmTargets(operation) {
88
+ // Tokenize the command
89
+ const tokens = [];
90
+ let current = '';
91
+ let inQuote = false;
92
+ let quoteChar = '';
93
+ // Remove the 'rm' prefix
94
+ const withoutRm = operation.replace(/^rm\s+/, '');
95
+ for (const char of withoutRm) {
96
+ if ((char === '"' || char === "'") && !inQuote) {
97
+ inQuote = true;
98
+ quoteChar = char;
99
+ }
100
+ else if (char === quoteChar && inQuote) {
101
+ inQuote = false;
102
+ quoteChar = '';
103
+ }
104
+ else if (char === ' ' && !inQuote) {
105
+ if (current) {
106
+ tokens.push(current);
107
+ current = '';
108
+ }
109
+ }
110
+ else {
111
+ current += char;
112
+ }
113
+ }
114
+ if (current) {
115
+ tokens.push(current);
116
+ }
117
+ // Filter out flags (tokens starting with -)
118
+ const targets = tokens.filter((t) => !t.startsWith('-'));
119
+ return targets;
120
+ }
121
+ /**
122
+ * Expand glob patterns (basic implementation)
123
+ */
124
+ function expandPath(targetPath, cwd) {
125
+ const absolutePath = path.isAbsolute(targetPath) ? targetPath : path.join(cwd, targetPath);
126
+ // If it contains wildcards, we'd need glob - for now, just return the path
127
+ if (targetPath.includes('*') || targetPath.includes('?')) {
128
+ // Basic glob support - just return warning
129
+ return [absolutePath];
130
+ }
131
+ return [absolutePath];
132
+ }
133
+ /**
134
+ * Analyze impact of deleting a path
135
+ */
136
+ function analyzePathDeletion(targetPath, cwd) {
137
+ const absolutePath = path.isAbsolute(targetPath) ? targetPath : path.join(cwd, targetPath);
138
+ const stats = getFileStats(absolutePath);
139
+ if (!stats) {
140
+ return { files: [], totalLines: 0, totalSize: 0 };
141
+ }
142
+ const files = [];
143
+ let totalLines = 0;
144
+ let totalSize = 0;
145
+ if (stats.isDirectory()) {
146
+ const allFiles = getFilesRecursively(absolutePath);
147
+ for (const filePath of allFiles) {
148
+ const fileStats = getFileStats(filePath);
149
+ if (fileStats) {
150
+ const lines = countLines(filePath);
151
+ totalLines += lines;
152
+ totalSize += fileStats.size;
153
+ files.push({
154
+ path: filePath,
155
+ changeType: 'delete',
156
+ linesAffected: lines,
157
+ lastModified: fileStats.mtime,
158
+ size: fileStats.size,
159
+ });
160
+ }
161
+ }
162
+ }
163
+ else {
164
+ const lines = countLines(absolutePath);
165
+ totalLines = lines;
166
+ totalSize = stats.size;
167
+ files.push({
168
+ path: absolutePath,
169
+ changeType: 'delete',
170
+ linesAffected: lines,
171
+ lastModified: stats.mtime,
172
+ size: stats.size,
173
+ });
174
+ }
175
+ return { files, totalLines, totalSize };
176
+ }
177
+ /**
178
+ * Format bytes to human readable
179
+ */
180
+ function formatBytes(bytes) {
181
+ if (bytes < 1024)
182
+ return `${String(bytes)} B`;
183
+ if (bytes < 1024 * 1024)
184
+ return `${(bytes / 1024).toFixed(1)} KB`;
185
+ if (bytes < 1024 * 1024 * 1024)
186
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
187
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
188
+ }
189
+ /**
190
+ * Calculate time investment based on file modification times
191
+ */
192
+ function calculateTimeInvestment(files, _sessionStartTime) {
193
+ if (files.length === 0)
194
+ return undefined;
195
+ const now = new Date();
196
+ const recentFiles = files.filter((f) => {
197
+ if (!f.lastModified)
198
+ return false;
199
+ const hoursSinceModified = (now.getTime() - f.lastModified.getTime()) / (1000 * 60 * 60);
200
+ return hoursSinceModified < 24;
201
+ });
202
+ if (recentFiles.length === 0)
203
+ return undefined;
204
+ // Find the most recent modification
205
+ const mostRecent = recentFiles.reduce((latest, f) => {
206
+ if (!f.lastModified)
207
+ return latest;
208
+ if (!latest || f.lastModified > latest)
209
+ return f.lastModified;
210
+ return latest;
211
+ }, null);
212
+ if (!mostRecent)
213
+ return undefined;
214
+ const hoursSince = (now.getTime() - mostRecent.getTime()) / (1000 * 60 * 60);
215
+ if (hoursSince < 1) {
216
+ return `${String(recentFiles.length)} file(s) modified in the last hour`;
217
+ }
218
+ else if (hoursSince < 24) {
219
+ return `${String(recentFiles.length)} file(s) modified in the last ${String(Math.round(hoursSince))} hours`;
220
+ }
221
+ return undefined;
222
+ }
223
+ /**
224
+ * Determine severity based on impact
225
+ */
226
+ function calculateSeverity(filesAffected, linesAffected, sizeBytes, isRecursive) {
227
+ // Critical: large recursive deletions
228
+ if (isRecursive && (filesAffected > 50 || linesAffected > 5000 || sizeBytes > 10 * 1024 * 1024)) {
229
+ return 'critical';
230
+ }
231
+ // High: significant deletions
232
+ if (filesAffected > 20 || linesAffected > 1000 || sizeBytes > 1024 * 1024) {
233
+ return 'high';
234
+ }
235
+ // Medium: moderate deletions
236
+ if (filesAffected > 5 || linesAffected > 100) {
237
+ return 'medium';
238
+ }
239
+ return 'low';
240
+ }
241
+ /**
242
+ * Determine recommendation based on analysis
243
+ */
244
+ function calculateRecommendation(severity, filesExist) {
245
+ if (!filesExist) {
246
+ return 'proceed'; // Nothing to delete
247
+ }
248
+ if (severity === 'critical') {
249
+ return 'abort';
250
+ }
251
+ if (severity === 'high') {
252
+ return 'confirm';
253
+ }
254
+ if (severity === 'medium') {
255
+ return 'caution';
256
+ }
257
+ return 'proceed';
258
+ }
259
+ /**
260
+ * File Rehearsal Analyzer
261
+ */
262
+ export class FileRehearsalAnalyzer {
263
+ id = 'file-analyzer';
264
+ name = 'File Operations Analyzer';
265
+ category = 'file';
266
+ patterns = FILE_DESTRUCTIVE_PATTERNS;
267
+ canAnalyze(operation) {
268
+ return this.patterns.some((pattern) => pattern.test(operation));
269
+ }
270
+ async analyze(operation, context) {
271
+ await Promise.resolve(); // Ensure async method has await
272
+ const startTime = Date.now();
273
+ const cwd = context.workingDirectory;
274
+ // Detect operation type
275
+ const isRm = /^rm\s+/i.test(operation);
276
+ const isRecursive = /-r/i.test(operation) || /-R/i.test(operation);
277
+ const isForce = /-f/i.test(operation);
278
+ let allFiles = [];
279
+ let totalLines = 0;
280
+ let totalSize = 0;
281
+ const warnings = [];
282
+ if (isRm) {
283
+ const targets = parseRmTargets(operation);
284
+ for (const target of targets) {
285
+ const expandedPaths = expandPath(target, cwd);
286
+ for (const targetPath of expandedPaths) {
287
+ // Check for dangerous paths
288
+ if (targetPath === '/' || targetPath === cwd || targetPath === process.env.HOME) {
289
+ warnings.push(`⚠️ DANGER: Attempting to delete critical path: ${targetPath}`);
290
+ }
291
+ const analysis = analyzePathDeletion(targetPath, cwd);
292
+ allFiles = [...allFiles, ...analysis.files];
293
+ totalLines += analysis.totalLines;
294
+ totalSize += analysis.totalSize;
295
+ }
296
+ }
297
+ if (isRecursive) {
298
+ warnings.push('Recursive deletion (-r) will remove all contents');
299
+ }
300
+ if (isForce) {
301
+ warnings.push('Force flag (-f) will ignore nonexistent files and skip prompts');
302
+ }
303
+ if (operation.includes('*')) {
304
+ warnings.push('Glob pattern (*) detected - verify targets before proceeding');
305
+ }
306
+ }
307
+ const timeInvestment = calculateTimeInvestment(allFiles, context.sessionStartTime);
308
+ const severity = calculateSeverity(allFiles.length, totalLines, totalSize, isRecursive);
309
+ const recommendation = calculateRecommendation(severity, allFiles.length > 0);
310
+ // Build summary
311
+ let summary;
312
+ if (allFiles.length === 0) {
313
+ summary = 'No files found to delete';
314
+ }
315
+ else if (allFiles.length === 1) {
316
+ summary = `Will delete 1 file (${formatBytes(totalSize)}, ${String(totalLines)} lines)`;
317
+ }
318
+ else {
319
+ summary = `Will delete ${String(allFiles.length)} files (${formatBytes(totalSize)}, ${String(totalLines)} lines total)`;
320
+ }
321
+ return {
322
+ operation,
323
+ category: 'file',
324
+ isDestructive: allFiles.length > 0,
325
+ isReversible: false,
326
+ impact: {
327
+ filesAffected: allFiles.length,
328
+ linesAffected: totalLines,
329
+ affectedFiles: allFiles.slice(0, 50), // Limit to first 50 for large deletions
330
+ timeInvestment,
331
+ dataSize: totalSize,
332
+ summary,
333
+ },
334
+ severity,
335
+ warnings,
336
+ recommendation,
337
+ alternatives: [
338
+ 'mv <files> /tmp/ (move to temp instead of deleting)',
339
+ 'ls -la <path> (list files first)',
340
+ 'Create a backup before deleting',
341
+ ],
342
+ analysisTimeMs: Date.now() - startTime,
343
+ };
344
+ }
345
+ }
346
+ /**
347
+ * Create a File rehearsal analyzer
348
+ */
349
+ export function createFileAnalyzer() {
350
+ return new FileRehearsalAnalyzer();
351
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Git Rehearsal Analyzer
3
+ *
4
+ * Analyzes destructive git operations and provides impact assessment.
5
+ * Handles: git reset, git checkout --, git restore, git clean, git stash drop
6
+ */
7
+ import type { RehearsalAnalyzer, RehearsalResult, RehearsalContext } from './types.js';
8
+ /**
9
+ * Git Rehearsal Analyzer
10
+ */
11
+ export declare class GitRehearsalAnalyzer implements RehearsalAnalyzer {
12
+ readonly id = "git-analyzer";
13
+ readonly name = "Git Operations Analyzer";
14
+ readonly category: "git";
15
+ readonly patterns: RegExp[];
16
+ canAnalyze(operation: string): boolean;
17
+ analyze(operation: string, context: RehearsalContext): Promise<RehearsalResult>;
18
+ }
19
+ /**
20
+ * Create a Git rehearsal analyzer
21
+ */
22
+ export declare function createGitAnalyzer(): GitRehearsalAnalyzer;