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.
- package/ARCHITECTURE.md +240 -0
- package/CHANGELOG.md +44 -0
- package/CONTRIBUTING.md +171 -0
- package/LICENSE +22 -0
- package/README.md +203 -0
- package/dist/ast-parser.d.ts +55 -0
- package/dist/ast-parser.d.ts.map +1 -0
- package/dist/ast-parser.js +331 -0
- package/dist/ast-parser.js.map +1 -0
- package/dist/benchmark.d.ts +39 -0
- package/dist/benchmark.d.ts.map +1 -0
- package/dist/benchmark.js +195 -0
- package/dist/benchmark.js.map +1 -0
- package/dist/cache.d.ts +45 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +182 -0
- package/dist/cache.js.map +1 -0
- package/dist/chat.d.ts +4 -0
- package/dist/chat.d.ts.map +1 -0
- package/dist/chat.js +132 -0
- package/dist/chat.js.map +1 -0
- package/dist/code-analysis.d.ts +50 -0
- package/dist/code-analysis.d.ts.map +1 -0
- package/dist/code-analysis.js +327 -0
- package/dist/code-analysis.js.map +1 -0
- package/dist/context.d.ts +44 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +187 -0
- package/dist/context.js.map +1 -0
- package/dist/docs.d.ts +21 -0
- package/dist/docs.d.ts.map +1 -0
- package/dist/docs.js +147 -0
- package/dist/docs.js.map +1 -0
- package/dist/edit.d.ts +38 -0
- package/dist/edit.d.ts.map +1 -0
- package/dist/edit.js +594 -0
- package/dist/edit.js.map +1 -0
- package/dist/embed.d.ts +18 -0
- package/dist/embed.d.ts.map +1 -0
- package/dist/embed.js +479 -0
- package/dist/embed.js.map +1 -0
- package/dist/error-handler.d.ts +76 -0
- package/dist/error-handler.d.ts.map +1 -0
- package/dist/error-handler.js +213 -0
- package/dist/error-handler.js.map +1 -0
- package/dist/formatter.d.ts +25 -0
- package/dist/formatter.d.ts.map +1 -0
- package/dist/formatter.js +148 -0
- package/dist/formatter.js.map +1 -0
- package/dist/git.d.ts +55 -0
- package/dist/git.d.ts.map +1 -0
- package/dist/git.js +198 -0
- package/dist/git.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +964 -0
- package/dist/index.js.map +1 -0
- package/dist/intelligent-retrieval.d.ts +41 -0
- package/dist/intelligent-retrieval.d.ts.map +1 -0
- package/dist/intelligent-retrieval.js +265 -0
- package/dist/intelligent-retrieval.js.map +1 -0
- package/dist/iterative-refinement.d.ts +31 -0
- package/dist/iterative-refinement.d.ts.map +1 -0
- package/dist/iterative-refinement.js +172 -0
- package/dist/iterative-refinement.js.map +1 -0
- package/dist/learning.d.ts +69 -0
- package/dist/learning.d.ts.map +1 -0
- package/dist/learning.js +233 -0
- package/dist/learning.js.map +1 -0
- package/dist/log.d.ts +4 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +31 -0
- package/dist/log.js.map +1 -0
- package/dist/migrate.d.ts +33 -0
- package/dist/migrate.d.ts.map +1 -0
- package/dist/migrate.js +133 -0
- package/dist/migrate.js.map +1 -0
- package/dist/monitoring.d.ts +75 -0
- package/dist/monitoring.d.ts.map +1 -0
- package/dist/monitoring.js +248 -0
- package/dist/monitoring.js.map +1 -0
- package/dist/parallel-processor.d.ts +43 -0
- package/dist/parallel-processor.d.ts.map +1 -0
- package/dist/parallel-processor.js +308 -0
- package/dist/parallel-processor.js.map +1 -0
- package/dist/planner.d.ts +47 -0
- package/dist/planner.d.ts.map +1 -0
- package/dist/planner.js +198 -0
- package/dist/planner.js.map +1 -0
- package/dist/policy.d.ts +3 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +26 -0
- package/dist/policy.js.map +1 -0
- package/dist/query-optimizer.d.ts +32 -0
- package/dist/query-optimizer.d.ts.map +1 -0
- package/dist/query-optimizer.js +205 -0
- package/dist/query-optimizer.js.map +1 -0
- package/dist/refactor.d.ts +27 -0
- package/dist/refactor.d.ts.map +1 -0
- package/dist/refactor.js +118 -0
- package/dist/refactor.js.map +1 -0
- package/dist/review.d.ts +31 -0
- package/dist/review.d.ts.map +1 -0
- package/dist/review.js +206 -0
- package/dist/review.js.map +1 -0
- package/dist/scaffold.d.ts +14 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +85 -0
- package/dist/scaffold.js.map +1 -0
- package/dist/search.d.ts +19 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +198 -0
- package/dist/search.js.map +1 -0
- package/dist/session.d.ts +17 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +301 -0
- package/dist/session.js.map +1 -0
- package/dist/task-verification.d.ts +39 -0
- package/dist/task-verification.d.ts.map +1 -0
- package/dist/task-verification.js +336 -0
- package/dist/task-verification.js.map +1 -0
- package/dist/test_cohere.d.ts +2 -0
- package/dist/test_cohere.d.ts.map +1 -0
- package/dist/test_cohere.js +68 -0
- package/dist/test_cohere.js.map +1 -0
- package/dist/test_env.d.ts +2 -0
- package/dist/test_env.d.ts.map +1 -0
- package/dist/test_env.js +24 -0
- package/dist/test_env.js.map +1 -0
- package/dist/test_retrieval.d.ts +2 -0
- package/dist/test_retrieval.d.ts.map +1 -0
- package/dist/test_retrieval.js +84 -0
- package/dist/test_retrieval.js.map +1 -0
- package/dist/testgen.d.ts +24 -0
- package/dist/testgen.d.ts.map +1 -0
- package/dist/testgen.js +167 -0
- package/dist/testgen.js.map +1 -0
- package/dist/token-optimizer.d.ts +20 -0
- package/dist/token-optimizer.d.ts.map +1 -0
- package/dist/token-optimizer.js +277 -0
- package/dist/token-optimizer.js.map +1 -0
- package/dist/types.d.ts +36 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/ui.d.ts +54 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +295 -0
- package/dist/ui.js.map +1 -0
- package/dist/verify_db.d.ts +2 -0
- package/dist/verify_db.d.ts.map +1 -0
- package/dist/verify_db.js +52 -0
- package/dist/verify_db.js.map +1 -0
- package/package.json +71 -0
- package/templates/next-page/app/layout.tsx +19 -0
- package/templates/next-page/app/page.tsx +10 -0
- package/templates/next-page/package.json +22 -0
- package/templates/node-api/index.js +57 -0
- 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
|