@goldensheepai/toknxr-cli 0.2.2 → 0.4.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.
@@ -1,319 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import chalk from 'chalk';
4
- // Comprehensive code review rules for AI-generated code
5
- export const DEFAULT_CODE_REVIEW_RULES = [
6
- // Security Issues
7
- {
8
- id: 'SEC001',
9
- name: 'Hardcoded Secrets',
10
- description: 'Detects hardcoded API keys, passwords, or tokens',
11
- severity: 'error',
12
- category: 'security',
13
- pattern: /(api[_-]?key|password|secret|token)\s*[=:]?\s*["'][^"']{8,}["']/gi,
14
- message: 'Potential hardcoded secret detected',
15
- suggestion: 'Use environment variables or secure secret management'
16
- },
17
- {
18
- id: 'SEC002',
19
- name: 'SQL Injection Risk',
20
- description: 'Detects unsafe string concatenation in SQL queries',
21
- severity: 'error',
22
- category: 'security',
23
- pattern: /(SELECT|INSERT|UPDATE|DELETE).*\+.*["'][^"']*["']/gi,
24
- message: 'Potential SQL injection vulnerability',
25
- suggestion: 'Use parameterized queries or prepared statements'
26
- },
27
- {
28
- id: 'SEC003',
29
- name: 'Insecure Random',
30
- description: 'Detects use of Math.random() for security-sensitive operations',
31
- severity: 'warning',
32
- category: 'security',
33
- pattern: /Math\.random\(\)/g,
34
- message: 'Math.random() is not cryptographically secure',
35
- suggestion: 'Use crypto.randomBytes() or crypto.getRandomValues() for security-sensitive operations'
36
- },
37
- // Performance Issues
38
- {
39
- id: 'PERF001',
40
- name: 'Inefficient Loop',
41
- description: 'Detects potential infinite or inefficient loops',
42
- severity: 'warning',
43
- category: 'performance',
44
- pattern: /for\s*\(\s*let\s+\w+\s*=\s*0\s*;\s*\w+\s*<\s*\w+\.length\s*;\s*\w+\+\+\s*\)\s*{[^}]*\w+\[\w+\]/g,
45
- message: 'Potential inefficient array iteration',
46
- suggestion: 'Consider using modern array methods like forEach, map, or for...of'
47
- },
48
- {
49
- id: 'PERF002',
50
- name: 'Large DOM Manipulation',
51
- description: 'Detects multiple DOM manipulations that could be batched',
52
- severity: 'info',
53
- category: 'performance',
54
- pattern: /(document\.createElement|appendChild|innerHTML\s*=)[^;]+;[^;]*\1/gi,
55
- message: 'Multiple DOM manipulations detected',
56
- suggestion: 'Consider batching DOM operations or using DocumentFragment'
57
- },
58
- // Maintainability Issues
59
- {
60
- id: 'MAINT001',
61
- name: 'Long Function',
62
- description: 'Detects functions that are too long and should be broken up',
63
- severity: 'warning',
64
- category: 'maintainability',
65
- pattern: /^(?:function|\w+\s*=>|\w+\s*\([^)]*\)\s*{[^}]*{[^}]*}[^}]*})/gm,
66
- message: 'Function appears to be too long',
67
- suggestion: 'Consider breaking this function into smaller, more focused functions'
68
- },
69
- {
70
- id: 'MAINT002',
71
- name: 'Missing Error Handling',
72
- description: 'Detects operations that might throw errors without proper handling',
73
- severity: 'warning',
74
- category: 'maintainability',
75
- pattern: /(fetch|axios|XMLHttpRequest)\s*\([^)]+\)\s*(?![^}]*catch|\.catch)/gi,
76
- message: 'Async operation without error handling',
77
- suggestion: 'Add try-catch blocks or .catch() handlers for async operations'
78
- },
79
- {
80
- id: 'MAINT003',
81
- name: 'Magic Numbers',
82
- description: 'Detects hardcoded numeric values that should be constants',
83
- severity: 'info',
84
- category: 'maintainability',
85
- pattern: /\b\d{2,}\b/g,
86
- message: 'Magic number detected',
87
- suggestion: 'Consider defining this as a named constant for better readability'
88
- },
89
- // Reliability Issues
90
- {
91
- id: 'REL001',
92
- name: 'Unsafe Type Coercion',
93
- description: 'Detects potentially unsafe type conversions',
94
- severity: 'warning',
95
- category: 'reliability',
96
- pattern: /(==|!=)\s*null/gi,
97
- message: 'Loose equality comparison with null',
98
- suggestion: 'Use strict equality (===) or explicitly check for null/undefined'
99
- },
100
- {
101
- id: 'REL002',
102
- name: 'Resource Leaks',
103
- description: 'Detects potential resource leaks',
104
- severity: 'warning',
105
- category: 'reliability',
106
- pattern: /(addEventListener|setTimeout|setInterval)\s*\([^)]+\)\s*(?![^}]*removeEventListener|\.clear)/gi,
107
- message: 'Event listener or timer without cleanup',
108
- suggestion: 'Ensure proper cleanup of event listeners and timers'
109
- }
110
- ];
111
- export class CodeReviewer {
112
- rules;
113
- constructor(customRules = []) {
114
- this.rules = [...DEFAULT_CODE_REVIEW_RULES, ...customRules];
115
- }
116
- async reviewFile(filePath) {
117
- try {
118
- const content = fs.readFileSync(filePath, 'utf8');
119
- const lines = content.split('\n');
120
- const results = [];
121
- let errors = 0;
122
- let warnings = 0;
123
- let info = 0;
124
- lines.forEach((line, index) => {
125
- this.rules.forEach(rule => {
126
- const matches = line.match(rule.pattern);
127
- if (matches) {
128
- matches.forEach(match => {
129
- const result = {
130
- rule,
131
- line: index + 1,
132
- context: line.trim(),
133
- severity: rule.severity,
134
- message: rule.message,
135
- suggestion: rule.suggestion
136
- };
137
- results.push(result);
138
- switch (rule.severity) {
139
- case 'error':
140
- errors++;
141
- break;
142
- case 'warning':
143
- warnings++;
144
- break;
145
- case 'info':
146
- info++;
147
- break;
148
- }
149
- });
150
- }
151
- });
152
- });
153
- const totalIssues = results.length;
154
- const summary = this.generateSummary(totalIssues, errors, warnings, info);
155
- const recommendations = this.generateRecommendations(results);
156
- return {
157
- filePath,
158
- totalIssues,
159
- errors,
160
- warnings,
161
- info,
162
- results,
163
- summary,
164
- recommendations
165
- };
166
- }
167
- catch (error) {
168
- throw new Error(`Failed to review file ${filePath}: ${error}`);
169
- }
170
- }
171
- async reviewDirectory(dirPath) {
172
- const reports = [];
173
- const files = this.getCodeFiles(dirPath);
174
- for (const file of files) {
175
- try {
176
- const report = await this.reviewFile(file);
177
- reports.push(report);
178
- }
179
- catch (error) {
180
- console.warn(chalk.yellow(`Warning: Could not review file ${file}: ${error}`));
181
- }
182
- }
183
- return reports;
184
- }
185
- getCodeFiles(dirPath) {
186
- const files = [];
187
- const extensions = ['.js', '.ts', '.jsx', '.tsx', '.py', '.java', '.cpp', '.c', '.cs'];
188
- const scan = (currentPath) => {
189
- const items = fs.readdirSync(currentPath);
190
- for (const item of items) {
191
- const fullPath = path.join(currentPath, item);
192
- const stat = fs.statSync(fullPath);
193
- if (stat.isDirectory() && !item.startsWith('.') && item !== 'node_modules') {
194
- scan(fullPath);
195
- }
196
- else if (stat.isFile() && extensions.some(ext => item.endsWith(ext))) {
197
- files.push(fullPath);
198
- }
199
- }
200
- };
201
- scan(dirPath);
202
- return files;
203
- }
204
- generateSummary(totalIssues, errors, warnings, info) {
205
- if (totalIssues === 0) {
206
- return 'āœ… No issues found! The code looks good.';
207
- }
208
- let summary = `Found ${totalIssues} issue${totalIssues > 1 ? 's' : ''}: `;
209
- const parts = [];
210
- if (errors > 0)
211
- parts.push(`${errors} error${errors > 1 ? 's' : ''}`);
212
- if (warnings > 0)
213
- parts.push(`${warnings} warning${warnings > 1 ? 's' : ''}`);
214
- if (info > 0)
215
- parts.push(`${info} info item${info > 1 ? 's' : ''}`);
216
- summary += parts.join(', ');
217
- if (errors > 0) {
218
- summary += ' šŸ”“ Critical issues need immediate attention.';
219
- }
220
- else if (warnings > 0) {
221
- summary += ' 🟔 Some issues should be addressed.';
222
- }
223
- else {
224
- summary += ' ā„¹ļø Minor improvements suggested.';
225
- }
226
- return summary;
227
- }
228
- generateRecommendations(results) {
229
- const recommendations = [];
230
- const categories = new Set(results.map(r => r.rule.category));
231
- if (categories.has('security')) {
232
- recommendations.push('šŸ”’ Review security issues first - they pose the highest risk');
233
- }
234
- if (categories.has('reliability')) {
235
- recommendations.push('šŸ›”ļø Address reliability issues to prevent runtime errors');
236
- }
237
- if (categories.has('performance')) {
238
- recommendations.push('⚔ Consider performance optimizations for better user experience');
239
- }
240
- if (categories.has('maintainability')) {
241
- recommendations.push('🧹 Refactor maintainability issues for long-term code health');
242
- }
243
- if (recommendations.length === 0) {
244
- recommendations.push('✨ Code quality looks good! Consider regular reviews to maintain standards');
245
- }
246
- return recommendations;
247
- }
248
- addCustomRule(rule) {
249
- this.rules.push(rule);
250
- }
251
- getRules() {
252
- return [...this.rules];
253
- }
254
- }
255
- export function printReviewReport(report) {
256
- console.log(chalk.bold.blue(`\nšŸ“‹ Code Review Report for: ${path.basename(report.filePath)}`));
257
- console.log(chalk.gray(`File: ${report.filePath}`));
258
- console.log(chalk.cyan(report.summary));
259
- console.log('');
260
- if (report.results.length > 0) {
261
- // Group by severity
262
- const errors = report.results.filter(r => r.severity === 'error');
263
- const warnings = report.results.filter(r => r.severity === 'warning');
264
- const info = report.results.filter(r => r.severity === 'info');
265
- if (errors.length > 0) {
266
- console.log(chalk.red.bold('šŸ”“ ERRORS:'));
267
- errors.forEach(result => {
268
- console.log(` ${chalk.red(`[${result.rule.id}]`)} Line ${result.line}: ${result.message}`);
269
- if (result.suggestion) {
270
- console.log(` šŸ’” ${chalk.gray(result.suggestion)}`);
271
- }
272
- });
273
- console.log('');
274
- }
275
- if (warnings.length > 0) {
276
- console.log(chalk.yellow.bold('🟔 WARNINGS:'));
277
- warnings.forEach(result => {
278
- console.log(` ${chalk.yellow(`[${result.rule.id}]`)} Line ${result.line}: ${result.message}`);
279
- if (result.suggestion) {
280
- console.log(` šŸ’” ${chalk.gray(result.suggestion)}`);
281
- }
282
- });
283
- console.log('');
284
- }
285
- if (info.length > 0) {
286
- console.log(chalk.blue.bold('ā„¹ļø INFO:'));
287
- info.forEach(result => {
288
- console.log(` ${chalk.blue(`[${result.rule.id}]`)} Line ${result.line}: ${result.message}`);
289
- if (result.suggestion) {
290
- console.log(` šŸ’” ${chalk.gray(result.suggestion)}`);
291
- }
292
- });
293
- console.log('');
294
- }
295
- }
296
- if (report.recommendations.length > 0) {
297
- console.log(chalk.green.bold('šŸ’” RECOMMENDATIONS:'));
298
- report.recommendations.forEach(rec => {
299
- console.log(` ${rec}`);
300
- });
301
- console.log('');
302
- }
303
- console.log(chalk.gray(`Total: ${report.totalIssues} issues (${report.errors} errors, ${report.warnings} warnings, ${report.info} info)`));
304
- }
305
- export function printMultipleReports(reports) {
306
- const totalIssues = reports.reduce((sum, r) => sum + r.totalIssues, 0);
307
- const totalErrors = reports.reduce((sum, r) => sum + r.errors, 0);
308
- const totalWarnings = reports.reduce((sum, r) => sum + r.warnings, 0);
309
- const totalInfo = reports.reduce((sum, r) => sum + r.info, 0);
310
- console.log(chalk.bold.blue(`\nšŸ“Š Code Review Summary`));
311
- console.log(chalk.cyan(`Reviewed ${reports.length} files`));
312
- console.log(chalk.gray(`Total Issues: ${totalIssues} (${totalErrors} errors, ${totalWarnings} warnings, ${totalInfo} info)`));
313
- console.log('');
314
- reports.forEach(report => {
315
- if (report.totalIssues > 0) {
316
- printReviewReport(report);
317
- }
318
- });
319
- }
package/lib/config.js DELETED
@@ -1,7 +0,0 @@
1
- import { createClient } from '@supabase/supabase-js';
2
- export const supabaseUrl = process.env.SUPABASE_URL || 'https://pkdytotoptkknghtsomn.supabase.co';
3
- export const supabaseAnonKey = process.env.SUPABASE_ANON_KEY || 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBrZHl0b3RvcHRra25naHRzb21uIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTk3MjI1OTgsImV4cCI6MjA3NTI5ODU5OH0.Y3RVJvx7w-eGD4Nv2aVv8jnkUKhmA2vpkBs7_rFzEoQ';
4
- export const supabase = createClient(supabaseUrl, supabaseAnonKey);
5
- export const setAuthToken = (token) => {
6
- supabase.auth.setSession({ access_token: token, refresh_token: '' });
7
- };
package/lib/sync.js DELETED
@@ -1,117 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import { getToken } from './auth.js';
4
- import { setAuthToken } from './config.js';
5
- import chalk from 'chalk';
6
- export async function syncInteractions(supabase, options) {
7
- console.log(chalk.blue('Syncing local analytics with the cloud dashboard...'));
8
- // Get the stored token
9
- const token = await getToken();
10
- if (!token) {
11
- console.error(chalk.red('No authentication token found. Please login first with `toknxr login`.'));
12
- process.exit(1);
13
- }
14
- // Set auth in Supabase
15
- setAuthToken(token);
16
- // Resolve interactions.log robustly across run contexts (repo root vs toknxr-cli)
17
- const candidatePaths = [
18
- path.resolve(process.cwd(), 'interactions.log'),
19
- path.resolve(process.cwd(), 'toknxr-cli', 'interactions.log'),
20
- // When executed from built JS in lib/, __dirname points to lib; go up one
21
- path.resolve(__dirname, '../interactions.log'),
22
- ];
23
- const logFilePath = candidatePaths.find(p => fs.existsSync(p)) || candidatePaths[0];
24
- if (!fs.existsSync(logFilePath)) {
25
- console.log(chalk.yellow('No interactions.log file found in current directory or toknxr-cli/. Nothing to sync.'));
26
- return;
27
- }
28
- const fileContent = fs.readFileSync(logFilePath, 'utf8');
29
- const lines = fileContent.trim().split('\n');
30
- if (lines.length === 0 || (lines.length === 1 && lines[0] === '')) {
31
- console.log(chalk.yellow('Log file is empty. Nothing to sync.'));
32
- return;
33
- }
34
- const logs = [];
35
- for (const line of lines) {
36
- try {
37
- const log = JSON.parse(line);
38
- logs.push(log);
39
- }
40
- catch {
41
- console.warn(`Skipping invalid log entry: ${line}`);
42
- }
43
- }
44
- if (logs.length === 0) {
45
- console.log('No valid interactions to sync.');
46
- return;
47
- }
48
- // Map logs to interactions
49
- const interactions = [];
50
- for (const log of logs) {
51
- // Find ai_service_id by provider and model
52
- const { data: aiService, error: aiError } = await supabase
53
- .from('ai_services')
54
- .select('id')
55
- .eq('provider', log.provider)
56
- .eq('name', log.model)
57
- .single();
58
- if (aiError || !aiService) {
59
- console.warn(`AI service not found for provider: ${log.provider}, model: ${log.model}`);
60
- continue;
61
- }
62
- // Assume project_id - for now, use a default or query user's projects
63
- // TODO: Add project selection or default project
64
- const { data: projects, error: projError } = await supabase
65
- .from('projects')
66
- .select('id')
67
- .limit(1);
68
- if (projError || !projects || projects.length === 0) {
69
- console.error('No projects found for user.');
70
- continue;
71
- }
72
- const projectId = projects[0].id;
73
- const interaction = {
74
- project_id: projectId,
75
- ai_service_id: aiService.id,
76
- tokens_used: log.totalTokens,
77
- cost_in_cents: Math.round(log.costUSD * 100),
78
- timestamp: new Date(log.timestamp).toISOString(),
79
- request_details: log.userPrompt || '',
80
- response_details: log.aiResponse || log.extractedCode || '',
81
- // Rich metrics from CLI analysis
82
- code_quality_score: log.codeQualityScore,
83
- effectiveness_score: log.effectivenessScore,
84
- language: log.codeQualityMetrics?.language,
85
- syntax_valid: log.codeQualityMetrics?.syntaxValid,
86
- readability_score: log.codeQualityMetrics?.estimatedReadability,
87
- hallucination_detected: log.hallucinationDetection?.isLikelyHallucination,
88
- hallucination_confidence: log.hallucinationDetection?.confidence,
89
- prompt_clarity_match: log.hallucinationDetection?.promptClarityMatch,
90
- task_type: log.taskType,
91
- };
92
- interactions.push(interaction);
93
- }
94
- if (interactions.length === 0) {
95
- console.log('No interactions to insert.');
96
- return;
97
- }
98
- // Insert interactions
99
- const { error: insertError } = await supabase
100
- .from('interactions')
101
- .insert(interactions);
102
- if (insertError) {
103
- console.error('Error syncing interactions:', insertError);
104
- }
105
- else {
106
- console.log(`Successfully synced ${interactions.length} interactions.`);
107
- }
108
- if (options.clear) {
109
- try {
110
- fs.writeFileSync(logFilePath, '');
111
- console.log(chalk.gray('Local interactions.log has been cleared.'));
112
- }
113
- catch {
114
- console.log(chalk.yellow('Could not clear interactions.log. Proceeding without clearing.'));
115
- }
116
- }
117
- }