codehere 0.1.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 (159) hide show
  1. package/ARCHITECTURE.md +240 -0
  2. package/CHANGELOG.md +44 -0
  3. package/CONTRIBUTING.md +171 -0
  4. package/LICENSE +22 -0
  5. package/README.md +203 -0
  6. package/dist/ast-parser.d.ts +55 -0
  7. package/dist/ast-parser.d.ts.map +1 -0
  8. package/dist/ast-parser.js +331 -0
  9. package/dist/ast-parser.js.map +1 -0
  10. package/dist/benchmark.d.ts +39 -0
  11. package/dist/benchmark.d.ts.map +1 -0
  12. package/dist/benchmark.js +195 -0
  13. package/dist/benchmark.js.map +1 -0
  14. package/dist/cache.d.ts +45 -0
  15. package/dist/cache.d.ts.map +1 -0
  16. package/dist/cache.js +182 -0
  17. package/dist/cache.js.map +1 -0
  18. package/dist/chat.d.ts +4 -0
  19. package/dist/chat.d.ts.map +1 -0
  20. package/dist/chat.js +132 -0
  21. package/dist/chat.js.map +1 -0
  22. package/dist/code-analysis.d.ts +50 -0
  23. package/dist/code-analysis.d.ts.map +1 -0
  24. package/dist/code-analysis.js +327 -0
  25. package/dist/code-analysis.js.map +1 -0
  26. package/dist/context.d.ts +44 -0
  27. package/dist/context.d.ts.map +1 -0
  28. package/dist/context.js +187 -0
  29. package/dist/context.js.map +1 -0
  30. package/dist/docs.d.ts +21 -0
  31. package/dist/docs.d.ts.map +1 -0
  32. package/dist/docs.js +147 -0
  33. package/dist/docs.js.map +1 -0
  34. package/dist/edit.d.ts +38 -0
  35. package/dist/edit.d.ts.map +1 -0
  36. package/dist/edit.js +594 -0
  37. package/dist/edit.js.map +1 -0
  38. package/dist/embed.d.ts +18 -0
  39. package/dist/embed.d.ts.map +1 -0
  40. package/dist/embed.js +479 -0
  41. package/dist/embed.js.map +1 -0
  42. package/dist/error-handler.d.ts +76 -0
  43. package/dist/error-handler.d.ts.map +1 -0
  44. package/dist/error-handler.js +213 -0
  45. package/dist/error-handler.js.map +1 -0
  46. package/dist/formatter.d.ts +25 -0
  47. package/dist/formatter.d.ts.map +1 -0
  48. package/dist/formatter.js +148 -0
  49. package/dist/formatter.js.map +1 -0
  50. package/dist/git.d.ts +55 -0
  51. package/dist/git.d.ts.map +1 -0
  52. package/dist/git.js +198 -0
  53. package/dist/git.js.map +1 -0
  54. package/dist/index.d.ts +3 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +964 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/intelligent-retrieval.d.ts +41 -0
  59. package/dist/intelligent-retrieval.d.ts.map +1 -0
  60. package/dist/intelligent-retrieval.js +265 -0
  61. package/dist/intelligent-retrieval.js.map +1 -0
  62. package/dist/iterative-refinement.d.ts +31 -0
  63. package/dist/iterative-refinement.d.ts.map +1 -0
  64. package/dist/iterative-refinement.js +172 -0
  65. package/dist/iterative-refinement.js.map +1 -0
  66. package/dist/learning.d.ts +69 -0
  67. package/dist/learning.d.ts.map +1 -0
  68. package/dist/learning.js +233 -0
  69. package/dist/learning.js.map +1 -0
  70. package/dist/log.d.ts +4 -0
  71. package/dist/log.d.ts.map +1 -0
  72. package/dist/log.js +31 -0
  73. package/dist/log.js.map +1 -0
  74. package/dist/migrate.d.ts +33 -0
  75. package/dist/migrate.d.ts.map +1 -0
  76. package/dist/migrate.js +133 -0
  77. package/dist/migrate.js.map +1 -0
  78. package/dist/monitoring.d.ts +75 -0
  79. package/dist/monitoring.d.ts.map +1 -0
  80. package/dist/monitoring.js +248 -0
  81. package/dist/monitoring.js.map +1 -0
  82. package/dist/parallel-processor.d.ts +43 -0
  83. package/dist/parallel-processor.d.ts.map +1 -0
  84. package/dist/parallel-processor.js +308 -0
  85. package/dist/parallel-processor.js.map +1 -0
  86. package/dist/planner.d.ts +47 -0
  87. package/dist/planner.d.ts.map +1 -0
  88. package/dist/planner.js +198 -0
  89. package/dist/planner.js.map +1 -0
  90. package/dist/policy.d.ts +3 -0
  91. package/dist/policy.d.ts.map +1 -0
  92. package/dist/policy.js +26 -0
  93. package/dist/policy.js.map +1 -0
  94. package/dist/query-optimizer.d.ts +32 -0
  95. package/dist/query-optimizer.d.ts.map +1 -0
  96. package/dist/query-optimizer.js +205 -0
  97. package/dist/query-optimizer.js.map +1 -0
  98. package/dist/refactor.d.ts +27 -0
  99. package/dist/refactor.d.ts.map +1 -0
  100. package/dist/refactor.js +118 -0
  101. package/dist/refactor.js.map +1 -0
  102. package/dist/review.d.ts +31 -0
  103. package/dist/review.d.ts.map +1 -0
  104. package/dist/review.js +206 -0
  105. package/dist/review.js.map +1 -0
  106. package/dist/scaffold.d.ts +14 -0
  107. package/dist/scaffold.d.ts.map +1 -0
  108. package/dist/scaffold.js +85 -0
  109. package/dist/scaffold.js.map +1 -0
  110. package/dist/search.d.ts +19 -0
  111. package/dist/search.d.ts.map +1 -0
  112. package/dist/search.js +198 -0
  113. package/dist/search.js.map +1 -0
  114. package/dist/session.d.ts +17 -0
  115. package/dist/session.d.ts.map +1 -0
  116. package/dist/session.js +301 -0
  117. package/dist/session.js.map +1 -0
  118. package/dist/task-verification.d.ts +39 -0
  119. package/dist/task-verification.d.ts.map +1 -0
  120. package/dist/task-verification.js +336 -0
  121. package/dist/task-verification.js.map +1 -0
  122. package/dist/test_cohere.d.ts +2 -0
  123. package/dist/test_cohere.d.ts.map +1 -0
  124. package/dist/test_cohere.js +68 -0
  125. package/dist/test_cohere.js.map +1 -0
  126. package/dist/test_env.d.ts +2 -0
  127. package/dist/test_env.d.ts.map +1 -0
  128. package/dist/test_env.js +24 -0
  129. package/dist/test_env.js.map +1 -0
  130. package/dist/test_retrieval.d.ts +2 -0
  131. package/dist/test_retrieval.d.ts.map +1 -0
  132. package/dist/test_retrieval.js +84 -0
  133. package/dist/test_retrieval.js.map +1 -0
  134. package/dist/testgen.d.ts +24 -0
  135. package/dist/testgen.d.ts.map +1 -0
  136. package/dist/testgen.js +167 -0
  137. package/dist/testgen.js.map +1 -0
  138. package/dist/token-optimizer.d.ts +20 -0
  139. package/dist/token-optimizer.d.ts.map +1 -0
  140. package/dist/token-optimizer.js +277 -0
  141. package/dist/token-optimizer.js.map +1 -0
  142. package/dist/types.d.ts +36 -0
  143. package/dist/types.d.ts.map +1 -0
  144. package/dist/types.js +2 -0
  145. package/dist/types.js.map +1 -0
  146. package/dist/ui.d.ts +54 -0
  147. package/dist/ui.d.ts.map +1 -0
  148. package/dist/ui.js +295 -0
  149. package/dist/ui.js.map +1 -0
  150. package/dist/verify_db.d.ts +2 -0
  151. package/dist/verify_db.d.ts.map +1 -0
  152. package/dist/verify_db.js +52 -0
  153. package/dist/verify_db.js.map +1 -0
  154. package/package.json +71 -0
  155. package/templates/next-page/app/layout.tsx +19 -0
  156. package/templates/next-page/app/page.tsx +10 -0
  157. package/templates/next-page/package.json +22 -0
  158. package/templates/node-api/index.js +57 -0
  159. package/templates/node-api/package.json +13 -0
package/dist/index.js ADDED
@@ -0,0 +1,964 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { config } from 'dotenv';
4
+ import { join, dirname } from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import { indexRepositoryParallel } from './embed.js';
7
+ import { searchIntelligent } from './search.js';
8
+ import { chatWithContext } from './chat.js';
9
+ import { generateAndApplyEdit, generateEditDryRun } from './edit.js';
10
+ import { scaffoldTemplate, listTemplates } from './scaffold.js';
11
+ import { initLogFile, logEntry } from './log.js';
12
+ import { performHealthCheck, formatHealthStatus, getMetrics } from './monitoring.js';
13
+ import { refactorMultiFile, findFilesByPattern } from './refactor.js';
14
+ import { getGitStatus, formatGitStatus, getAllGitDiffs, analyzeDiff } from './git.js';
15
+ import { planTask, executePlan, formatPlan } from './planner.js';
16
+ import { reviewFile, formatReview } from './review.js';
17
+ import { formatLearningInsights, getPerformanceInsights } from './learning.js';
18
+ import { getCacheStats, clearAllCaches } from './cache.js';
19
+ import { planMigration, formatMigrationPlan } from './migrate.js';
20
+ import { generateFileDocs, generateREADME, updateDocs, saveDocs } from './docs.js';
21
+ import { generateTests, getTestFilePath, saveTests, improveTests } from './testgen.js';
22
+ import { startSession } from './session.js';
23
+ import { cleanResponse } from './formatter.js';
24
+ import { readFileSync } from 'fs';
25
+ import { colors, createSpinner, createBanner, createTable, success, error, warning, info, formatPath, formatNumber, formatScore, formatPercent, sectionHeader, newline, statusIndicator, createWelcomeMessage, } from './ui.js';
26
+ // Load environment variables - try agent directory first, then current directory
27
+ try {
28
+ const __filename = fileURLToPath(import.meta.url);
29
+ const __dirname = dirname(__filename);
30
+ const agentDir = join(__dirname, '..');
31
+ config({ path: join(agentDir, '.env') });
32
+ }
33
+ catch {
34
+ // Fallback to current directory
35
+ }
36
+ config(); // Also try current directory
37
+ const program = new Command();
38
+ program
39
+ .name('codehere')
40
+ .description('AI-powered coding assistant with local embeddings and semantic search')
41
+ .version('0.1.0')
42
+ .configureHelp({
43
+ helpWidth: 80,
44
+ sortSubcommands: true,
45
+ })
46
+ .addHelpText('beforeAll', () => {
47
+ return createWelcomeMessage();
48
+ });
49
+ // Start interactive session when no command provided
50
+ program
51
+ .action(async () => {
52
+ // Check if we should start interactive session
53
+ if (process.stdin.isTTY) {
54
+ // Interactive mode - start session
55
+ await startSession();
56
+ }
57
+ else {
58
+ // Non-interactive - show help
59
+ console.log(createWelcomeMessage());
60
+ newline();
61
+ console.log(colors.bold('Available Commands:'));
62
+ newline();
63
+ console.log(colors.cyan(' codehere') + ' Start interactive session');
64
+ console.log(colors.cyan(' codehere index') + ' Build embeddings index');
65
+ console.log(colors.cyan(' codehere ask "<question>"') + ' Ask questions');
66
+ console.log(colors.cyan(' codehere explain <file>') + ' Explain a file');
67
+ console.log(colors.cyan(' codehere fix <file> "<instruction>"') + ' Edit files');
68
+ newline();
69
+ console.log(colors.dim('Run "codehere --help" for all commands'));
70
+ newline();
71
+ }
72
+ });
73
+ program
74
+ .command('index')
75
+ .description('Build embeddings index of the repository')
76
+ .option('-r, --repo <path>', 'Repository path (default: current directory)')
77
+ .action(async (options) => {
78
+ initLogFile();
79
+ if (!process.env.COHERE_API_KEY) {
80
+ newline();
81
+ console.error(error('COHERE_API_KEY not found in environment'));
82
+ newline();
83
+ console.log(colors.bold('Setup Instructions:'));
84
+ console.log(' 1. Get your API key from: https://dashboard.cohere.com/api-keys');
85
+ console.log(' 2. Create a .env file in your project root:');
86
+ console.log(colors.cyan(' echo "COHERE_API_KEY=your_key_here" > .env'));
87
+ console.log(' 3. Run the command again');
88
+ newline();
89
+ process.exit(1);
90
+ }
91
+ try {
92
+ console.log(createBanner('Indexing Repository', 'Building semantic search index'));
93
+ newline();
94
+ const repoPath = options.repo || process.cwd();
95
+ // NEW: Use parallel indexing with caching
96
+ await indexRepositoryParallel(repoPath, {
97
+ useCache: true,
98
+ maxConcurrency: 5,
99
+ });
100
+ logEntry({
101
+ timestamp: new Date().toISOString(),
102
+ command: 'index',
103
+ policyStatus: 'allowed',
104
+ });
105
+ newline();
106
+ console.log(success('Indexing complete!'));
107
+ }
108
+ catch (err) {
109
+ console.error(error('Error indexing repository:'), err instanceof Error ? err.message : String(err));
110
+ process.exit(1);
111
+ }
112
+ });
113
+ program
114
+ .command('ask')
115
+ .description('Ask questions about your codebase using semantic search')
116
+ .argument('<question>', 'The question to ask about your code')
117
+ .alias('q')
118
+ .addHelpText('after', `
119
+ Examples:
120
+ $ codehere ask "How does authentication work?"
121
+ $ codehere ask "Where is the user login function?"
122
+ $ codehere q "Explain the database schema"
123
+
124
+ This command will:
125
+ • Search your indexed codebase for relevant code
126
+ • Use AI to generate a contextual answer
127
+ • Show which files were used as context
128
+
129
+ Make sure to run "codehere index" first!
130
+ `)
131
+ .action(async (question) => {
132
+ initLogFile();
133
+ if (!process.env.COHERE_API_KEY) {
134
+ newline();
135
+ console.error(error('COHERE_API_KEY not found in environment'));
136
+ newline();
137
+ console.log(colors.bold('Setup Instructions:'));
138
+ console.log(' 1. Get your API key from: https://dashboard.cohere.com/api-keys');
139
+ console.log(' 2. Create a .env file in your project root:');
140
+ console.log(colors.cyan(' echo "COHERE_API_KEY=your_key_here" > .env'));
141
+ console.log(' 3. Run the command again');
142
+ newline();
143
+ process.exit(1);
144
+ }
145
+ try {
146
+ console.log(createBanner('Ask Question', 'Semantic search + AI chat'));
147
+ newline();
148
+ // Display query
149
+ console.log(colors.bold('Query:'));
150
+ console.log(colors.cyan(' ' + question));
151
+ newline();
152
+ // NEW: Intelligent search with AST-based understanding
153
+ const searchSpinner = createSpinner('Searching with intelligent context (AST + code structure)...');
154
+ searchSpinner.start();
155
+ const searchResult = await searchIntelligent(question, {
156
+ baseDir: process.cwd(),
157
+ });
158
+ searchSpinner.stop();
159
+ if (searchResult.chunks.length === 0) {
160
+ newline();
161
+ console.log(warning('No relevant code found.'));
162
+ console.log(info('Make sure to run "codehere index" first to build the search index.'));
163
+ newline();
164
+ return;
165
+ }
166
+ // Display search results
167
+ sectionHeader('Search Results (Intelligent)');
168
+ const fileTable = createTable(['File', 'Relevance', 'Preview'], searchResult.chunks.slice(0, 10).map(r => [
169
+ formatPath(r.filepath),
170
+ formatScore(r.score),
171
+ colors.dim(r.content.substring(0, 50) + '...'),
172
+ ]));
173
+ console.log(fileTable.toString());
174
+ const retrievedFiles = [...new Set(searchResult.chunks.map(r => r.filepath))];
175
+ console.log(colors.dim(`\nFound ${formatNumber(searchResult.chunks.length)} optimized chunks from ${formatNumber(retrievedFiles.length)} file(s)`));
176
+ console.log(colors.dim(`Token reduction: ${formatPercent(searchResult.reductionPercent)} (${formatNumber(searchResult.tokenEstimate)} tokens)`));
177
+ newline();
178
+ // Generate response with spinner
179
+ const responseSpinner = createSpinner('Generating response with optimized context...');
180
+ responseSpinner.start();
181
+ const response = await chatWithContext(question, searchResult.chunks);
182
+ responseSpinner.stop();
183
+ newline();
184
+ // Display response - clean format
185
+ sectionHeader('Response');
186
+ const formattedResponse = cleanResponse(response);
187
+ console.log(formattedResponse);
188
+ newline();
189
+ logEntry({
190
+ timestamp: new Date().toISOString(),
191
+ command: 'ask',
192
+ query: question,
193
+ policyStatus: 'allowed',
194
+ retrievedFiles,
195
+ });
196
+ }
197
+ catch (err) {
198
+ console.error(error('Error:'), err instanceof Error ? err.message : String(err));
199
+ process.exit(1);
200
+ }
201
+ });
202
+ program
203
+ .command('fix')
204
+ .description('Apply AI-generated code edits to a file')
205
+ .argument('<file>', 'Path to the file to edit')
206
+ .argument('<instruction>', 'Natural language instruction for the edit')
207
+ .option('--dry-run', 'Show changes without applying them')
208
+ .alias('edit')
209
+ .addHelpText('after', `
210
+ Examples:
211
+ $ codehere fix src/auth.ts "Add error handling for invalid tokens"
212
+ $ codehere fix utils/helpers.js "Rename function foo() to bar()"
213
+ $ codehere edit app.js "Add input validation"
214
+
215
+ This command will:
216
+ • Generate a unified diff using AI
217
+ • Apply safety policy checks
218
+ • Apply the changes to your file
219
+ • Log the operation for audit
220
+
221
+ Safety policies:
222
+ • Maximum 50 lines changed per edit
223
+ • Cannot edit files in restricted folders (infra/, billing/)
224
+ • Only one file can be edited at a time
225
+ `)
226
+ .action(async (file, instruction, options) => {
227
+ initLogFile();
228
+ if (!process.env.COHERE_API_KEY) {
229
+ newline();
230
+ console.error(error('COHERE_API_KEY not found in environment'));
231
+ newline();
232
+ console.log(colors.bold('Setup Instructions:'));
233
+ console.log(' 1. Get your API key from: https://dashboard.cohere.com/api-keys');
234
+ console.log(' 2. Create a .env file in your project root:');
235
+ console.log(colors.cyan(' echo "COHERE_API_KEY=your_key_here" > .env'));
236
+ console.log(' 3. Run the command again');
237
+ newline();
238
+ process.exit(1);
239
+ }
240
+ try {
241
+ console.log(createBanner('Edit File', 'AI-powered code editing'));
242
+ newline();
243
+ // Display edit info
244
+ console.log(colors.bold('File:') + ' ' + formatPath(file));
245
+ console.log(colors.bold('Instruction:') + ' ' + colors.cyan(instruction));
246
+ newline();
247
+ // Generate diff with spinner
248
+ const diffSpinner = createSpinner('Generating code changes...');
249
+ diffSpinner.start();
250
+ // Check if file exists
251
+ const { existsSync } = await import('fs');
252
+ const fullPath = join(process.cwd(), file);
253
+ if (!existsSync(fullPath)) {
254
+ diffSpinner.stop();
255
+ newline();
256
+ console.error(error(`File not found: ${file}`));
257
+ console.log(info(`Looking for: ${fullPath}`));
258
+ console.log(info('Tip: Use a relative path from your current directory'));
259
+ newline();
260
+ process.exit(1);
261
+ }
262
+ const isDryRun = options.dryRun || false;
263
+ const result = isDryRun
264
+ ? await generateEditDryRun(file, instruction)
265
+ : await generateAndApplyEdit(file, instruction);
266
+ diffSpinner.stop();
267
+ if (result.success) {
268
+ newline();
269
+ // Policy check display
270
+ if (result.policyResult) {
271
+ sectionHeader('Policy Check');
272
+ const policyTable = createTable(['Check', 'Status'], [
273
+ ['Line Limit (≤50)', result.policyResult.allowed ? statusIndicator('success') + ' Passed' : statusIndicator('error') + ' Failed'],
274
+ ['Restricted Folders', result.policyResult.allowed ? statusIndicator('success') + ' Passed' : statusIndicator('error') + ' Failed'],
275
+ ['Single File Only', result.policyResult.allowed ? statusIndicator('success') + ' Passed' : statusIndicator('error') + ' Failed'],
276
+ ]);
277
+ console.log(policyTable.toString());
278
+ newline();
279
+ }
280
+ // Diff stats
281
+ if (result.diffStats) {
282
+ sectionHeader('Changes');
283
+ const statsTable = createTable(['Metric', 'Value'], [
284
+ ['Lines Added', formatNumber(result.diffStats.linesAdded)],
285
+ ['Lines Removed', formatNumber(result.diffStats.linesRemoved)],
286
+ ['Total Changes', formatNumber(result.diffStats.linesAdded + result.diffStats.linesRemoved)],
287
+ ['Files Changed', formatNumber(result.diffStats.filesChanged)],
288
+ ]);
289
+ console.log(statsTable.toString());
290
+ newline();
291
+ }
292
+ // Show diff in dry-run mode
293
+ if (isDryRun && 'diffText' in result && result.diffText) {
294
+ sectionHeader('Generated Diff (Dry Run)');
295
+ console.log(colors.dim('---'));
296
+ console.log(result.diffText);
297
+ console.log(colors.dim('---'));
298
+ newline();
299
+ if ('syntaxValid' in result) {
300
+ if (result.syntaxValid === false) {
301
+ console.log(warning('⚠️ Syntax validation failed'));
302
+ console.log(colors.yellow(' ' + (result.error || 'Unknown syntax error')));
303
+ newline();
304
+ }
305
+ else if (result.syntaxValid === true) {
306
+ console.log(success('✓ Syntax validation passed'));
307
+ newline();
308
+ }
309
+ }
310
+ }
311
+ if (isDryRun) {
312
+ console.log(info('Dry-run mode: No changes were applied to the file.'));
313
+ console.log(info('Remove --dry-run to apply changes.'));
314
+ }
315
+ else {
316
+ console.log(success('Edit applied successfully'));
317
+ }
318
+ logEntry({
319
+ timestamp: new Date().toISOString(),
320
+ command: 'fix',
321
+ query: instruction,
322
+ filepath: file,
323
+ diffStats: result.diffStats,
324
+ policyStatus: result.policyResult?.allowed ? 'allowed' : 'rejected',
325
+ });
326
+ }
327
+ else {
328
+ newline();
329
+ // Show policy rejection reason
330
+ newline();
331
+ sectionHeader('Error');
332
+ if (result.policyResult && !result.policyResult.allowed) {
333
+ console.log(error('Policy check failed'));
334
+ console.log(colors.yellow(' ' + (result.policyResult.reason || 'Unknown reason')));
335
+ }
336
+ else {
337
+ console.log(error('Edit failed'));
338
+ console.log(colors.red(' ' + (result.error || 'Unknown error')));
339
+ }
340
+ logEntry({
341
+ timestamp: new Date().toISOString(),
342
+ command: 'fix',
343
+ query: instruction,
344
+ filepath: file,
345
+ diffStats: result.diffStats,
346
+ policyStatus: 'rejected',
347
+ policyReason: result.error,
348
+ });
349
+ process.exit(1);
350
+ }
351
+ }
352
+ catch (err) {
353
+ console.error(error('Error:'), err instanceof Error ? err.message : String(err));
354
+ process.exit(1);
355
+ }
356
+ });
357
+ program
358
+ .command('debug-search')
359
+ .description('Debug search: show top results with scores and previews')
360
+ .argument('<query>', 'Search query')
361
+ .option('-n, --top <number>', 'Number of results to show', '10')
362
+ .action(async (query, options) => {
363
+ initLogFile();
364
+ if (!process.env.COHERE_API_KEY) {
365
+ newline();
366
+ console.error(error('COHERE_API_KEY not found in environment'));
367
+ process.exit(1);
368
+ }
369
+ try {
370
+ console.log(createBanner('Debug Search', 'Show search results with scores'));
371
+ newline();
372
+ console.log(colors.bold('Query:'));
373
+ console.log(colors.cyan(' ' + query));
374
+ newline();
375
+ const searchSpinner = createSpinner('Searching...');
376
+ searchSpinner.start();
377
+ const searchResult = await searchIntelligent(query, {
378
+ baseDir: process.cwd(),
379
+ });
380
+ searchSpinner.stop();
381
+ if (searchResult.chunks.length === 0) {
382
+ newline();
383
+ console.log(warning('No results found.'));
384
+ return;
385
+ }
386
+ const topN = parseInt(options.top || '10', 10);
387
+ const topResults = searchResult.chunks.slice(0, topN);
388
+ sectionHeader('Search Results');
389
+ const fileTable = createTable(['Rank', 'File', 'Score', 'Preview'], topResults.map((r, idx) => [
390
+ String(idx + 1),
391
+ formatPath(r.filepath),
392
+ formatScore(r.score),
393
+ colors.dim(r.content.substring(0, 60) + '...'),
394
+ ]));
395
+ console.log(fileTable.toString());
396
+ newline();
397
+ console.log(colors.dim(`Showing ${topResults.length} of ${searchResult.chunks.length} results`));
398
+ console.log(colors.dim(`Token reduction: ${formatPercent(searchResult.reductionPercent)}`));
399
+ }
400
+ catch (err) {
401
+ console.error(error('Search error:'), err instanceof Error ? err.message : String(err));
402
+ process.exit(1);
403
+ }
404
+ });
405
+ program
406
+ .command('explain')
407
+ .description('Get AI explanation of a code file')
408
+ .argument('<file>', 'Path to the file to explain')
409
+ .alias('e')
410
+ .addHelpText('after', `
411
+ Examples:
412
+ $ codehere explain src/auth.ts
413
+ $ codehere explain utils/helpers.js
414
+ $ codehere e app.js
415
+
416
+ This command will:
417
+ • Read the file content
418
+ • Generate an AI explanation
419
+ • Show file structure, functions, and logic
420
+ `)
421
+ .action(async (file) => {
422
+ initLogFile();
423
+ if (!process.env.COHERE_API_KEY) {
424
+ newline();
425
+ console.error(error('COHERE_API_KEY not found in environment'));
426
+ newline();
427
+ console.log(colors.bold('Setup Instructions:'));
428
+ console.log(' 1. Get your API key from: https://dashboard.cohere.com/api-keys');
429
+ console.log(' 2. Create a .env file in your project root:');
430
+ console.log(colors.cyan(' echo "COHERE_API_KEY=your_key_here" > .env'));
431
+ console.log(' 3. Run the command again');
432
+ newline();
433
+ process.exit(1);
434
+ }
435
+ try {
436
+ console.log(createBanner('Explain File', 'AI-powered code explanation'));
437
+ newline();
438
+ const fullPath = join(process.cwd(), file);
439
+ // Check if file exists
440
+ const { existsSync } = await import('fs');
441
+ if (!existsSync(fullPath)) {
442
+ newline();
443
+ console.error(error(`File not found: ${file}`));
444
+ console.log(info(`Looking for: ${fullPath}`));
445
+ console.log(info('Tip: Use a relative path from your current directory'));
446
+ newline();
447
+ process.exit(1);
448
+ }
449
+ const content = readFileSync(fullPath, 'utf-8');
450
+ // File info
451
+ const lines = content.split('\n').length;
452
+ const size = (content.length / 1024).toFixed(2);
453
+ console.log(colors.bold('File:') + ' ' + formatPath(file));
454
+ console.log(colors.dim(` Lines: ${lines} | Size: ${size} KB`));
455
+ newline();
456
+ const context = [{
457
+ filepath: file,
458
+ content: content,
459
+ score: 1.0,
460
+ }];
461
+ const question = `Explain this file: ${file}`;
462
+ // Generate explanation with spinner
463
+ const explainSpinner = createSpinner('Generating explanation...');
464
+ explainSpinner.start();
465
+ const response = await chatWithContext(question, context);
466
+ explainSpinner.stop();
467
+ newline();
468
+ // Display explanation - clean format
469
+ sectionHeader('Explanation');
470
+ const formattedResponse = cleanResponse(response);
471
+ console.log(formattedResponse);
472
+ newline();
473
+ logEntry({
474
+ timestamp: new Date().toISOString(),
475
+ command: 'explain',
476
+ query: question,
477
+ filepath: file,
478
+ policyStatus: 'allowed',
479
+ retrievedFiles: [file],
480
+ });
481
+ }
482
+ catch (err) {
483
+ console.error(error('Error:'), err instanceof Error ? err.message : String(err));
484
+ process.exit(1);
485
+ }
486
+ });
487
+ program
488
+ .command('health')
489
+ .description('Check system health and metrics')
490
+ .option('-m, --metrics', 'Show detailed metrics')
491
+ .action((options) => {
492
+ const health = performHealthCheck();
493
+ newline();
494
+ console.log(createBanner('System Health', 'Enterprise monitoring'));
495
+ newline();
496
+ console.log(formatHealthStatus(health));
497
+ if (options.metrics) {
498
+ newline();
499
+ sectionHeader('Metrics');
500
+ const metrics = getMetrics();
501
+ const metricsTable = createTable(['Metric', 'Value'], [
502
+ ['Total Operations', formatNumber(metrics.operations.total)],
503
+ ['Successful', formatNumber(metrics.operations.successful)],
504
+ ['Failed', formatNumber(metrics.operations.failed)],
505
+ ['Retries', formatNumber(metrics.operations.retries)],
506
+ ['Avg Response Time', `${metrics.performance.avgResponseTime.toFixed(2)}ms`],
507
+ ['API Calls', formatNumber(metrics.performance.apiCalls)],
508
+ ['Cache Hits', formatNumber(metrics.performance.cacheHits)],
509
+ ]);
510
+ console.log(metricsTable.toString());
511
+ if (Object.keys(metrics.errors.byType).length > 0) {
512
+ newline();
513
+ sectionHeader('Error Summary');
514
+ const errorTable = createTable(['Error Type', 'Count'], Object.entries(metrics.errors.byType).map(([type, count]) => [
515
+ type,
516
+ formatNumber(count),
517
+ ]));
518
+ console.log(errorTable.toString());
519
+ }
520
+ }
521
+ newline();
522
+ if (health.status === 'unhealthy') {
523
+ process.exit(1);
524
+ }
525
+ });
526
+ program
527
+ .command('refactor')
528
+ .description('Refactor multiple files with coordinated changes')
529
+ .argument('<pattern>', 'File pattern (e.g., "src/**/*.ts") or comma-separated files')
530
+ .argument('<instruction>', 'Refactoring instruction')
531
+ .option('--dry-run', 'Show changes without applying')
532
+ .option('--max-files <number>', 'Maximum files to process', '10')
533
+ .action(async (pattern, instruction, options) => {
534
+ initLogFile();
535
+ if (!process.env.COHERE_API_KEY) {
536
+ newline();
537
+ console.error(error('COHERE_API_KEY not found in environment'));
538
+ process.exit(1);
539
+ }
540
+ try {
541
+ console.log(createBanner('Multi-File Refactor', 'Enterprise-grade cross-file operations'));
542
+ newline();
543
+ // Parse files from pattern
544
+ let files;
545
+ if (pattern.includes(',')) {
546
+ files = pattern.split(',').map(f => f.trim());
547
+ }
548
+ else {
549
+ files = findFilesByPattern(pattern);
550
+ }
551
+ if (files.length === 0) {
552
+ console.log(warning('No files found matching pattern'));
553
+ return;
554
+ }
555
+ console.log(colors.bold('Files to refactor:'));
556
+ files.slice(0, 10).forEach(f => console.log(colors.cyan(' ' + f)));
557
+ if (files.length > 10) {
558
+ console.log(colors.dim(` ... and ${files.length - 10} more`));
559
+ }
560
+ newline();
561
+ console.log(colors.bold('Instruction:') + ' ' + colors.cyan(instruction));
562
+ newline();
563
+ const maxFiles = parseInt(options.maxFiles || '10', 10);
564
+ const result = await refactorMultiFile(files.slice(0, maxFiles), instruction, {
565
+ dryRun: options.dryRun || false,
566
+ maxFiles,
567
+ });
568
+ // Display results
569
+ sectionHeader('Refactoring Results');
570
+ const resultsTable = createTable(['Metric', 'Value'], [
571
+ ['Files Processed', formatNumber(files.length)],
572
+ ['Files Changed', formatNumber(result.filesChanged.length)],
573
+ ['Total Lines Added', formatNumber(result.totalChanges.linesAdded)],
574
+ ['Total Lines Removed', formatNumber(result.totalChanges.linesRemoved)],
575
+ ['Errors', formatNumber(result.errors.length)],
576
+ ]);
577
+ console.log(resultsTable.toString());
578
+ if (result.errors.length > 0) {
579
+ newline();
580
+ sectionHeader('Errors');
581
+ result.errors.forEach(err => {
582
+ console.log(error(`${err.file}: ${err.error}`));
583
+ });
584
+ }
585
+ if (result.filesChanged.length > 0) {
586
+ newline();
587
+ console.log(success(`Refactoring ${options.dryRun ? 'planned' : 'completed'} successfully`));
588
+ }
589
+ }
590
+ catch (err) {
591
+ console.error(error('Error:'), err instanceof Error ? err.message : String(err));
592
+ process.exit(1);
593
+ }
594
+ });
595
+ program
596
+ .command('review')
597
+ .description('Review git changes and suggest improvements')
598
+ .option('-f, --file <file>', 'Review specific file')
599
+ .action((options) => {
600
+ initLogFile();
601
+ try {
602
+ console.log(createBanner('Code Review', 'Git-aware code analysis'));
603
+ newline();
604
+ const status = getGitStatus();
605
+ if (!status.isRepo) {
606
+ console.log(warning('Not a git repository'));
607
+ return;
608
+ }
609
+ console.log(formatGitStatus(status));
610
+ newline();
611
+ if (!status.hasChanges) {
612
+ console.log(info('No changes to review'));
613
+ return;
614
+ }
615
+ // Get diffs
616
+ const { getGitDiff } = require('./git.js');
617
+ const diffs = options.file
618
+ ? (() => {
619
+ const diff = getGitDiff(options.file);
620
+ return diff ? new Map([[options.file, diff]]) : new Map();
621
+ })()
622
+ : getAllGitDiffs();
623
+ if (diffs.size === 0) {
624
+ console.log(info('No diffs found'));
625
+ return;
626
+ }
627
+ sectionHeader('Change Analysis');
628
+ for (const [file, diff] of diffs.entries()) {
629
+ const analysis = analyzeDiff(diff);
630
+ console.log(colors.bold(file));
631
+ console.log(colors.dim(` ${analysis.summary}`));
632
+ console.log(colors.dim(` Risk: ${analysis.riskLevel.toUpperCase()}`));
633
+ if (analysis.suggestions.length > 0) {
634
+ console.log(colors.yellow(' Suggestions:'));
635
+ analysis.suggestions.forEach(s => {
636
+ console.log(colors.yellow(` - ${s}`));
637
+ });
638
+ }
639
+ newline();
640
+ }
641
+ }
642
+ catch (err) {
643
+ console.error(error('Error:'), err instanceof Error ? err.message : String(err));
644
+ process.exit(1);
645
+ }
646
+ });
647
+ program
648
+ .command('plan')
649
+ .description('Plan a complex multi-step task')
650
+ .argument('<instruction>', 'Complex instruction to plan')
651
+ .option('-e, --execute', 'Execute the plan after creating it')
652
+ .action(async (instruction, options) => {
653
+ initLogFile();
654
+ if (!process.env.COHERE_API_KEY) {
655
+ console.error(error('COHERE_API_KEY not found in environment'));
656
+ process.exit(1);
657
+ }
658
+ try {
659
+ console.log(createBanner('Task Planning', 'Intelligent multi-step execution'));
660
+ newline();
661
+ console.log(colors.bold('Instruction:') + ' ' + colors.cyan(instruction));
662
+ newline();
663
+ const plan = await planTask(instruction);
664
+ sectionHeader('Execution Plan');
665
+ console.log(formatPlan(plan));
666
+ if (options.execute) {
667
+ newline();
668
+ const searchSpinner = createSpinner('Executing plan...');
669
+ searchSpinner.start();
670
+ const result = await executePlan(plan, { instruction });
671
+ searchSpinner.stop();
672
+ newline();
673
+ sectionHeader('Execution Results');
674
+ const resultsTable = createTable(['Metric', 'Value'], [
675
+ ['Tasks Completed', formatNumber(result.completedTasks)],
676
+ ['Total Tasks', formatNumber(plan.tasks.length)],
677
+ ['Success', result.success ? statusIndicator('success') + ' Yes' : statusIndicator('error') + ' No'],
678
+ ['Errors', formatNumber(result.errors.length)],
679
+ ]);
680
+ console.log(resultsTable.toString());
681
+ if (result.errors.length > 0) {
682
+ newline();
683
+ sectionHeader('Errors');
684
+ result.errors.forEach(err => {
685
+ console.log(error(`${err.task}: ${err.error}`));
686
+ });
687
+ }
688
+ }
689
+ }
690
+ catch (err) {
691
+ console.error(error('Error:'), err instanceof Error ? err.message : String(err));
692
+ process.exit(1);
693
+ }
694
+ });
695
+ program
696
+ .command('review-file')
697
+ .description('Automated code review for a file')
698
+ .argument('<file>', 'File to review')
699
+ .action(async (file) => {
700
+ initLogFile();
701
+ if (!process.env.COHERE_API_KEY) {
702
+ console.error(error('COHERE_API_KEY not found in environment'));
703
+ process.exit(1);
704
+ }
705
+ try {
706
+ console.log(createBanner('Code Review', 'Automated senior-level analysis'));
707
+ newline();
708
+ const reviewSpinner = createSpinner('Reviewing code...');
709
+ reviewSpinner.start();
710
+ const review = await reviewFile(file);
711
+ reviewSpinner.stop();
712
+ newline();
713
+ console.log(formatReview(review));
714
+ // Display score
715
+ newline();
716
+ sectionHeader('Review Score');
717
+ const scoreTable = createTable(['Metric', 'Value'], [
718
+ ['Overall Score', `${review.score}/100`],
719
+ ['Errors', formatNumber(review.issues.filter(i => i.severity === 'error').length)],
720
+ ['Warnings', formatNumber(review.issues.filter(i => i.severity === 'warning').length)],
721
+ ['Info', formatNumber(review.issues.filter(i => i.severity === 'info').length)],
722
+ ]);
723
+ console.log(scoreTable.toString());
724
+ }
725
+ catch (err) {
726
+ console.error(error('Error:'), err instanceof Error ? err.message : String(err));
727
+ process.exit(1);
728
+ }
729
+ });
730
+ program
731
+ .command('learn')
732
+ .description('Show learning insights and performance recommendations')
733
+ .action(() => {
734
+ try {
735
+ console.log(createBanner('Learning Insights', 'Self-improvement system'));
736
+ newline();
737
+ const insights = formatLearningInsights();
738
+ console.log(insights);
739
+ const perfInsights = getPerformanceInsights();
740
+ if (perfInsights.recommendations.length > 0) {
741
+ sectionHeader('Performance Recommendations');
742
+ perfInsights.recommendations.forEach(rec => {
743
+ console.log(info(rec));
744
+ });
745
+ }
746
+ const cacheStats = getCacheStats();
747
+ newline();
748
+ sectionHeader('Cache Statistics');
749
+ const cacheTable = createTable(['Metric', 'Value'], [
750
+ ['Memory Cache', `${cacheStats.memorySize}/${cacheStats.memoryMaxSize}`],
751
+ ['File Cache', formatNumber(cacheStats.fileCacheCount)],
752
+ ]);
753
+ console.log(cacheTable.toString());
754
+ }
755
+ catch (err) {
756
+ console.error(error('Error:'), err instanceof Error ? err.message : String(err));
757
+ process.exit(1);
758
+ }
759
+ });
760
+ program
761
+ .command('cache')
762
+ .description('Manage cache')
763
+ .option('--clear', 'Clear all caches')
764
+ .option('--stats', 'Show cache statistics')
765
+ .action((options) => {
766
+ if (options.clear) {
767
+ clearAllCaches();
768
+ console.log(success('All caches cleared'));
769
+ }
770
+ else if (options.stats) {
771
+ const stats = getCacheStats();
772
+ sectionHeader('Cache Statistics');
773
+ const cacheTable = createTable(['Metric', 'Value'], [
774
+ ['Memory Cache', `${stats.memorySize}/${stats.memoryMaxSize}`],
775
+ ['File Cache', formatNumber(stats.fileCacheCount)],
776
+ ]);
777
+ console.log(cacheTable.toString());
778
+ }
779
+ else {
780
+ console.log(info('Use --clear to clear caches or --stats to show statistics'));
781
+ }
782
+ });
783
+ program
784
+ .command('migrate')
785
+ .description('Plan and execute framework migrations')
786
+ .argument('<from>', 'Source framework/version')
787
+ .argument('<to>', 'Target framework/version')
788
+ .option('-f, --files <files...>', 'Specific files to migrate')
789
+ .option('--plan-only', 'Only show migration plan, do not execute')
790
+ .action(async (from, to, options) => {
791
+ initLogFile();
792
+ if (!process.env.COHERE_API_KEY) {
793
+ console.error(error('COHERE_API_KEY not found in environment'));
794
+ process.exit(1);
795
+ }
796
+ try {
797
+ console.log(createBanner('Migration Assistant', `From ${from} to ${to}`));
798
+ newline();
799
+ const plan = await planMigration(from, to, options.files);
800
+ sectionHeader('Migration Plan');
801
+ console.log(formatMigrationPlan(plan));
802
+ if (!options.planOnly) {
803
+ newline();
804
+ console.log(info('Migration execution coming soon...'));
805
+ }
806
+ }
807
+ catch (err) {
808
+ console.error(error('Error:'), err instanceof Error ? err.message : String(err));
809
+ process.exit(1);
810
+ }
811
+ });
812
+ program
813
+ .command('docs')
814
+ .description('Generate and update documentation')
815
+ .argument('<file>', 'File to document')
816
+ .option('--readme', 'Generate README for entire project')
817
+ .option('--update', 'Update existing documentation')
818
+ .option('--save', 'Save documentation to file')
819
+ .action(async (file, options) => {
820
+ initLogFile();
821
+ if (!process.env.COHERE_API_KEY) {
822
+ console.error(error('COHERE_API_KEY not found in environment'));
823
+ process.exit(1);
824
+ }
825
+ try {
826
+ console.log(createBanner('Documentation Generator', 'Auto-generate docs'));
827
+ newline();
828
+ const docsSpinner = createSpinner('Generating documentation...');
829
+ docsSpinner.start();
830
+ let docs;
831
+ if (options.readme) {
832
+ docs = await generateREADME();
833
+ }
834
+ else if (options.update) {
835
+ docs = await updateDocs(file);
836
+ }
837
+ else {
838
+ docs = await generateFileDocs(file);
839
+ }
840
+ docsSpinner.stop();
841
+ newline();
842
+ sectionHeader('Generated Documentation');
843
+ const formattedDocs = cleanResponse(docs);
844
+ console.log(formattedDocs);
845
+ if (options.save) {
846
+ const outputPath = options.readme ? 'README.md' : file.replace(/\.[^/.]+$/, '.md');
847
+ saveDocs(outputPath, docs);
848
+ console.log(success(`Documentation saved to ${outputPath}`));
849
+ }
850
+ }
851
+ catch (err) {
852
+ console.error(error('Error:'), err instanceof Error ? err.message : String(err));
853
+ process.exit(1);
854
+ }
855
+ });
856
+ program
857
+ .command('test')
858
+ .description('Generate tests for code')
859
+ .argument('<file>', 'File to generate tests for')
860
+ .option('--framework <framework>', 'Test framework (jest, pytest, etc.)')
861
+ .option('--coverage <type>', 'Coverage type (unit, integration, both)', 'unit')
862
+ .option('--improve', 'Improve existing test file')
863
+ .option('--save', 'Save tests to file')
864
+ .action(async (file, options) => {
865
+ initLogFile();
866
+ if (!process.env.COHERE_API_KEY) {
867
+ console.error(error('COHERE_API_KEY not found in environment'));
868
+ process.exit(1);
869
+ }
870
+ try {
871
+ console.log(createBanner('Test Generator', 'Comprehensive test generation'));
872
+ newline();
873
+ const testSpinner = createSpinner('Generating tests...');
874
+ testSpinner.start();
875
+ let tests;
876
+ if (options.improve) {
877
+ tests = await improveTests(file);
878
+ }
879
+ else {
880
+ tests = await generateTests(file, {
881
+ framework: options.framework,
882
+ coverage: options.coverage,
883
+ });
884
+ }
885
+ testSpinner.stop();
886
+ newline();
887
+ sectionHeader('Generated Tests');
888
+ const formattedTests = cleanResponse(tests);
889
+ console.log(formattedTests);
890
+ if (options.save) {
891
+ const testPath = options.improve ? file : getTestFilePath(file, options.framework);
892
+ saveTests(testPath, tests);
893
+ console.log(success(`Tests saved to ${testPath}`));
894
+ }
895
+ }
896
+ catch (err) {
897
+ console.error(error('Error:'), err instanceof Error ? err.message : String(err));
898
+ process.exit(1);
899
+ }
900
+ });
901
+ program
902
+ .command('scaffold')
903
+ .description('Scaffold a simple app skeleton from a template')
904
+ .argument('<template>', 'Template name (e.g., node-api, next-page)')
905
+ .argument('<targetDir>', 'Target directory (relative to current directory)')
906
+ .action(async (template, targetDir) => {
907
+ initLogFile();
908
+ try {
909
+ console.log(createBanner('Scaffold App', 'Create app skeleton from template'));
910
+ newline();
911
+ // List available templates if user asks
912
+ if (template === 'list' || template === '--list') {
913
+ const templates = listTemplates();
914
+ if (templates.length === 0) {
915
+ console.log(warning('No templates available.'));
916
+ return;
917
+ }
918
+ sectionHeader('Available Templates');
919
+ templates.forEach(t => {
920
+ console.log(colors.cyan(` ${t}`));
921
+ });
922
+ newline();
923
+ return;
924
+ }
925
+ console.log(colors.bold('Template:') + ' ' + colors.cyan(template));
926
+ console.log(colors.bold('Target:') + ' ' + colors.cyan(targetDir));
927
+ newline();
928
+ const result = scaffoldTemplate(template, targetDir);
929
+ if (result.success) {
930
+ sectionHeader('Files Created');
931
+ result.filesCreated.forEach(file => {
932
+ console.log(colors.green(' ✓ ') + file);
933
+ });
934
+ newline();
935
+ console.log(success(`Scaffold complete! Created ${result.filesCreated.length} file(s).`));
936
+ newline();
937
+ console.log(info('Next steps:'));
938
+ console.log(colors.dim(` 1. cd ${targetDir}`));
939
+ console.log(colors.dim(` 2. codehere index`));
940
+ console.log(colors.dim(` 3. codehere explain <file>`));
941
+ console.log(colors.dim(` 4. codehere fix <file> "<instruction>"`));
942
+ newline();
943
+ logEntry({
944
+ timestamp: new Date().toISOString(),
945
+ command: 'scaffold',
946
+ query: `${template} -> ${targetDir}`,
947
+ policyStatus: 'allowed',
948
+ });
949
+ }
950
+ else {
951
+ newline();
952
+ console.error(error('Scaffold failed'));
953
+ console.log(colors.red(' ' + (result.error || 'Unknown error')));
954
+ newline();
955
+ process.exit(1);
956
+ }
957
+ }
958
+ catch (err) {
959
+ console.error(error('Error:'), err instanceof Error ? err.message : String(err));
960
+ process.exit(1);
961
+ }
962
+ });
963
+ program.parse();
964
+ //# sourceMappingURL=index.js.map