codeep 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +576 -0
  3. package/dist/api/index.d.ts +8 -0
  4. package/dist/api/index.js +421 -0
  5. package/dist/app.d.ts +2 -0
  6. package/dist/app.js +1406 -0
  7. package/dist/components/AgentProgress.d.ts +33 -0
  8. package/dist/components/AgentProgress.js +97 -0
  9. package/dist/components/Export.d.ts +8 -0
  10. package/dist/components/Export.js +27 -0
  11. package/dist/components/Help.d.ts +2 -0
  12. package/dist/components/Help.js +3 -0
  13. package/dist/components/Input.d.ts +9 -0
  14. package/dist/components/Input.js +89 -0
  15. package/dist/components/Loading.d.ts +9 -0
  16. package/dist/components/Loading.js +31 -0
  17. package/dist/components/Login.d.ts +7 -0
  18. package/dist/components/Login.js +77 -0
  19. package/dist/components/Logo.d.ts +8 -0
  20. package/dist/components/Logo.js +89 -0
  21. package/dist/components/LogoutPicker.d.ts +8 -0
  22. package/dist/components/LogoutPicker.js +61 -0
  23. package/dist/components/Message.d.ts +10 -0
  24. package/dist/components/Message.js +234 -0
  25. package/dist/components/MessageList.d.ts +10 -0
  26. package/dist/components/MessageList.js +8 -0
  27. package/dist/components/ProjectPermission.d.ts +7 -0
  28. package/dist/components/ProjectPermission.js +52 -0
  29. package/dist/components/Search.d.ts +10 -0
  30. package/dist/components/Search.js +30 -0
  31. package/dist/components/SessionPicker.d.ts +9 -0
  32. package/dist/components/SessionPicker.js +88 -0
  33. package/dist/components/Sessions.d.ts +12 -0
  34. package/dist/components/Sessions.js +102 -0
  35. package/dist/components/Settings.d.ts +7 -0
  36. package/dist/components/Settings.js +162 -0
  37. package/dist/components/Status.d.ts +2 -0
  38. package/dist/components/Status.js +12 -0
  39. package/dist/config/config.test.d.ts +1 -0
  40. package/dist/config/config.test.js +157 -0
  41. package/dist/config/index.d.ts +121 -0
  42. package/dist/config/index.js +555 -0
  43. package/dist/config/providers.d.ts +43 -0
  44. package/dist/config/providers.js +82 -0
  45. package/dist/config/providers.test.d.ts +1 -0
  46. package/dist/config/providers.test.js +132 -0
  47. package/dist/index.d.ts +2 -0
  48. package/dist/index.js +38 -0
  49. package/dist/utils/agent.d.ts +37 -0
  50. package/dist/utils/agent.js +627 -0
  51. package/dist/utils/codeReview.d.ts +36 -0
  52. package/dist/utils/codeReview.js +390 -0
  53. package/dist/utils/context.d.ts +49 -0
  54. package/dist/utils/context.js +216 -0
  55. package/dist/utils/diffPreview.d.ts +57 -0
  56. package/dist/utils/diffPreview.js +335 -0
  57. package/dist/utils/export.d.ts +19 -0
  58. package/dist/utils/export.js +94 -0
  59. package/dist/utils/git.d.ts +85 -0
  60. package/dist/utils/git.js +399 -0
  61. package/dist/utils/git.test.d.ts +1 -0
  62. package/dist/utils/git.test.js +193 -0
  63. package/dist/utils/history.d.ts +93 -0
  64. package/dist/utils/history.js +348 -0
  65. package/dist/utils/interactive.d.ts +34 -0
  66. package/dist/utils/interactive.js +206 -0
  67. package/dist/utils/keychain.d.ts +17 -0
  68. package/dist/utils/keychain.js +160 -0
  69. package/dist/utils/learning.d.ts +89 -0
  70. package/dist/utils/learning.js +330 -0
  71. package/dist/utils/logger.d.ts +33 -0
  72. package/dist/utils/logger.js +130 -0
  73. package/dist/utils/project.d.ts +86 -0
  74. package/dist/utils/project.js +415 -0
  75. package/dist/utils/project.test.d.ts +1 -0
  76. package/dist/utils/project.test.js +212 -0
  77. package/dist/utils/ratelimit.d.ts +26 -0
  78. package/dist/utils/ratelimit.js +132 -0
  79. package/dist/utils/ratelimit.test.d.ts +1 -0
  80. package/dist/utils/ratelimit.test.js +131 -0
  81. package/dist/utils/retry.d.ts +28 -0
  82. package/dist/utils/retry.js +109 -0
  83. package/dist/utils/retry.test.d.ts +1 -0
  84. package/dist/utils/retry.test.js +163 -0
  85. package/dist/utils/search.d.ts +11 -0
  86. package/dist/utils/search.js +29 -0
  87. package/dist/utils/shell.d.ts +45 -0
  88. package/dist/utils/shell.js +242 -0
  89. package/dist/utils/skills.d.ts +144 -0
  90. package/dist/utils/skills.js +1137 -0
  91. package/dist/utils/smartContext.d.ts +29 -0
  92. package/dist/utils/smartContext.js +441 -0
  93. package/dist/utils/tools.d.ts +224 -0
  94. package/dist/utils/tools.js +731 -0
  95. package/dist/utils/update.d.ts +22 -0
  96. package/dist/utils/update.js +128 -0
  97. package/dist/utils/validation.d.ts +28 -0
  98. package/dist/utils/validation.js +141 -0
  99. package/dist/utils/validation.test.d.ts +1 -0
  100. package/dist/utils/validation.test.js +164 -0
  101. package/dist/utils/verify.d.ts +78 -0
  102. package/dist/utils/verify.js +464 -0
  103. package/package.json +68 -0
@@ -0,0 +1,22 @@
1
+ export interface VersionInfo {
2
+ current: string;
3
+ latest: string | null;
4
+ hasUpdate: boolean;
5
+ error?: string;
6
+ }
7
+ /**
8
+ * Get current version from package.json
9
+ */
10
+ export declare function getCurrentVersion(): string;
11
+ /**
12
+ * Check for updates from npm registry
13
+ */
14
+ export declare function checkForUpdates(): Promise<VersionInfo>;
15
+ /**
16
+ * Get update instructions based on installation method
17
+ */
18
+ export declare function getUpdateInstructions(): string;
19
+ /**
20
+ * Format version info for display
21
+ */
22
+ export declare function formatVersionInfo(info: VersionInfo): string;
@@ -0,0 +1,128 @@
1
+ import { readFileSync } from 'fs';
2
+ import { join, dirname } from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ const __filename = fileURLToPath(import.meta.url);
5
+ const __dirname = dirname(__filename);
6
+ /**
7
+ * Get current version from package.json
8
+ */
9
+ export function getCurrentVersion() {
10
+ try {
11
+ // In built version, package.json is in parent directory
12
+ const packagePath = join(__dirname, '../../package.json');
13
+ const packageJson = JSON.parse(readFileSync(packagePath, 'utf-8'));
14
+ return packageJson.version;
15
+ }
16
+ catch {
17
+ return 'unknown';
18
+ }
19
+ }
20
+ /**
21
+ * Check for updates from npm registry
22
+ */
23
+ export async function checkForUpdates() {
24
+ const current = getCurrentVersion();
25
+ if (current === 'unknown') {
26
+ return {
27
+ current,
28
+ latest: null,
29
+ hasUpdate: false,
30
+ error: 'Could not determine current version',
31
+ };
32
+ }
33
+ try {
34
+ const response = await fetch('https://registry.npmjs.org/codeep/latest', {
35
+ headers: { 'Accept': 'application/json' },
36
+ });
37
+ if (!response.ok) {
38
+ throw new Error(`HTTP ${response.status}`);
39
+ }
40
+ const data = await response.json();
41
+ const latest = data.version;
42
+ const hasUpdate = compareVersions(latest, current) > 0;
43
+ return {
44
+ current,
45
+ latest,
46
+ hasUpdate,
47
+ };
48
+ }
49
+ catch (error) {
50
+ return {
51
+ current,
52
+ latest: null,
53
+ hasUpdate: false,
54
+ error: error instanceof Error ? error.message : 'Failed to check for updates',
55
+ };
56
+ }
57
+ }
58
+ /**
59
+ * Compare two semver versions
60
+ * Returns: 1 if a > b, -1 if a < b, 0 if equal
61
+ */
62
+ function compareVersions(a, b) {
63
+ const aParts = a.replace(/^v/, '').split('.').map(Number);
64
+ const bParts = b.replace(/^v/, '').split('.').map(Number);
65
+ for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
66
+ const aPart = aParts[i] || 0;
67
+ const bPart = bParts[i] || 0;
68
+ if (aPart > bPart)
69
+ return 1;
70
+ if (aPart < bPart)
71
+ return -1;
72
+ }
73
+ return 0;
74
+ }
75
+ /**
76
+ * Detect installation method
77
+ */
78
+ function detectInstallMethod() {
79
+ const execPath = process.execPath;
80
+ const argv0 = process.argv[0];
81
+ // Homebrew detection - installed in Cellar or with homebrew in path
82
+ if (execPath.includes('/Cellar/codeep') ||
83
+ execPath.includes('homebrew') ||
84
+ execPath.includes('/opt/homebrew')) {
85
+ return 'homebrew';
86
+ }
87
+ // npm global detection - running via node with npm in path
88
+ if (process.env.npm_package_name === 'codeep' ||
89
+ execPath.includes('/.npm/') ||
90
+ execPath.includes('/npm/') ||
91
+ argv0.includes('node')) {
92
+ return 'npm';
93
+ }
94
+ // Binary installation (curl install or manual download)
95
+ return 'binary';
96
+ }
97
+ /**
98
+ * Get update instructions based on installation method
99
+ */
100
+ export function getUpdateInstructions() {
101
+ const method = detectInstallMethod();
102
+ switch (method) {
103
+ case 'homebrew':
104
+ return 'brew update && brew upgrade codeep';
105
+ case 'npm':
106
+ return 'npm update -g codeep';
107
+ case 'binary':
108
+ return 'curl -fsSL https://raw.githubusercontent.com/VladoIvankovic/Codeep/main/install.sh | bash';
109
+ default:
110
+ return 'Visit: https://codeep.dev';
111
+ }
112
+ }
113
+ /**
114
+ * Format version info for display
115
+ */
116
+ export function formatVersionInfo(info) {
117
+ if (info.error) {
118
+ return `Current: ${info.current}\nUpdate check failed: ${info.error}`;
119
+ }
120
+ if (!info.latest) {
121
+ return `Current: ${info.current}\nCould not check for updates`;
122
+ }
123
+ if (info.hasUpdate) {
124
+ const instructions = getUpdateInstructions();
125
+ return `Current: ${info.current}\nLatest: ${info.latest}\n\nUpdate available! Run:\n ${instructions}`;
126
+ }
127
+ return `Current: ${info.current}\nYou're running the latest version! 🎉`;
128
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Input validation and sanitization utilities
3
+ */
4
+ export interface ValidationResult {
5
+ valid: boolean;
6
+ sanitized?: string;
7
+ error?: string;
8
+ }
9
+ /**
10
+ * Validate and sanitize user input before sending to API
11
+ */
12
+ export declare function validateInput(input: string): ValidationResult;
13
+ /**
14
+ * Validate API key format
15
+ */
16
+ export declare function validateApiKey(key: string): ValidationResult;
17
+ /**
18
+ * Validate command arguments
19
+ */
20
+ export declare function validateCommandArgs(command: string, args: string[]): ValidationResult;
21
+ /**
22
+ * Validate file path (for file operations)
23
+ */
24
+ export declare function validateFilePath(path: string): ValidationResult;
25
+ /**
26
+ * Sanitize output before display (prevent terminal escape sequence injection)
27
+ */
28
+ export declare function sanitizeOutput(output: string): string;
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Input validation and sanitization utilities
3
+ */
4
+ const MAX_INPUT_LENGTH = 50000; // ~50KB max input
5
+ const MAX_LINES = 5000;
6
+ /**
7
+ * Validate and sanitize user input before sending to API
8
+ */
9
+ export function validateInput(input) {
10
+ // Check for empty input
11
+ if (!input || input.trim().length === 0) {
12
+ return {
13
+ valid: false,
14
+ error: 'Input cannot be empty',
15
+ };
16
+ }
17
+ // Check length
18
+ if (input.length > MAX_INPUT_LENGTH) {
19
+ return {
20
+ valid: false,
21
+ error: `Input too long (max ${MAX_INPUT_LENGTH} characters)`,
22
+ };
23
+ }
24
+ // Check line count
25
+ const lines = input.split('\n');
26
+ if (lines.length > MAX_LINES) {
27
+ return {
28
+ valid: false,
29
+ error: `Too many lines (max ${MAX_LINES} lines)`,
30
+ };
31
+ }
32
+ // Sanitize: remove null bytes and other control characters (except newlines, tabs)
33
+ let sanitized = input
34
+ .replace(/\0/g, '') // Remove null bytes
35
+ .replace(/[\x01-\x08\x0B-\x0C\x0E-\x1F\x7F]/g, ''); // Remove other control chars
36
+ // Trim excessive whitespace (but preserve intentional formatting)
37
+ sanitized = sanitized.replace(/\n{5,}/g, '\n\n\n\n'); // Max 4 consecutive newlines
38
+ return {
39
+ valid: true,
40
+ sanitized,
41
+ };
42
+ }
43
+ /**
44
+ * Validate API key format
45
+ */
46
+ export function validateApiKey(key) {
47
+ if (!key || key.trim().length === 0) {
48
+ return {
49
+ valid: false,
50
+ error: 'API key cannot be empty',
51
+ };
52
+ }
53
+ // Basic format check - most API keys are alphanumeric with some special chars
54
+ const keyPattern = /^[a-zA-Z0-9._-]+$/;
55
+ if (!keyPattern.test(key)) {
56
+ return {
57
+ valid: false,
58
+ error: 'API key contains invalid characters',
59
+ };
60
+ }
61
+ // Length check - most API keys are 20-100 characters
62
+ if (key.length < 10 || key.length > 200) {
63
+ return {
64
+ valid: false,
65
+ error: 'API key length invalid (expected 10-200 characters)',
66
+ };
67
+ }
68
+ return {
69
+ valid: true,
70
+ sanitized: key.trim(),
71
+ };
72
+ }
73
+ /**
74
+ * Validate command arguments
75
+ */
76
+ export function validateCommandArgs(command, args) {
77
+ // Check for command injection attempts
78
+ const dangerousPatterns = [
79
+ /[;&|`$()]/, // Shell metacharacters
80
+ /\.\./, // Path traversal
81
+ /\beval\b/i, // eval() attempts
82
+ /\bexec\b/i, // exec() attempts
83
+ ];
84
+ const fullCommand = [command, ...args].join(' ');
85
+ for (const pattern of dangerousPatterns) {
86
+ if (pattern.test(fullCommand)) {
87
+ return {
88
+ valid: false,
89
+ error: 'Command contains potentially dangerous characters',
90
+ };
91
+ }
92
+ }
93
+ return {
94
+ valid: true,
95
+ sanitized: fullCommand,
96
+ };
97
+ }
98
+ /**
99
+ * Validate file path (for file operations)
100
+ */
101
+ export function validateFilePath(path) {
102
+ if (!path || path.trim().length === 0) {
103
+ return {
104
+ valid: false,
105
+ error: 'File path cannot be empty',
106
+ };
107
+ }
108
+ // Check for path traversal
109
+ if (path.includes('..')) {
110
+ return {
111
+ valid: false,
112
+ error: 'Path traversal not allowed',
113
+ };
114
+ }
115
+ // Check for absolute paths outside project (basic check)
116
+ if (path.startsWith('/etc/') ||
117
+ path.startsWith('/sys/') ||
118
+ path.startsWith('/proc/') ||
119
+ path.startsWith('C:\\Windows\\') ||
120
+ path.startsWith('C:\\System')) {
121
+ return {
122
+ valid: false,
123
+ error: 'Access to system paths not allowed',
124
+ };
125
+ }
126
+ return {
127
+ valid: true,
128
+ sanitized: path.trim(),
129
+ };
130
+ }
131
+ /**
132
+ * Sanitize output before display (prevent terminal escape sequence injection)
133
+ */
134
+ export function sanitizeOutput(output) {
135
+ // Remove ANSI escape sequences that could be malicious
136
+ // Keep basic formatting codes but remove cursor movement, clear screen, etc.
137
+ return output
138
+ .replace(/\x1b\[(\d+;)*\d*[ABCDEFGHJKSTfmsu]/g, '') // Remove cursor control
139
+ .replace(/\x1b\].*?\x07/g, '') // Remove OSC sequences
140
+ .replace(/\x1b\[.*?~/g, ''); // Remove other escape sequences
141
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,164 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { validateInput, validateApiKey, validateCommandArgs, validateFilePath, sanitizeOutput, } from './validation';
3
+ describe('validation utilities', () => {
4
+ describe('validateInput', () => {
5
+ it('should reject empty input', () => {
6
+ expect(validateInput('').valid).toBe(false);
7
+ expect(validateInput(' ').valid).toBe(false);
8
+ });
9
+ it('should accept valid input', () => {
10
+ const result = validateInput('Hello, world!');
11
+ expect(result.valid).toBe(true);
12
+ expect(result.sanitized).toBe('Hello, world!');
13
+ });
14
+ it('should reject input exceeding max length', () => {
15
+ const longInput = 'a'.repeat(60000);
16
+ const result = validateInput(longInput);
17
+ expect(result.valid).toBe(false);
18
+ expect(result.error).toContain('too long');
19
+ });
20
+ it('should reject input with too many lines', () => {
21
+ const manyLines = Array(6000).fill('line').join('\n');
22
+ const result = validateInput(manyLines);
23
+ expect(result.valid).toBe(false);
24
+ expect(result.error).toContain('Too many lines');
25
+ });
26
+ it('should remove null bytes', () => {
27
+ const result = validateInput('hello\0world');
28
+ expect(result.valid).toBe(true);
29
+ expect(result.sanitized).toBe('helloworld');
30
+ });
31
+ it('should remove control characters except newlines and tabs', () => {
32
+ const result = validateInput('hello\x01\x02world\n\ttab');
33
+ expect(result.valid).toBe(true);
34
+ expect(result.sanitized).toBe('helloworld\n\ttab');
35
+ });
36
+ it('should limit consecutive newlines to 4', () => {
37
+ const result = validateInput('hello\n\n\n\n\n\n\nworld');
38
+ expect(result.valid).toBe(true);
39
+ expect(result.sanitized).toBe('hello\n\n\n\nworld');
40
+ });
41
+ it('should preserve normal formatting', () => {
42
+ const input = 'function test() {\n return true;\n}';
43
+ const result = validateInput(input);
44
+ expect(result.valid).toBe(true);
45
+ expect(result.sanitized).toBe(input);
46
+ });
47
+ });
48
+ describe('validateApiKey', () => {
49
+ it('should reject empty key', () => {
50
+ expect(validateApiKey('').valid).toBe(false);
51
+ expect(validateApiKey(' ').valid).toBe(false);
52
+ });
53
+ it('should accept valid API key', () => {
54
+ const result = validateApiKey('sk-abcdef123456789012345');
55
+ expect(result.valid).toBe(true);
56
+ expect(result.sanitized).toBe('sk-abcdef123456789012345');
57
+ });
58
+ it('should reject key with invalid characters', () => {
59
+ const result = validateApiKey('sk-test!@#$%');
60
+ expect(result.valid).toBe(false);
61
+ expect(result.error).toContain('invalid characters');
62
+ });
63
+ it('should reject too short keys', () => {
64
+ const result = validateApiKey('short');
65
+ expect(result.valid).toBe(false);
66
+ expect(result.error).toContain('length invalid');
67
+ });
68
+ it('should reject too long keys', () => {
69
+ const result = validateApiKey('a'.repeat(250));
70
+ expect(result.valid).toBe(false);
71
+ expect(result.error).toContain('length invalid');
72
+ });
73
+ it('should reject keys with whitespace', () => {
74
+ // API keys with leading/trailing spaces contain invalid characters
75
+ const result = validateApiKey(' sk-validkey12345678 ');
76
+ expect(result.valid).toBe(false);
77
+ });
78
+ it('should accept keys with dots, underscores, and dashes', () => {
79
+ const result = validateApiKey('sk_test.key-123456789');
80
+ expect(result.valid).toBe(true);
81
+ });
82
+ });
83
+ describe('validateCommandArgs', () => {
84
+ it('should accept safe commands', () => {
85
+ const result = validateCommandArgs('help', []);
86
+ expect(result.valid).toBe(true);
87
+ });
88
+ it('should reject shell metacharacters', () => {
89
+ expect(validateCommandArgs('test', ['; rm -rf /']).valid).toBe(false);
90
+ expect(validateCommandArgs('test', ['| cat /etc/passwd']).valid).toBe(false);
91
+ expect(validateCommandArgs('test', ['`whoami`']).valid).toBe(false);
92
+ expect(validateCommandArgs('test', ['$(id)']).valid).toBe(false);
93
+ expect(validateCommandArgs('test', ['&& echo']).valid).toBe(false);
94
+ });
95
+ it('should reject path traversal', () => {
96
+ const result = validateCommandArgs('read', ['../../etc/passwd']);
97
+ expect(result.valid).toBe(false);
98
+ });
99
+ it('should reject eval attempts', () => {
100
+ const result = validateCommandArgs('run', ['eval("code")', 'test']);
101
+ expect(result.valid).toBe(false);
102
+ });
103
+ it('should reject exec attempts', () => {
104
+ const result = validateCommandArgs('run', ['exec(cmd)']);
105
+ expect(result.valid).toBe(false);
106
+ });
107
+ });
108
+ describe('validateFilePath', () => {
109
+ it('should reject empty path', () => {
110
+ expect(validateFilePath('').valid).toBe(false);
111
+ expect(validateFilePath(' ').valid).toBe(false);
112
+ });
113
+ it('should accept valid paths', () => {
114
+ expect(validateFilePath('src/index.ts').valid).toBe(true);
115
+ expect(validateFilePath('./package.json').valid).toBe(true);
116
+ expect(validateFilePath('utils/helper.js').valid).toBe(true);
117
+ });
118
+ it('should reject path traversal', () => {
119
+ expect(validateFilePath('../secret.txt').valid).toBe(false);
120
+ expect(validateFilePath('src/../../etc/passwd').valid).toBe(false);
121
+ expect(validateFilePath('..').valid).toBe(false);
122
+ });
123
+ it('should reject system paths on Unix', () => {
124
+ expect(validateFilePath('/etc/passwd').valid).toBe(false);
125
+ expect(validateFilePath('/sys/kernel').valid).toBe(false);
126
+ expect(validateFilePath('/proc/self').valid).toBe(false);
127
+ });
128
+ it('should reject system paths on Windows', () => {
129
+ expect(validateFilePath('C:\\Windows\\System32').valid).toBe(false);
130
+ expect(validateFilePath('C:\\System\\config').valid).toBe(false);
131
+ });
132
+ it('should trim whitespace', () => {
133
+ const result = validateFilePath(' src/index.ts ');
134
+ expect(result.valid).toBe(true);
135
+ expect(result.sanitized).toBe('src/index.ts');
136
+ });
137
+ });
138
+ describe('sanitizeOutput', () => {
139
+ it('should preserve normal text', () => {
140
+ expect(sanitizeOutput('Hello, world!')).toBe('Hello, world!');
141
+ });
142
+ it('should remove cursor control sequences', () => {
143
+ // Move cursor up
144
+ expect(sanitizeOutput('text\x1b[2Amore')).toBe('textmore');
145
+ // Move cursor down
146
+ expect(sanitizeOutput('text\x1b[5Bmore')).toBe('textmore');
147
+ // Clear screen
148
+ expect(sanitizeOutput('text\x1b[2Jmore')).toBe('textmore');
149
+ });
150
+ it('should remove OSC sequences', () => {
151
+ // Set window title
152
+ expect(sanitizeOutput('text\x1b]0;malicious title\x07more')).toBe('textmore');
153
+ });
154
+ it('should handle multiple escape sequences', () => {
155
+ const malicious = '\x1b[2J\x1b[H\x1b]0;pwned\x07dangerous content';
156
+ const result = sanitizeOutput(malicious);
157
+ expect(result).not.toContain('\x1b');
158
+ expect(result).toContain('dangerous content');
159
+ });
160
+ it('should handle empty string', () => {
161
+ expect(sanitizeOutput('')).toBe('');
162
+ });
163
+ });
164
+ });
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Self-verification module for agent
3
+ * Runs build/test and analyzes errors for auto-fixing
4
+ */
5
+ export interface VerifyResult {
6
+ success: boolean;
7
+ type: 'build' | 'test' | 'lint' | 'typecheck';
8
+ command: string;
9
+ output: string;
10
+ errors: ParsedError[];
11
+ duration: number;
12
+ }
13
+ export interface ParsedError {
14
+ file?: string;
15
+ line?: number;
16
+ column?: number;
17
+ message: string;
18
+ code?: string;
19
+ severity: 'error' | 'warning';
20
+ }
21
+ export interface VerifyOptions {
22
+ runBuild: boolean;
23
+ runTest: boolean;
24
+ runLint: boolean;
25
+ runTypecheck: boolean;
26
+ timeout: number;
27
+ }
28
+ /**
29
+ * Detect project type and available scripts
30
+ */
31
+ export declare function detectProjectScripts(projectRoot: string): {
32
+ build?: string;
33
+ test?: string;
34
+ lint?: string;
35
+ typecheck?: string;
36
+ packageManager: 'npm' | 'yarn' | 'pnpm' | 'bun';
37
+ };
38
+ /**
39
+ * Run build verification
40
+ */
41
+ export declare function runBuildVerification(projectRoot: string, timeout?: number): VerifyResult | null;
42
+ /**
43
+ * Run test verification
44
+ */
45
+ export declare function runTestVerification(projectRoot: string, timeout?: number): VerifyResult | null;
46
+ /**
47
+ * Run TypeScript type checking
48
+ */
49
+ export declare function runTypecheckVerification(projectRoot: string, timeout?: number): VerifyResult | null;
50
+ /**
51
+ * Run lint verification
52
+ */
53
+ export declare function runLintVerification(projectRoot: string, timeout?: number): VerifyResult | null;
54
+ /**
55
+ * Run all verifications
56
+ */
57
+ export declare function runAllVerifications(projectRoot: string, options?: Partial<VerifyOptions>): VerifyResult[];
58
+ /**
59
+ * Format verification results for display
60
+ */
61
+ export declare function formatVerifyResults(results: VerifyResult[]): string;
62
+ /**
63
+ * Format errors for agent to fix
64
+ */
65
+ export declare function formatErrorsForAgent(results: VerifyResult[]): string;
66
+ /**
67
+ * Check if any verification failed
68
+ */
69
+ export declare function hasVerificationErrors(results: VerifyResult[]): boolean;
70
+ /**
71
+ * Get summary of verification
72
+ */
73
+ export declare function getVerificationSummary(results: VerifyResult[]): {
74
+ passed: number;
75
+ failed: number;
76
+ total: number;
77
+ errors: number;
78
+ };