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,160 @@
1
+ import keytar from 'keytar';
2
+ import { logger } from './logger';
3
+ const SERVICE_NAME = 'codeep';
4
+ class KeychainStorage {
5
+ getAccountName(providerId) {
6
+ return `api-key-${providerId}`;
7
+ }
8
+ async getApiKey(providerId) {
9
+ try {
10
+ const account = this.getAccountName(providerId);
11
+ const password = await keytar.getPassword(SERVICE_NAME, account);
12
+ return password;
13
+ }
14
+ catch (error) {
15
+ logger.debug(`Failed to get API key from keychain: ${error}`);
16
+ return null;
17
+ }
18
+ }
19
+ async setApiKey(providerId, apiKey) {
20
+ try {
21
+ const account = this.getAccountName(providerId);
22
+ await keytar.setPassword(SERVICE_NAME, account, apiKey);
23
+ }
24
+ catch (error) {
25
+ throw new Error(`Failed to store API key in keychain: ${error}`);
26
+ }
27
+ }
28
+ async deleteApiKey(providerId) {
29
+ try {
30
+ const account = this.getAccountName(providerId);
31
+ await keytar.deletePassword(SERVICE_NAME, account);
32
+ }
33
+ catch (error) {
34
+ logger.debug(`Failed to delete API key from keychain: ${error}`);
35
+ }
36
+ }
37
+ async hasApiKey(providerId) {
38
+ const key = await this.getApiKey(providerId);
39
+ return key !== null && key.length > 0;
40
+ }
41
+ }
42
+ /**
43
+ * Fallback to plain text storage in config
44
+ * Used when keychain is not available or fails
45
+ */
46
+ class FallbackStorage {
47
+ config;
48
+ constructor(config) {
49
+ this.config = config;
50
+ }
51
+ async getApiKey(providerId) {
52
+ const keys = this.config.get('apiKeys') || {};
53
+ return keys[providerId] || null;
54
+ }
55
+ async setApiKey(providerId, apiKey) {
56
+ const keys = this.config.get('apiKeys') || {};
57
+ keys[providerId] = apiKey;
58
+ this.config.set('apiKeys', keys);
59
+ }
60
+ async deleteApiKey(providerId) {
61
+ const keys = this.config.get('apiKeys') || {};
62
+ delete keys[providerId];
63
+ this.config.set('apiKeys', keys);
64
+ }
65
+ async hasApiKey(providerId) {
66
+ const key = await this.getApiKey(providerId);
67
+ return key !== null && key.length > 0;
68
+ }
69
+ }
70
+ /**
71
+ * Smart storage that tries keychain first, falls back to config
72
+ */
73
+ class SmartStorage {
74
+ keychain;
75
+ fallback;
76
+ useKeychain = true;
77
+ keychainTested = false;
78
+ constructor(config) {
79
+ this.keychain = new KeychainStorage();
80
+ this.fallback = new FallbackStorage(config);
81
+ }
82
+ async ensureKeychainTested() {
83
+ if (this.keychainTested)
84
+ return;
85
+ try {
86
+ const testKey = '__codeep_test__';
87
+ await keytar.setPassword(SERVICE_NAME, testKey, 'test');
88
+ await keytar.deletePassword(SERVICE_NAME, testKey);
89
+ this.useKeychain = true;
90
+ }
91
+ catch {
92
+ this.useKeychain = false;
93
+ // Silently fallback - don't warn user
94
+ }
95
+ this.keychainTested = true;
96
+ }
97
+ async getApiKey(providerId) {
98
+ await this.ensureKeychainTested();
99
+ if (this.useKeychain) {
100
+ const key = await this.keychain.getApiKey(providerId);
101
+ if (key)
102
+ return key;
103
+ }
104
+ return this.fallback.getApiKey(providerId);
105
+ }
106
+ async setApiKey(providerId, apiKey) {
107
+ await this.ensureKeychainTested();
108
+ if (this.useKeychain) {
109
+ try {
110
+ await this.keychain.setApiKey(providerId, apiKey);
111
+ // Remove from fallback if it was there
112
+ await this.fallback.deleteApiKey(providerId);
113
+ return;
114
+ }
115
+ catch (error) {
116
+ // Keychain failed, fall back to config
117
+ this.useKeychain = false;
118
+ }
119
+ }
120
+ await this.fallback.setApiKey(providerId, apiKey);
121
+ }
122
+ async deleteApiKey(providerId) {
123
+ await this.ensureKeychainTested();
124
+ if (this.useKeychain) {
125
+ await this.keychain.deleteApiKey(providerId);
126
+ }
127
+ await this.fallback.deleteApiKey(providerId);
128
+ }
129
+ async hasApiKey(providerId) {
130
+ await this.ensureKeychainTested();
131
+ if (this.useKeychain) {
132
+ const hasKeychain = await this.keychain.hasApiKey(providerId);
133
+ if (hasKeychain)
134
+ return true;
135
+ }
136
+ return this.fallback.hasApiKey(providerId);
137
+ }
138
+ }
139
+ /**
140
+ * Migrate existing plain-text API keys to keychain
141
+ */
142
+ export async function migrateApiKeysToKeychain(config) {
143
+ const storage = new SmartStorage(config);
144
+ const apiKeys = config.get('apiKeys') || {};
145
+ for (const [providerId, apiKey] of Object.entries(apiKeys)) {
146
+ if (typeof apiKey === 'string' && apiKey.length > 0) {
147
+ try {
148
+ await storage.setApiKey(providerId, apiKey);
149
+ logger.info(`Migrated API key for ${providerId} to secure storage`);
150
+ }
151
+ catch (error) {
152
+ logger.error(`Failed to migrate API key for ${providerId}`);
153
+ }
154
+ }
155
+ }
156
+ }
157
+ // Export singleton
158
+ export function createSecureStorage(config) {
159
+ return new SmartStorage(config);
160
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Learning Mode - remember user preferences and coding style
3
+ */
4
+ export interface UserPreferences {
5
+ codeStyle: {
6
+ indentation: 'tabs' | 'spaces';
7
+ indentSize: number;
8
+ quotes: 'single' | 'double';
9
+ semicolons: boolean;
10
+ trailingComma: 'none' | 'es5' | 'all';
11
+ lineWidth: number;
12
+ };
13
+ naming: {
14
+ variables: 'camelCase' | 'snake_case' | 'PascalCase';
15
+ functions: 'camelCase' | 'snake_case' | 'PascalCase';
16
+ classes: 'PascalCase' | 'camelCase';
17
+ constants: 'UPPER_CASE' | 'camelCase';
18
+ files: 'kebab-case' | 'camelCase' | 'PascalCase' | 'snake_case';
19
+ };
20
+ frameworks: {
21
+ frontend?: string;
22
+ backend?: string;
23
+ testing?: string;
24
+ styling?: string;
25
+ stateManagement?: string;
26
+ };
27
+ languages: {
28
+ primary?: string;
29
+ preferTypeScript: boolean;
30
+ preferAsyncAwait: boolean;
31
+ };
32
+ patterns: {
33
+ importStyle?: 'named' | 'default' | 'mixed';
34
+ exportStyle?: 'named' | 'default' | 'mixed';
35
+ componentStyle?: 'functional' | 'class';
36
+ errorHandling?: 'try-catch' | 'then-catch' | 'result-type';
37
+ };
38
+ preferredLibraries: string[];
39
+ customRules: string[];
40
+ lastUpdated: number;
41
+ sampleCount: number;
42
+ }
43
+ export interface ProjectPreferences extends UserPreferences {
44
+ projectPath: string;
45
+ }
46
+ /**
47
+ * Load global preferences
48
+ */
49
+ export declare function loadGlobalPreferences(): UserPreferences;
50
+ /**
51
+ * Save global preferences
52
+ */
53
+ export declare function saveGlobalPreferences(prefs: UserPreferences): void;
54
+ /**
55
+ * Load project-specific preferences
56
+ */
57
+ export declare function loadProjectPreferences(projectRoot: string): UserPreferences;
58
+ /**
59
+ * Save project-specific preferences
60
+ */
61
+ export declare function saveProjectPreferences(projectRoot: string, prefs: Partial<UserPreferences>): void;
62
+ /**
63
+ * Analyze code to learn preferences
64
+ */
65
+ export declare function learnFromCode(code: string, filename: string, currentPrefs: UserPreferences): Partial<UserPreferences>;
66
+ /**
67
+ * Learn from multiple files in a project
68
+ */
69
+ export declare function learnFromProject(projectRoot: string, files: string[]): UserPreferences;
70
+ /**
71
+ * Format preferences for system prompt
72
+ */
73
+ export declare function formatPreferencesForPrompt(prefs: UserPreferences): string;
74
+ /**
75
+ * Add a custom rule
76
+ */
77
+ export declare function addCustomRule(rule: string, projectRoot?: string): void;
78
+ /**
79
+ * Remove a custom rule
80
+ */
81
+ export declare function removeCustomRule(rule: string, projectRoot?: string): void;
82
+ /**
83
+ * Reset preferences
84
+ */
85
+ export declare function resetPreferences(projectRoot?: string): void;
86
+ /**
87
+ * Get learning status
88
+ */
89
+ export declare function getLearningStatus(projectRoot?: string): string;
@@ -0,0 +1,330 @@
1
+ /**
2
+ * Learning Mode - remember user preferences and coding style
3
+ */
4
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
5
+ import { join } from 'path';
6
+ import { homedir } from 'os';
7
+ // Learning data storage
8
+ const LEARNING_DIR = join(homedir(), '.codeep', 'learning');
9
+ const GLOBAL_PREFS_FILE = join(LEARNING_DIR, 'preferences.json');
10
+ const DEFAULT_PREFERENCES = {
11
+ codeStyle: {
12
+ indentation: 'spaces',
13
+ indentSize: 2,
14
+ quotes: 'single',
15
+ semicolons: true,
16
+ trailingComma: 'es5',
17
+ lineWidth: 100,
18
+ },
19
+ naming: {
20
+ variables: 'camelCase',
21
+ functions: 'camelCase',
22
+ classes: 'PascalCase',
23
+ constants: 'UPPER_CASE',
24
+ files: 'kebab-case',
25
+ },
26
+ frameworks: {},
27
+ languages: {
28
+ preferTypeScript: true,
29
+ preferAsyncAwait: true,
30
+ },
31
+ patterns: {},
32
+ preferredLibraries: [],
33
+ customRules: [],
34
+ lastUpdated: Date.now(),
35
+ sampleCount: 0,
36
+ };
37
+ /**
38
+ * Ensure learning directory exists
39
+ */
40
+ function ensureLearningDir() {
41
+ if (!existsSync(LEARNING_DIR)) {
42
+ mkdirSync(LEARNING_DIR, { recursive: true });
43
+ }
44
+ }
45
+ /**
46
+ * Get project-specific preferences file path
47
+ */
48
+ function getProjectPrefsPath(projectRoot) {
49
+ const projectId = Buffer.from(projectRoot).toString('base64url').substring(0, 32);
50
+ return join(LEARNING_DIR, `project-${projectId}.json`);
51
+ }
52
+ /**
53
+ * Load global preferences
54
+ */
55
+ export function loadGlobalPreferences() {
56
+ ensureLearningDir();
57
+ if (existsSync(GLOBAL_PREFS_FILE)) {
58
+ try {
59
+ const content = readFileSync(GLOBAL_PREFS_FILE, 'utf-8');
60
+ return { ...DEFAULT_PREFERENCES, ...JSON.parse(content) };
61
+ }
62
+ catch { }
63
+ }
64
+ return { ...DEFAULT_PREFERENCES };
65
+ }
66
+ /**
67
+ * Save global preferences
68
+ */
69
+ export function saveGlobalPreferences(prefs) {
70
+ ensureLearningDir();
71
+ prefs.lastUpdated = Date.now();
72
+ writeFileSync(GLOBAL_PREFS_FILE, JSON.stringify(prefs, null, 2));
73
+ }
74
+ /**
75
+ * Load project-specific preferences
76
+ */
77
+ export function loadProjectPreferences(projectRoot) {
78
+ const globalPrefs = loadGlobalPreferences();
79
+ const projectPrefsPath = getProjectPrefsPath(projectRoot);
80
+ if (existsSync(projectPrefsPath)) {
81
+ try {
82
+ const content = readFileSync(projectPrefsPath, 'utf-8');
83
+ const projectPrefs = JSON.parse(content);
84
+ // Merge with global, project takes precedence
85
+ return { ...globalPrefs, ...projectPrefs };
86
+ }
87
+ catch { }
88
+ }
89
+ return globalPrefs;
90
+ }
91
+ /**
92
+ * Save project-specific preferences
93
+ */
94
+ export function saveProjectPreferences(projectRoot, prefs) {
95
+ ensureLearningDir();
96
+ const projectPrefsPath = getProjectPrefsPath(projectRoot);
97
+ const existing = existsSync(projectPrefsPath)
98
+ ? JSON.parse(readFileSync(projectPrefsPath, 'utf-8'))
99
+ : {};
100
+ const merged = { ...existing, ...prefs, lastUpdated: Date.now() };
101
+ writeFileSync(projectPrefsPath, JSON.stringify(merged, null, 2));
102
+ }
103
+ /**
104
+ * Analyze code to learn preferences
105
+ */
106
+ export function learnFromCode(code, filename, currentPrefs) {
107
+ const learned = {};
108
+ const codeStyle = {};
109
+ const naming = {};
110
+ const patterns = {};
111
+ const lines = code.split('\n');
112
+ // Learn indentation
113
+ const indentedLines = lines.filter(l => l.match(/^(\s+)\S/));
114
+ if (indentedLines.length > 0) {
115
+ const firstIndent = indentedLines[0].match(/^(\s+)/)?.[1] || '';
116
+ if (firstIndent.includes('\t')) {
117
+ codeStyle.indentation = 'tabs';
118
+ }
119
+ else {
120
+ codeStyle.indentation = 'spaces';
121
+ codeStyle.indentSize = firstIndent.length;
122
+ }
123
+ }
124
+ // Learn quote style
125
+ const singleQuotes = (code.match(/'/g) || []).length;
126
+ const doubleQuotes = (code.match(/"/g) || []).length;
127
+ if (singleQuotes > doubleQuotes * 1.5) {
128
+ codeStyle.quotes = 'single';
129
+ }
130
+ else if (doubleQuotes > singleQuotes * 1.5) {
131
+ codeStyle.quotes = 'double';
132
+ }
133
+ // Learn semicolon usage
134
+ const withSemicolons = (code.match(/;\s*$/gm) || []).length;
135
+ const statements = lines.filter(l => l.trim() && !l.trim().startsWith('//')).length;
136
+ codeStyle.semicolons = withSemicolons > statements * 0.5;
137
+ // Learn trailing comma
138
+ if (code.includes(',\n]') || code.includes(',\n}')) {
139
+ codeStyle.trailingComma = 'all';
140
+ }
141
+ // Learn naming conventions from variable declarations
142
+ const varMatches = code.matchAll(/(?:const|let|var)\s+(\w+)/g);
143
+ let camelCount = 0;
144
+ let snakeCount = 0;
145
+ for (const match of varMatches) {
146
+ const name = match[1];
147
+ if (name.includes('_'))
148
+ snakeCount++;
149
+ else if (/^[a-z]/.test(name) && /[A-Z]/.test(name))
150
+ camelCount++;
151
+ }
152
+ if (camelCount > snakeCount * 2) {
153
+ naming.variables = 'camelCase';
154
+ }
155
+ else if (snakeCount > camelCount * 2) {
156
+ naming.variables = 'snake_case';
157
+ }
158
+ // Learn import style
159
+ const namedImports = (code.match(/import\s*{[^}]+}\s*from/g) || []).length;
160
+ const defaultImports = (code.match(/import\s+\w+\s+from/g) || []).length;
161
+ if (namedImports > defaultImports * 2) {
162
+ patterns.importStyle = 'named';
163
+ }
164
+ else if (defaultImports > namedImports * 2) {
165
+ patterns.importStyle = 'default';
166
+ }
167
+ else {
168
+ patterns.importStyle = 'mixed';
169
+ }
170
+ // Learn component style (React)
171
+ if (code.includes('function') && code.includes('return') && code.includes('<')) {
172
+ patterns.componentStyle = 'functional';
173
+ }
174
+ else if (code.includes('class') && code.includes('extends') && code.includes('render')) {
175
+ patterns.componentStyle = 'class';
176
+ }
177
+ // Learn async/await vs then/catch
178
+ const asyncAwait = (code.match(/async|await/g) || []).length;
179
+ const thenCatch = (code.match(/\.then\(|\.catch\(/g) || []).length;
180
+ if (asyncAwait > thenCatch) {
181
+ learned.languages = { ...currentPrefs.languages, preferAsyncAwait: true };
182
+ }
183
+ // Learn preferred libraries
184
+ const libraries = [];
185
+ const importMatches = code.matchAll(/import\s+.*?\s+from\s+['"]([^'"./][^'"]*)['"]/g);
186
+ for (const match of importMatches) {
187
+ const lib = match[1].split('/')[0];
188
+ if (!libraries.includes(lib)) {
189
+ libraries.push(lib);
190
+ }
191
+ }
192
+ if (libraries.length > 0) {
193
+ learned.preferredLibraries = [
194
+ ...new Set([...currentPrefs.preferredLibraries, ...libraries]),
195
+ ].slice(0, 20);
196
+ }
197
+ // Merge learned styles
198
+ if (Object.keys(codeStyle).length > 0) {
199
+ learned.codeStyle = { ...currentPrefs.codeStyle, ...codeStyle };
200
+ }
201
+ if (Object.keys(naming).length > 0) {
202
+ learned.naming = { ...currentPrefs.naming, ...naming };
203
+ }
204
+ if (Object.keys(patterns).length > 0) {
205
+ learned.patterns = { ...currentPrefs.patterns, ...patterns };
206
+ }
207
+ learned.sampleCount = (currentPrefs.sampleCount || 0) + 1;
208
+ return learned;
209
+ }
210
+ /**
211
+ * Learn from multiple files in a project
212
+ */
213
+ export function learnFromProject(projectRoot, files) {
214
+ let prefs = loadProjectPreferences(projectRoot);
215
+ for (const file of files.slice(0, 20)) { // Limit to 20 files
216
+ try {
217
+ const content = readFileSync(join(projectRoot, file), 'utf-8');
218
+ const learned = learnFromCode(content, file, prefs);
219
+ prefs = { ...prefs, ...learned };
220
+ }
221
+ catch { }
222
+ }
223
+ saveProjectPreferences(projectRoot, prefs);
224
+ return prefs;
225
+ }
226
+ /**
227
+ * Format preferences for system prompt
228
+ */
229
+ export function formatPreferencesForPrompt(prefs) {
230
+ const lines = ['## User Preferences (Learned)', ''];
231
+ // Code style
232
+ lines.push('### Code Style');
233
+ lines.push(`- Indentation: ${prefs.codeStyle.indentSize} ${prefs.codeStyle.indentation}`);
234
+ lines.push(`- Quotes: ${prefs.codeStyle.quotes}`);
235
+ lines.push(`- Semicolons: ${prefs.codeStyle.semicolons ? 'yes' : 'no'}`);
236
+ lines.push(`- Trailing comma: ${prefs.codeStyle.trailingComma}`);
237
+ lines.push('');
238
+ // Naming
239
+ lines.push('### Naming Conventions');
240
+ lines.push(`- Variables: ${prefs.naming.variables}`);
241
+ lines.push(`- Functions: ${prefs.naming.functions}`);
242
+ lines.push(`- Classes: ${prefs.naming.classes}`);
243
+ lines.push(`- Files: ${prefs.naming.files}`);
244
+ lines.push('');
245
+ // Patterns
246
+ if (Object.keys(prefs.patterns).length > 0) {
247
+ lines.push('### Patterns');
248
+ if (prefs.patterns.importStyle)
249
+ lines.push(`- Import style: ${prefs.patterns.importStyle}`);
250
+ if (prefs.patterns.componentStyle)
251
+ lines.push(`- Components: ${prefs.patterns.componentStyle}`);
252
+ if (prefs.patterns.errorHandling)
253
+ lines.push(`- Error handling: ${prefs.patterns.errorHandling}`);
254
+ lines.push('');
255
+ }
256
+ // Libraries
257
+ if (prefs.preferredLibraries.length > 0) {
258
+ lines.push('### Preferred Libraries');
259
+ lines.push(prefs.preferredLibraries.slice(0, 10).join(', '));
260
+ lines.push('');
261
+ }
262
+ // Custom rules
263
+ if (prefs.customRules.length > 0) {
264
+ lines.push('### Custom Rules');
265
+ for (const rule of prefs.customRules) {
266
+ lines.push(`- ${rule}`);
267
+ }
268
+ lines.push('');
269
+ }
270
+ lines.push('Follow these preferences when writing code.');
271
+ return lines.join('\n');
272
+ }
273
+ /**
274
+ * Add a custom rule
275
+ */
276
+ export function addCustomRule(rule, projectRoot) {
277
+ if (projectRoot) {
278
+ const prefs = loadProjectPreferences(projectRoot);
279
+ prefs.customRules = [...new Set([...prefs.customRules, rule])];
280
+ saveProjectPreferences(projectRoot, prefs);
281
+ }
282
+ else {
283
+ const prefs = loadGlobalPreferences();
284
+ prefs.customRules = [...new Set([...prefs.customRules, rule])];
285
+ saveGlobalPreferences(prefs);
286
+ }
287
+ }
288
+ /**
289
+ * Remove a custom rule
290
+ */
291
+ export function removeCustomRule(rule, projectRoot) {
292
+ if (projectRoot) {
293
+ const prefs = loadProjectPreferences(projectRoot);
294
+ prefs.customRules = prefs.customRules.filter(r => r !== rule);
295
+ saveProjectPreferences(projectRoot, prefs);
296
+ }
297
+ else {
298
+ const prefs = loadGlobalPreferences();
299
+ prefs.customRules = prefs.customRules.filter(r => r !== rule);
300
+ saveGlobalPreferences(prefs);
301
+ }
302
+ }
303
+ /**
304
+ * Reset preferences
305
+ */
306
+ export function resetPreferences(projectRoot) {
307
+ if (projectRoot) {
308
+ const projectPrefsPath = getProjectPrefsPath(projectRoot);
309
+ if (existsSync(projectPrefsPath)) {
310
+ writeFileSync(projectPrefsPath, JSON.stringify({}, null, 2));
311
+ }
312
+ }
313
+ else {
314
+ saveGlobalPreferences({ ...DEFAULT_PREFERENCES });
315
+ }
316
+ }
317
+ /**
318
+ * Get learning status
319
+ */
320
+ export function getLearningStatus(projectRoot) {
321
+ const prefs = projectRoot
322
+ ? loadProjectPreferences(projectRoot)
323
+ : loadGlobalPreferences();
324
+ const lines = [];
325
+ lines.push(`Samples analyzed: ${prefs.sampleCount || 0}`);
326
+ lines.push(`Last updated: ${new Date(prefs.lastUpdated).toLocaleString()}`);
327
+ lines.push(`Libraries known: ${prefs.preferredLibraries.length}`);
328
+ lines.push(`Custom rules: ${prefs.customRules.length}`);
329
+ return lines.join('\n');
330
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Set current project path for local logging
3
+ */
4
+ export declare function setLogProjectPath(projectPath: string | null): void;
5
+ /**
6
+ * Logger API
7
+ */
8
+ export declare const logger: {
9
+ info: (message: string, data?: any) => void;
10
+ warn: (message: string, data?: any) => void;
11
+ error: (message: string, data?: any) => void;
12
+ debug: (message: string, data?: any) => void;
13
+ };
14
+ /**
15
+ * Log API request (both global and local)
16
+ */
17
+ export declare function logApiRequest(provider: string, model: string, messageCount: number): void;
18
+ /**
19
+ * Log API response (both global and local)
20
+ */
21
+ export declare function logApiResponse(provider: string, success: boolean, responseLength?: number, error?: string): void;
22
+ /**
23
+ * Log session operation (local only - project-specific)
24
+ */
25
+ export declare function logSession(operation: 'save' | 'load' | 'delete' | 'rename', sessionName: string, success: boolean): void;
26
+ /**
27
+ * Log application startup
28
+ */
29
+ export declare function logStartup(version: string): void;
30
+ /**
31
+ * Log application error
32
+ */
33
+ export declare function logAppError(error: Error, context?: string): void;