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,348 @@
1
+ /**
2
+ * Agent action history for undo/rollback functionality
3
+ */
4
+ import { existsSync, readFileSync, writeFileSync, unlinkSync, mkdirSync, rmSync, statSync } from 'fs';
5
+ import { dirname, join } from 'path';
6
+ import { homedir } from 'os';
7
+ // In-memory current session
8
+ let currentSession = null;
9
+ // History storage path
10
+ const HISTORY_DIR = join(homedir(), '.codeep', 'history');
11
+ /**
12
+ * Initialize history directory
13
+ */
14
+ function ensureHistoryDir() {
15
+ if (!existsSync(HISTORY_DIR)) {
16
+ mkdirSync(HISTORY_DIR, { recursive: true });
17
+ }
18
+ }
19
+ /**
20
+ * Generate unique ID
21
+ */
22
+ function generateId() {
23
+ return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
24
+ }
25
+ /**
26
+ * Start a new action session
27
+ */
28
+ export function startSession(prompt, projectRoot) {
29
+ const sessionId = generateId();
30
+ currentSession = {
31
+ id: sessionId,
32
+ startTime: Date.now(),
33
+ prompt,
34
+ actions: [],
35
+ projectRoot,
36
+ };
37
+ return sessionId;
38
+ }
39
+ /**
40
+ * End current session and save to disk
41
+ */
42
+ export function endSession() {
43
+ if (!currentSession)
44
+ return;
45
+ currentSession.endTime = Date.now();
46
+ // Only save if there were actions
47
+ if (currentSession.actions.length > 0) {
48
+ ensureHistoryDir();
49
+ const filename = `${currentSession.id}.json`;
50
+ const filepath = join(HISTORY_DIR, filename);
51
+ writeFileSync(filepath, JSON.stringify(currentSession, null, 2));
52
+ }
53
+ currentSession = null;
54
+ }
55
+ /**
56
+ * Record a file write action (before it happens)
57
+ */
58
+ export function recordWrite(path) {
59
+ if (!currentSession)
60
+ return null;
61
+ const record = {
62
+ id: generateId(),
63
+ timestamp: Date.now(),
64
+ type: 'write',
65
+ path,
66
+ previousExisted: existsSync(path),
67
+ };
68
+ // Save previous content if file existed
69
+ if (record.previousExisted) {
70
+ try {
71
+ record.previousContent = readFileSync(path, 'utf-8');
72
+ }
73
+ catch {
74
+ // Could be binary or unreadable
75
+ }
76
+ }
77
+ currentSession.actions.push(record);
78
+ return record;
79
+ }
80
+ /**
81
+ * Record a file edit action (before it happens)
82
+ */
83
+ export function recordEdit(path) {
84
+ if (!currentSession)
85
+ return null;
86
+ const record = {
87
+ id: generateId(),
88
+ timestamp: Date.now(),
89
+ type: 'edit',
90
+ path,
91
+ previousExisted: true,
92
+ };
93
+ // Save previous content
94
+ try {
95
+ record.previousContent = readFileSync(path, 'utf-8');
96
+ }
97
+ catch {
98
+ // Could be binary or unreadable
99
+ }
100
+ currentSession.actions.push(record);
101
+ return record;
102
+ }
103
+ /**
104
+ * Record a file/directory delete action (before it happens)
105
+ */
106
+ export function recordDelete(path) {
107
+ if (!currentSession)
108
+ return null;
109
+ const record = {
110
+ id: generateId(),
111
+ timestamp: Date.now(),
112
+ type: 'delete',
113
+ path,
114
+ previousExisted: true,
115
+ };
116
+ try {
117
+ const stat = statSync(path);
118
+ record.wasDirectory = stat.isDirectory();
119
+ if (!record.wasDirectory) {
120
+ record.deletedContent = readFileSync(path, 'utf-8');
121
+ }
122
+ // Note: For directories, we can't easily restore all contents
123
+ // User should use git for that
124
+ }
125
+ catch {
126
+ // Ignore errors
127
+ }
128
+ currentSession.actions.push(record);
129
+ return record;
130
+ }
131
+ /**
132
+ * Record a mkdir action
133
+ */
134
+ export function recordMkdir(path) {
135
+ if (!currentSession)
136
+ return null;
137
+ const record = {
138
+ id: generateId(),
139
+ timestamp: Date.now(),
140
+ type: 'mkdir',
141
+ path,
142
+ previousExisted: existsSync(path),
143
+ };
144
+ currentSession.actions.push(record);
145
+ return record;
146
+ }
147
+ /**
148
+ * Record a command execution (can't be undone, but tracked)
149
+ */
150
+ export function recordCommand(command, args) {
151
+ if (!currentSession)
152
+ return null;
153
+ const record = {
154
+ id: generateId(),
155
+ timestamp: Date.now(),
156
+ type: 'command',
157
+ command,
158
+ args,
159
+ };
160
+ currentSession.actions.push(record);
161
+ return record;
162
+ }
163
+ /**
164
+ * Get current session
165
+ */
166
+ export function getCurrentSession() {
167
+ return currentSession;
168
+ }
169
+ /**
170
+ * Undo the last action in current session
171
+ */
172
+ export function undoLastAction() {
173
+ if (!currentSession || currentSession.actions.length === 0) {
174
+ return { success: false, message: 'No actions to undo' };
175
+ }
176
+ // Find last non-undone action
177
+ const action = [...currentSession.actions].reverse().find(a => !a.undone);
178
+ if (!action) {
179
+ return { success: false, message: 'All actions already undone' };
180
+ }
181
+ return undoAction(action);
182
+ }
183
+ /**
184
+ * Undo a specific action
185
+ */
186
+ export function undoAction(action) {
187
+ try {
188
+ switch (action.type) {
189
+ case 'write':
190
+ if (action.previousExisted && action.previousContent !== undefined) {
191
+ // Restore previous content
192
+ writeFileSync(action.path, action.previousContent);
193
+ action.undone = true;
194
+ return { success: true, message: `Restored: ${action.path}` };
195
+ }
196
+ else if (!action.previousExisted) {
197
+ // Delete the newly created file
198
+ if (existsSync(action.path)) {
199
+ unlinkSync(action.path);
200
+ }
201
+ action.undone = true;
202
+ return { success: true, message: `Deleted new file: ${action.path}` };
203
+ }
204
+ break;
205
+ case 'edit':
206
+ if (action.previousContent !== undefined) {
207
+ writeFileSync(action.path, action.previousContent);
208
+ action.undone = true;
209
+ return { success: true, message: `Restored: ${action.path}` };
210
+ }
211
+ break;
212
+ case 'delete':
213
+ if (action.deletedContent !== undefined) {
214
+ // Recreate the file
215
+ const dir = dirname(action.path);
216
+ if (!existsSync(dir)) {
217
+ mkdirSync(dir, { recursive: true });
218
+ }
219
+ writeFileSync(action.path, action.deletedContent);
220
+ action.undone = true;
221
+ return { success: true, message: `Restored deleted file: ${action.path}` };
222
+ }
223
+ else if (action.wasDirectory) {
224
+ return { success: false, message: `Cannot restore directory: ${action.path}. Use git checkout.` };
225
+ }
226
+ break;
227
+ case 'mkdir':
228
+ if (!action.previousExisted && existsSync(action.path)) {
229
+ // Only remove if empty
230
+ try {
231
+ rmSync(action.path, { recursive: false });
232
+ action.undone = true;
233
+ return { success: true, message: `Removed directory: ${action.path}` };
234
+ }
235
+ catch {
236
+ return { success: false, message: `Cannot remove non-empty directory: ${action.path}` };
237
+ }
238
+ }
239
+ break;
240
+ case 'command':
241
+ return { success: false, message: `Cannot undo command: ${action.command} ${action.args?.join(' ')}` };
242
+ }
243
+ return { success: false, message: 'Cannot undo this action' };
244
+ }
245
+ catch (error) {
246
+ const err = error;
247
+ return { success: false, message: `Undo failed: ${err.message}` };
248
+ }
249
+ }
250
+ /**
251
+ * Undo all actions in current session
252
+ */
253
+ export function undoAllActions() {
254
+ if (!currentSession || currentSession.actions.length === 0) {
255
+ return { success: false, results: ['No actions to undo'] };
256
+ }
257
+ const results = [];
258
+ let allSuccess = true;
259
+ // Undo in reverse order
260
+ const actions = [...currentSession.actions].reverse();
261
+ for (const action of actions) {
262
+ if (action.undone)
263
+ continue;
264
+ const result = undoAction(action);
265
+ results.push(result.message);
266
+ if (!result.success)
267
+ allSuccess = false;
268
+ }
269
+ return { success: allSuccess, results };
270
+ }
271
+ /**
272
+ * Get list of recent sessions
273
+ */
274
+ export function getRecentSessions(limit = 10) {
275
+ ensureHistoryDir();
276
+ try {
277
+ const files = require('fs').readdirSync(HISTORY_DIR)
278
+ .filter((f) => f.endsWith('.json'))
279
+ .sort()
280
+ .reverse()
281
+ .slice(0, limit);
282
+ return files.map((f) => {
283
+ try {
284
+ const content = readFileSync(join(HISTORY_DIR, f), 'utf-8');
285
+ return JSON.parse(content);
286
+ }
287
+ catch {
288
+ return null;
289
+ }
290
+ }).filter(Boolean);
291
+ }
292
+ catch {
293
+ return [];
294
+ }
295
+ }
296
+ /**
297
+ * Get a specific session by ID
298
+ */
299
+ export function getSession(sessionId) {
300
+ const filepath = join(HISTORY_DIR, `${sessionId}.json`);
301
+ if (!existsSync(filepath)) {
302
+ return null;
303
+ }
304
+ try {
305
+ const content = readFileSync(filepath, 'utf-8');
306
+ return JSON.parse(content);
307
+ }
308
+ catch {
309
+ return null;
310
+ }
311
+ }
312
+ /**
313
+ * Format session for display
314
+ */
315
+ export function formatSession(session) {
316
+ const date = new Date(session.startTime).toLocaleString();
317
+ const duration = session.endTime
318
+ ? `${Math.round((session.endTime - session.startTime) / 1000)}s`
319
+ : 'ongoing';
320
+ const lines = [
321
+ `Session: ${session.id}`,
322
+ `Date: ${date}`,
323
+ `Duration: ${duration}`,
324
+ `Prompt: ${session.prompt.slice(0, 50)}${session.prompt.length > 50 ? '...' : ''}`,
325
+ `Actions (${session.actions.length}):`,
326
+ ];
327
+ for (const action of session.actions) {
328
+ const status = action.undone ? '↩️' : '✓';
329
+ const target = action.path || `${action.command} ${action.args?.join(' ')}`;
330
+ lines.push(` ${status} ${action.type}: ${target}`);
331
+ }
332
+ return lines.join('\n');
333
+ }
334
+ /**
335
+ * Clear all history
336
+ */
337
+ export function clearHistory() {
338
+ ensureHistoryDir();
339
+ try {
340
+ const files = require('fs').readdirSync(HISTORY_DIR);
341
+ for (const f of files) {
342
+ unlinkSync(join(HISTORY_DIR, f));
343
+ }
344
+ }
345
+ catch {
346
+ // Ignore errors
347
+ }
348
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Interactive Mode - agent asks clarifying questions when needed
3
+ */
4
+ export interface ClarificationQuestion {
5
+ question: string;
6
+ options?: string[];
7
+ type: 'choice' | 'text' | 'confirm';
8
+ context?: string;
9
+ }
10
+ export interface InteractiveContext {
11
+ needsClarification: boolean;
12
+ questions: ClarificationQuestion[];
13
+ originalPrompt: string;
14
+ }
15
+ /**
16
+ * Analyze prompt for ambiguity and generate clarifying questions
17
+ */
18
+ export declare function analyzeForClarification(prompt: string): InteractiveContext;
19
+ /**
20
+ * Format questions for display
21
+ */
22
+ export declare function formatQuestions(context: InteractiveContext): string;
23
+ /**
24
+ * Parse user's answers to questions
25
+ */
26
+ export declare function parseAnswers(response: string, context: InteractiveContext): Map<number, string>;
27
+ /**
28
+ * Enhance prompt with user's answers
29
+ */
30
+ export declare function enhancePromptWithAnswers(context: InteractiveContext, answers: Map<number, string>): string;
31
+ /**
32
+ * Generate interactive prompt for agent
33
+ */
34
+ export declare function getInteractiveSystemPrompt(): string;
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Interactive Mode - agent asks clarifying questions when needed
3
+ */
4
+ // Patterns that indicate ambiguity
5
+ const AMBIGUOUS_PATTERNS = [
6
+ {
7
+ pattern: /add\s+(?:an?\s+)?auth(?:entication)?/i,
8
+ question: 'What type of authentication do you want?',
9
+ options: ['JWT tokens', 'Session-based', 'OAuth (Google/GitHub)', 'Basic auth'],
10
+ type: 'choice',
11
+ },
12
+ {
13
+ pattern: /add\s+(?:a\s+)?database/i,
14
+ question: 'Which database do you want to use?',
15
+ options: ['PostgreSQL', 'MySQL', 'MongoDB', 'SQLite', 'Redis'],
16
+ type: 'choice',
17
+ },
18
+ {
19
+ pattern: /create\s+(?:an?\s+)?api/i,
20
+ question: 'What type of API do you want?',
21
+ options: ['REST API', 'GraphQL', 'gRPC', 'WebSocket'],
22
+ type: 'choice',
23
+ },
24
+ {
25
+ pattern: /add\s+(?:a\s+)?form/i,
26
+ question: 'What fields should the form have?',
27
+ type: 'text',
28
+ },
29
+ {
30
+ pattern: /add\s+(?:a\s+)?component/i,
31
+ question: 'Should this be a functional or class component?',
32
+ options: ['Functional (hooks)', 'Class component'],
33
+ type: 'choice',
34
+ },
35
+ {
36
+ pattern: /add\s+(?:a\s+)?test/i,
37
+ question: 'What testing framework should I use?',
38
+ options: ['Vitest', 'Jest', 'Mocha', 'PHPUnit', 'pytest'],
39
+ type: 'choice',
40
+ },
41
+ {
42
+ pattern: /refactor/i,
43
+ question: 'What aspect should I focus on?',
44
+ options: ['Performance', 'Readability', 'Type safety', 'Modularity', 'All of the above'],
45
+ type: 'choice',
46
+ },
47
+ {
48
+ pattern: /add\s+(?:a\s+)?state\s+management/i,
49
+ question: 'Which state management solution?',
50
+ options: ['React Context', 'Redux', 'Zustand', 'MobX', 'Jotai'],
51
+ type: 'choice',
52
+ },
53
+ {
54
+ pattern: /add\s+(?:a\s+)?(?:css|style|styling)/i,
55
+ question: 'Which styling approach?',
56
+ options: ['CSS Modules', 'Tailwind CSS', 'Styled Components', 'SCSS/Sass', 'Plain CSS'],
57
+ type: 'choice',
58
+ },
59
+ {
60
+ pattern: /deploy/i,
61
+ question: 'Where do you want to deploy?',
62
+ options: ['Vercel', 'Netlify', 'AWS', 'Docker', 'Heroku'],
63
+ type: 'choice',
64
+ },
65
+ ];
66
+ /**
67
+ * Analyze prompt for ambiguity and generate clarifying questions
68
+ */
69
+ export function analyzeForClarification(prompt) {
70
+ const questions = [];
71
+ for (const pattern of AMBIGUOUS_PATTERNS) {
72
+ if (pattern.pattern.test(prompt)) {
73
+ // Check if the prompt already specifies details
74
+ const hasDetails = checkForDetails(prompt, pattern);
75
+ if (!hasDetails) {
76
+ questions.push({
77
+ question: pattern.question,
78
+ options: pattern.options,
79
+ type: pattern.type,
80
+ });
81
+ }
82
+ }
83
+ }
84
+ return {
85
+ needsClarification: questions.length > 0,
86
+ questions,
87
+ originalPrompt: prompt,
88
+ };
89
+ }
90
+ /**
91
+ * Check if prompt already contains specific details
92
+ */
93
+ function checkForDetails(prompt, pattern) {
94
+ if (!pattern.options)
95
+ return false;
96
+ const promptLower = prompt.toLowerCase();
97
+ // Check if any option is mentioned in the prompt
98
+ for (const option of pattern.options) {
99
+ const optionWords = option.toLowerCase().split(/[\s/()]+/);
100
+ for (const word of optionWords) {
101
+ if (word.length > 3 && promptLower.includes(word)) {
102
+ return true;
103
+ }
104
+ }
105
+ }
106
+ return false;
107
+ }
108
+ /**
109
+ * Format questions for display
110
+ */
111
+ export function formatQuestions(context) {
112
+ if (!context.needsClarification) {
113
+ return '';
114
+ }
115
+ const lines = ['I have a few questions before proceeding:', ''];
116
+ for (let i = 0; i < context.questions.length; i++) {
117
+ const q = context.questions[i];
118
+ lines.push(`${i + 1}. ${q.question}`);
119
+ if (q.options) {
120
+ for (let j = 0; j < q.options.length; j++) {
121
+ lines.push(` ${String.fromCharCode(97 + j)}) ${q.options[j]}`);
122
+ }
123
+ }
124
+ lines.push('');
125
+ }
126
+ lines.push('Please answer the questions or say "proceed" to let me decide.');
127
+ return lines.join('\n');
128
+ }
129
+ /**
130
+ * Parse user's answers to questions
131
+ */
132
+ export function parseAnswers(response, context) {
133
+ const answers = new Map();
134
+ const responseLower = response.toLowerCase();
135
+ // Check for "proceed" or similar
136
+ if (/proceed|continue|decide|auto|skip/i.test(response)) {
137
+ return answers; // Empty answers = let agent decide
138
+ }
139
+ // Try to match answers
140
+ for (let i = 0; i < context.questions.length; i++) {
141
+ const q = context.questions[i];
142
+ if (q.options) {
143
+ // Check if user selected an option by letter (a, b, c)
144
+ const letterMatch = response.match(new RegExp(`${i + 1}[.:\\s]*([a-z])`, 'i'));
145
+ if (letterMatch) {
146
+ const letterIndex = letterMatch[1].toLowerCase().charCodeAt(0) - 97;
147
+ if (letterIndex >= 0 && letterIndex < q.options.length) {
148
+ answers.set(i, q.options[letterIndex]);
149
+ continue;
150
+ }
151
+ }
152
+ // Check if user mentioned an option directly
153
+ for (const option of q.options) {
154
+ if (responseLower.includes(option.toLowerCase())) {
155
+ answers.set(i, option);
156
+ break;
157
+ }
158
+ }
159
+ }
160
+ else {
161
+ // Free text - try to extract answer
162
+ const textMatch = response.match(new RegExp(`${i + 1}[.:\\s]*(.+?)(?=\\d+[.:]|$)`, 'i'));
163
+ if (textMatch) {
164
+ answers.set(i, textMatch[1].trim());
165
+ }
166
+ }
167
+ }
168
+ return answers;
169
+ }
170
+ /**
171
+ * Enhance prompt with user's answers
172
+ */
173
+ export function enhancePromptWithAnswers(context, answers) {
174
+ let enhancedPrompt = context.originalPrompt;
175
+ const specifications = [];
176
+ for (let i = 0; i < context.questions.length; i++) {
177
+ const q = context.questions[i];
178
+ const answer = answers.get(i);
179
+ if (answer) {
180
+ specifications.push(`${q.question} -> ${answer}`);
181
+ }
182
+ }
183
+ if (specifications.length > 0) {
184
+ enhancedPrompt += '\n\nUser specifications:\n- ' + specifications.join('\n- ');
185
+ }
186
+ return enhancedPrompt;
187
+ }
188
+ /**
189
+ * Generate interactive prompt for agent
190
+ */
191
+ export function getInteractiveSystemPrompt() {
192
+ return `
193
+ ## Interactive Mode
194
+ When the task is ambiguous, you may ask clarifying questions before proceeding.
195
+ Format your questions like this:
196
+
197
+ CLARIFICATION_NEEDED:
198
+ 1. [Question]
199
+ a) Option 1
200
+ b) Option 2
201
+ c) Option 3
202
+
203
+ The user will respond with their choices. Once you have enough information, proceed with the task.
204
+ Only ask questions when truly necessary - don't over-ask.
205
+ `;
206
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Secure storage for API keys using native keychain
3
+ * - macOS: Keychain
4
+ * - Linux: Secret Service API (libsecret)
5
+ * - Windows: Credential Vault
6
+ */
7
+ export interface SecureStorage {
8
+ getApiKey(providerId: string): Promise<string | null>;
9
+ setApiKey(providerId: string, apiKey: string): Promise<void>;
10
+ deleteApiKey(providerId: string): Promise<void>;
11
+ hasApiKey(providerId: string): Promise<boolean>;
12
+ }
13
+ /**
14
+ * Migrate existing plain-text API keys to keychain
15
+ */
16
+ export declare function migrateApiKeysToKeychain(config: any): Promise<void>;
17
+ export declare function createSecureStorage(config: any): SecureStorage;