@rahul05ranjan/dhruv-cli 1.3.0 ā 1.5.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/.github/ENTERPRISE.md +275 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +45 -17
- package/.github/ISSUE_TEMPLATE/documentation_issue.md +61 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +61 -9
- package/.github/ISSUE_TEMPLATE/security_vulnerability.md +74 -0
- package/.github/dependabot.yml +42 -2
- package/.github/pull_request_template.md +13 -0
- package/.github/workflows/ci.yml +51 -38
- package/.github/workflows/contribution.yml +41 -34
- package/.github/workflows/dependabot-auto-merge.yml +62 -2
- package/.github/workflows/labeler.yml +1 -0
- package/.github/workflows/release.yml +229 -0
- package/.github/workflows/security.yml +201 -0
- package/.releaserc.json +50 -0
- package/AGENTS.md +13 -0
- package/CHANGELOG.md +13 -0
- package/README.md +145 -40
- package/__tests__/cli-contract.test.ts +104 -0
- package/__tests__/core.test.ts +439 -0
- package/__tests__/diagnostics.test.ts +155 -0
- package/__tests__/file-workflows.test.ts +195 -0
- package/__tests__/interactive.test.ts +119 -0
- package/__tests__/setup.ts +62 -0
- package/__tests__/workflows.test.ts +118 -0
- package/dist/commands/explain.js +13 -44
- package/dist/commands/fix.js +13 -38
- package/dist/commands/generate.d.ts +6 -1
- package/dist/commands/generate.js +60 -55
- package/dist/commands/health.d.ts +4 -0
- package/dist/commands/health.js +419 -0
- package/dist/commands/init.js +56 -42
- package/dist/commands/menu.js +137 -24
- package/dist/commands/metrics.d.ts +5 -0
- package/dist/commands/metrics.js +80 -0
- package/dist/commands/optimize.js +42 -34
- package/dist/commands/review.d.ts +4 -1
- package/dist/commands/review.js +87 -43
- package/dist/commands/security-check.d.ts +4 -1
- package/dist/commands/security-check.js +109 -38
- package/dist/commands/status.d.ts +1 -0
- package/dist/commands/status.js +83 -0
- package/dist/commands/suggest.js +13 -39
- package/dist/config/config.d.ts +5 -1
- package/dist/config/config.js +53 -8
- package/dist/core/ai.d.ts +88 -2
- package/dist/core/ai.js +231 -30
- package/dist/core/command-catalog.d.ts +10 -0
- package/dist/core/command-catalog.js +27 -0
- package/dist/core/command-runner.d.ts +17 -0
- package/dist/core/command-runner.js +157 -0
- package/dist/core/logger.d.ts +40 -0
- package/dist/core/logger.js +139 -0
- package/dist/core/metrics.d.ts +62 -0
- package/dist/core/metrics.js +284 -0
- package/dist/core/prompts.d.ts +1 -0
- package/dist/core/prompts.js +121 -0
- package/dist/core/security.d.ts +34 -0
- package/dist/core/security.js +197 -0
- package/dist/index.js +84 -22
- package/dist/utils/projectType.d.ts +1 -1
- package/dist/utils/projectType.js +26 -7
- package/dist/utils/ux.d.ts +3 -0
- package/dist/utils/ux.js +15 -0
- package/docs/agents/domain.md +51 -0
- package/docs/agents/issue-tracker.md +45 -0
- package/docs/agents/triage-labels.md +15 -0
- package/docs/api/.nojekyll +1 -0
- package/docs/api/assets/hierarchy.js +1 -0
- package/docs/api/assets/highlight.css +71 -0
- package/docs/api/assets/icons.js +18 -0
- package/docs/api/assets/icons.svg +1 -0
- package/docs/api/assets/main.js +60 -0
- package/docs/api/assets/navigation.js +1 -0
- package/docs/api/assets/search.js +1 -0
- package/docs/api/assets/style.css +1633 -0
- package/docs/api/hierarchy.html +1 -0
- package/docs/api/index.html +161 -0
- package/docs/api/media/CONTRIBUTING.md +60 -0
- package/docs/api/media/SECURITY.md +8 -0
- package/docs/api/media/dhruv-cli-preview.svg +42 -0
- package/docs/api/media/publishing-fix.md +34 -0
- package/docs/api/modules.html +1 -0
- package/docs/dhruv-cli-preview.svg +42 -0
- package/docs/index.html +631 -533
- package/docs/publishing-fix.md +34 -0
- package/eslint.config.js +170 -0
- package/jest.config.json +37 -0
- package/lighthouserc.json +22 -0
- package/package.json +62 -8
- package/src/commands/explain.ts +13 -42
- package/src/commands/fix.ts +13 -31
- package/src/commands/generate.ts +68 -48
- package/src/commands/health.ts +485 -0
- package/src/commands/init.ts +57 -42
- package/src/commands/menu.ts +132 -24
- package/src/commands/metrics.ts +100 -0
- package/src/commands/optimize.ts +40 -28
- package/src/commands/review.ts +96 -37
- package/src/commands/security-check.ts +127 -33
- package/src/commands/status.ts +83 -0
- package/src/commands/suggest.ts +13 -32
- package/src/config/config.ts +60 -8
- package/src/core/ai.ts +265 -26
- package/src/core/command-catalog.ts +37 -0
- package/src/core/command-runner.ts +185 -0
- package/src/core/logger.ts +195 -0
- package/src/core/metrics.ts +335 -0
- package/src/core/prompts.ts +128 -0
- package/src/core/security.ts +243 -0
- package/src/index.ts +90 -22
- package/src/utils/projectType.ts +22 -7
- package/src/utils/ux.ts +18 -0
- package/test-suite.sh +147 -0
- package/tsconfig.json +4 -3
- package/typedoc.json +44 -0
- package/types/global.d.ts +13 -0
- package/validate-workflows.sh +270 -0
- package/.eslintignore +0 -1
- package/.eslintrc.cjs +0 -43
- package/.github/workflows/auto-assign.yml +0 -14
- package/src/core/ai.test.js +0 -40
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { loadConfig } from '../config/config.js';
|
|
4
|
+
import { printSuccess, printError, printInfo, printWarning } from '../utils/ux.js';
|
|
5
|
+
import { logger } from '../core/logger.js';
|
|
6
|
+
import { metricsCollector } from '../core/metrics.js';
|
|
7
|
+
import { securityManager } from '../core/security.js';
|
|
8
|
+
import { detectProjectType } from '../utils/projectType.js';
|
|
9
|
+
import fs from 'fs';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import os from 'os';
|
|
12
|
+
|
|
13
|
+
interface HealthCheckResult {
|
|
14
|
+
category: string;
|
|
15
|
+
status: 'pass' | 'warn' | 'fail';
|
|
16
|
+
message: string;
|
|
17
|
+
details?: unknown;
|
|
18
|
+
recommendation?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface SystemInfo {
|
|
22
|
+
platform: string;
|
|
23
|
+
arch: string;
|
|
24
|
+
nodeVersion: string;
|
|
25
|
+
totalMemory: string;
|
|
26
|
+
freeMemory: string;
|
|
27
|
+
uptime: string;
|
|
28
|
+
cpuCount: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface HealthOptions {
|
|
32
|
+
details?: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function health(options: HealthOptions = {}): Promise<void> {
|
|
36
|
+
const jsonOutput = loadConfig().responseFormat === 'json';
|
|
37
|
+
const results: HealthCheckResult[] = [];
|
|
38
|
+
const startTime = Date.now();
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
// System Information
|
|
42
|
+
const systemInfo = getSystemInfo();
|
|
43
|
+
if (!jsonOutput) {
|
|
44
|
+
console.log(chalk.blue.bold('š Dhruv CLI Health Check\n'));
|
|
45
|
+
console.log(chalk.cyan('š System Information:'));
|
|
46
|
+
Object.entries(systemInfo).forEach(([key, value]) => {
|
|
47
|
+
console.log(` ${key}: ${chalk.yellow(value)}`);
|
|
48
|
+
});
|
|
49
|
+
console.log();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Configuration Check
|
|
53
|
+
results.push(...await checkConfiguration());
|
|
54
|
+
|
|
55
|
+
// Dependencies Check
|
|
56
|
+
results.push(...await checkDependencies());
|
|
57
|
+
|
|
58
|
+
// AI Service Check
|
|
59
|
+
results.push(...await checkAIService());
|
|
60
|
+
|
|
61
|
+
// Security Check
|
|
62
|
+
results.push(...await checkSecurity());
|
|
63
|
+
|
|
64
|
+
// Performance Check
|
|
65
|
+
results.push(...await checkPerformance());
|
|
66
|
+
|
|
67
|
+
// File System Check
|
|
68
|
+
results.push(...await checkFileSystem());
|
|
69
|
+
|
|
70
|
+
// Plugin System Check
|
|
71
|
+
results.push(...await checkPlugins());
|
|
72
|
+
|
|
73
|
+
const duration = Date.now() - startTime;
|
|
74
|
+
const summary = summarizeResults(results);
|
|
75
|
+
if (jsonOutput) {
|
|
76
|
+
process.exitCode = summary.fail > 0 ? 1 : 0;
|
|
77
|
+
process.stdout.write(`${JSON.stringify({
|
|
78
|
+
ok: summary.fail === 0,
|
|
79
|
+
command: 'health',
|
|
80
|
+
system: systemInfo,
|
|
81
|
+
results,
|
|
82
|
+
summary,
|
|
83
|
+
durationMs: duration,
|
|
84
|
+
})}\n`);
|
|
85
|
+
} else {
|
|
86
|
+
if (options.details) displayResults(results);
|
|
87
|
+
else displayConciseResults(results);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
logger.info('Health check completed', { duration, results: results.length });
|
|
91
|
+
|
|
92
|
+
} catch (error) {
|
|
93
|
+
process.exitCode = 1;
|
|
94
|
+
if (jsonOutput) {
|
|
95
|
+
process.stdout.write(`${JSON.stringify({
|
|
96
|
+
ok: false,
|
|
97
|
+
command: 'health',
|
|
98
|
+
error: (error as Error).message,
|
|
99
|
+
})}\n`);
|
|
100
|
+
} else {
|
|
101
|
+
printError('Health check failed');
|
|
102
|
+
console.error(chalk.red((error as Error).message));
|
|
103
|
+
}
|
|
104
|
+
logger.error('Health check failed', error as Error);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function getSystemInfo(): SystemInfo {
|
|
109
|
+
const totalMem = os.totalmem();
|
|
110
|
+
const freeMem = os.freemem();
|
|
111
|
+
const uptime = os.uptime();
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
platform: `${os.platform()} ${os.release()}`,
|
|
115
|
+
arch: os.arch(),
|
|
116
|
+
nodeVersion: process.version,
|
|
117
|
+
totalMemory: `${(totalMem / 1024 / 1024 / 1024).toFixed(2)} GB`,
|
|
118
|
+
freeMemory: `${(freeMem / 1024 / 1024 / 1024).toFixed(2)} GB`,
|
|
119
|
+
uptime: `${Math.floor(uptime / 3600)}h ${Math.floor((uptime % 3600) / 60)}m`,
|
|
120
|
+
cpuCount: os.cpus().length
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function checkConfiguration(): Promise<HealthCheckResult[]> {
|
|
125
|
+
const results: HealthCheckResult[] = [];
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
const config = loadConfig();
|
|
129
|
+
|
|
130
|
+
// Check if config file exists
|
|
131
|
+
const configPath = path.join(process.cwd(), '.dhruv-config.json');
|
|
132
|
+
if (fs.existsSync(configPath)) {
|
|
133
|
+
results.push({
|
|
134
|
+
category: 'Configuration',
|
|
135
|
+
status: 'pass',
|
|
136
|
+
message: 'Configuration file found and loaded successfully'
|
|
137
|
+
});
|
|
138
|
+
} else {
|
|
139
|
+
results.push({
|
|
140
|
+
category: 'Configuration',
|
|
141
|
+
status: 'warn',
|
|
142
|
+
message: 'No configuration file found',
|
|
143
|
+
recommendation: 'Run "dhruv init" to create a configuration file'
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Check model configuration
|
|
148
|
+
if (config.model && config.model.trim().length > 0) {
|
|
149
|
+
results.push({
|
|
150
|
+
category: 'Configuration',
|
|
151
|
+
status: 'pass',
|
|
152
|
+
message: `Model configured: ${config.model}`
|
|
153
|
+
});
|
|
154
|
+
} else {
|
|
155
|
+
results.push({
|
|
156
|
+
category: 'Configuration',
|
|
157
|
+
status: 'fail',
|
|
158
|
+
message: 'No model configured',
|
|
159
|
+
recommendation: 'Set a model using "dhruv init" or --model flag'
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Check theme configuration
|
|
164
|
+
if (config.theme && ['default', 'dark', 'light', 'mono'].includes(config.theme)) {
|
|
165
|
+
results.push({
|
|
166
|
+
category: 'Configuration',
|
|
167
|
+
status: 'pass',
|
|
168
|
+
message: `Theme configured: ${config.theme}`
|
|
169
|
+
});
|
|
170
|
+
} else {
|
|
171
|
+
results.push({
|
|
172
|
+
category: 'Configuration',
|
|
173
|
+
status: 'warn',
|
|
174
|
+
message: 'Invalid or missing theme configuration',
|
|
175
|
+
recommendation: 'Set theme using "dhruv init"'
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
} catch (error) {
|
|
180
|
+
results.push({
|
|
181
|
+
category: 'Configuration',
|
|
182
|
+
status: 'fail',
|
|
183
|
+
message: `Configuration check failed: ${(error as Error).message}`,
|
|
184
|
+
details: error
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return results;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async function checkDependencies(): Promise<HealthCheckResult[]> {
|
|
192
|
+
const results: HealthCheckResult[] = [];
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
// Check package.json
|
|
196
|
+
const packagePath = path.join(process.cwd(), 'package.json');
|
|
197
|
+
if (fs.existsSync(packagePath)) {
|
|
198
|
+
const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf-8'));
|
|
199
|
+
results.push({
|
|
200
|
+
category: 'Dependencies',
|
|
201
|
+
status: 'pass',
|
|
202
|
+
message: `Package.json found with ${Object.keys(packageJson.dependencies || {}).length} dependencies`
|
|
203
|
+
});
|
|
204
|
+
} else {
|
|
205
|
+
results.push({
|
|
206
|
+
category: 'Dependencies',
|
|
207
|
+
status: 'fail',
|
|
208
|
+
message: 'package.json not found',
|
|
209
|
+
recommendation: 'Ensure you are in the correct project directory'
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Check node_modules
|
|
214
|
+
const nodeModulesPath = path.join(process.cwd(), 'node_modules');
|
|
215
|
+
if (fs.existsSync(nodeModulesPath)) {
|
|
216
|
+
results.push({
|
|
217
|
+
category: 'Dependencies',
|
|
218
|
+
status: 'pass',
|
|
219
|
+
message: 'Node modules installed'
|
|
220
|
+
});
|
|
221
|
+
} else {
|
|
222
|
+
results.push({
|
|
223
|
+
category: 'Dependencies',
|
|
224
|
+
status: 'fail',
|
|
225
|
+
message: 'Node modules not found',
|
|
226
|
+
recommendation: 'Run "npm install" to install dependencies'
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
} catch (error) {
|
|
231
|
+
results.push({
|
|
232
|
+
category: 'Dependencies',
|
|
233
|
+
status: 'fail',
|
|
234
|
+
message: `Dependencies check failed: ${(error as Error).message}`,
|
|
235
|
+
details: error
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return results;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async function checkAIService(): Promise<HealthCheckResult[]> {
|
|
243
|
+
const results: HealthCheckResult[] = [];
|
|
244
|
+
|
|
245
|
+
try {
|
|
246
|
+
// Reuses the AI module's seam ā no private Ollama connection here.
|
|
247
|
+
const { listModels } = await import('../core/ai.js');
|
|
248
|
+
await listModels();
|
|
249
|
+
results.push({
|
|
250
|
+
category: 'AI Service',
|
|
251
|
+
status: 'pass',
|
|
252
|
+
message: 'Ollama service is running and accessible'
|
|
253
|
+
});
|
|
254
|
+
} catch (error) {
|
|
255
|
+
results.push({
|
|
256
|
+
category: 'AI Service',
|
|
257
|
+
status: 'fail',
|
|
258
|
+
message: 'Ollama service is not accessible',
|
|
259
|
+
details: error,
|
|
260
|
+
recommendation: 'Start Ollama with "ollama serve"'
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return results;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async function checkSecurity(): Promise<HealthCheckResult[]> {
|
|
268
|
+
const results: HealthCheckResult[] = [];
|
|
269
|
+
|
|
270
|
+
try {
|
|
271
|
+
const securityStatus = securityManager.getSecurityStatus();
|
|
272
|
+
|
|
273
|
+
results.push({
|
|
274
|
+
category: 'Security',
|
|
275
|
+
status: 'pass',
|
|
276
|
+
message: `Security manager active with ${securityStatus.blockedPatternsCount} patterns`,
|
|
277
|
+
details: {
|
|
278
|
+
inputValidation: securityStatus.config.enableInputValidation,
|
|
279
|
+
rateLimiting: securityStatus.config.enableRateLimiting,
|
|
280
|
+
activeRateLimits: securityStatus.activeRateLimits
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
} catch (error) {
|
|
285
|
+
results.push({
|
|
286
|
+
category: 'Security',
|
|
287
|
+
status: 'fail',
|
|
288
|
+
message: `Security check failed: ${(error as Error).message}`,
|
|
289
|
+
details: error
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return results;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async function checkPerformance(): Promise<HealthCheckResult[]> {
|
|
297
|
+
const results: HealthCheckResult[] = [];
|
|
298
|
+
|
|
299
|
+
try {
|
|
300
|
+
// Memory usage check
|
|
301
|
+
const memUsage = process.memoryUsage();
|
|
302
|
+
const memUsageMB = memUsage.heapUsed / 1024 / 1024;
|
|
303
|
+
|
|
304
|
+
if (memUsageMB < 100) {
|
|
305
|
+
results.push({
|
|
306
|
+
category: 'Performance',
|
|
307
|
+
status: 'pass',
|
|
308
|
+
message: `Memory usage: ${memUsageMB.toFixed(2)} MB`
|
|
309
|
+
});
|
|
310
|
+
} else if (memUsageMB < 500) {
|
|
311
|
+
results.push({
|
|
312
|
+
category: 'Performance',
|
|
313
|
+
status: 'warn',
|
|
314
|
+
message: `High memory usage: ${memUsageMB.toFixed(2)} MB`,
|
|
315
|
+
recommendation: 'Monitor memory usage during extended use'
|
|
316
|
+
});
|
|
317
|
+
} else {
|
|
318
|
+
results.push({
|
|
319
|
+
category: 'Performance',
|
|
320
|
+
status: 'fail',
|
|
321
|
+
message: `Excessive memory usage: ${memUsageMB.toFixed(2)} MB`,
|
|
322
|
+
recommendation: 'Restart the application or investigate memory leaks'
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Update metrics
|
|
327
|
+
metricsCollector.updateMemoryUsage();
|
|
328
|
+
|
|
329
|
+
} catch (error) {
|
|
330
|
+
results.push({
|
|
331
|
+
category: 'Performance',
|
|
332
|
+
status: 'fail',
|
|
333
|
+
message: `Performance check failed: ${(error as Error).message}`,
|
|
334
|
+
details: error
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return results;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
async function checkFileSystem(): Promise<HealthCheckResult[]> {
|
|
342
|
+
const results: HealthCheckResult[] = [];
|
|
343
|
+
|
|
344
|
+
try {
|
|
345
|
+
// Check cache directory
|
|
346
|
+
const cacheDir = path.join(process.cwd(), '.dhruv-cache');
|
|
347
|
+
if (fs.existsSync(cacheDir)) {
|
|
348
|
+
const cacheFiles = fs.readdirSync(cacheDir);
|
|
349
|
+
results.push({
|
|
350
|
+
category: 'File System',
|
|
351
|
+
status: 'pass',
|
|
352
|
+
message: `Cache directory healthy with ${cacheFiles.length} files`
|
|
353
|
+
});
|
|
354
|
+
} else {
|
|
355
|
+
results.push({
|
|
356
|
+
category: 'File System',
|
|
357
|
+
status: 'pass',
|
|
358
|
+
message: 'Cache directory will be created on first use'
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Check logs directory
|
|
363
|
+
const logsDir = path.join(process.cwd(), 'logs');
|
|
364
|
+
if (fs.existsSync(logsDir)) {
|
|
365
|
+
const logFiles = fs.readdirSync(logsDir);
|
|
366
|
+
results.push({
|
|
367
|
+
category: 'File System',
|
|
368
|
+
status: 'pass',
|
|
369
|
+
message: `Logs directory healthy with ${logFiles.length} files`
|
|
370
|
+
});
|
|
371
|
+
} else {
|
|
372
|
+
results.push({
|
|
373
|
+
category: 'File System',
|
|
374
|
+
status: 'pass',
|
|
375
|
+
message: 'Logs directory will be created on first use'
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
} catch (error) {
|
|
380
|
+
results.push({
|
|
381
|
+
category: 'File System',
|
|
382
|
+
status: 'fail',
|
|
383
|
+
message: `File system check failed: ${(error as Error).message}`,
|
|
384
|
+
details: error
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return results;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
async function checkPlugins(): Promise<HealthCheckResult[]> {
|
|
392
|
+
const results: HealthCheckResult[] = [];
|
|
393
|
+
|
|
394
|
+
try {
|
|
395
|
+
const pluginsDir = path.join(process.cwd(), 'plugins');
|
|
396
|
+
|
|
397
|
+
if (fs.existsSync(pluginsDir)) {
|
|
398
|
+
const pluginFiles = fs.readdirSync(pluginsDir).filter(f => f.endsWith('.js'));
|
|
399
|
+
results.push({
|
|
400
|
+
category: 'Plugins',
|
|
401
|
+
status: 'pass',
|
|
402
|
+
message: `Plugin system active with ${pluginFiles.length} plugins`,
|
|
403
|
+
details: pluginFiles
|
|
404
|
+
});
|
|
405
|
+
} else {
|
|
406
|
+
results.push({
|
|
407
|
+
category: 'Plugins',
|
|
408
|
+
status: 'pass',
|
|
409
|
+
message: 'Plugin system ready (no plugins installed)'
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
} catch (error) {
|
|
414
|
+
results.push({
|
|
415
|
+
category: 'Plugins',
|
|
416
|
+
status: 'fail',
|
|
417
|
+
message: `Plugin check failed: ${(error as Error).message}`,
|
|
418
|
+
details: error
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return results;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function displayResults(results: HealthCheckResult[]): void {
|
|
426
|
+
const categories = ['Configuration', 'Dependencies', 'AI Service', 'Security', 'Performance', 'File System', 'Plugins'];
|
|
427
|
+
|
|
428
|
+
categories.forEach(category => {
|
|
429
|
+
const categoryResults = results.filter(r => r.category === category);
|
|
430
|
+
|
|
431
|
+
if (categoryResults.length > 0) {
|
|
432
|
+
console.log(chalk.cyan(`\nš ${category}:`));
|
|
433
|
+
|
|
434
|
+
categoryResults.forEach(result => {
|
|
435
|
+
const icon = result.status === 'pass' ? 'ā
' : result.status === 'warn' ? 'ā ļø' : 'ā';
|
|
436
|
+
const color = result.status === 'pass' ? chalk.green : result.status === 'warn' ? chalk.yellow : chalk.red;
|
|
437
|
+
|
|
438
|
+
console.log(` ${icon} ${color(result.message)}`);
|
|
439
|
+
|
|
440
|
+
if (result.details) {
|
|
441
|
+
console.log(` ${chalk.gray(JSON.stringify(result.details, null, 2))}`);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (result.recommendation) {
|
|
445
|
+
console.log(` š” ${chalk.blue(result.recommendation)}`);
|
|
446
|
+
}
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
// Summary
|
|
452
|
+
const summary = summarizeResults(results);
|
|
453
|
+
|
|
454
|
+
console.log(chalk.blue.bold('\nš Summary:'));
|
|
455
|
+
console.log(` ā
Passed: ${chalk.green(summary.pass)}`);
|
|
456
|
+
console.log(` ā ļø Warnings: ${chalk.yellow(summary.warn)}`);
|
|
457
|
+
console.log(` ā Failed: ${chalk.red(summary.fail)}`);
|
|
458
|
+
|
|
459
|
+
const overallStatus = summary.fail > 0 ? 'fail' : summary.warn > 0 ? 'warn' : 'pass';
|
|
460
|
+
const statusIcon = overallStatus === 'pass' ? 'š' : overallStatus === 'warn' ? 'ā ļø' : 'ā';
|
|
461
|
+
const statusColor = overallStatus === 'pass' ? chalk.green : overallStatus === 'warn' ? chalk.yellow : chalk.red;
|
|
462
|
+
|
|
463
|
+
console.log(`\n${statusIcon} ${statusColor('Overall Status: ' + overallStatus.toUpperCase())}`);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function displayConciseResults(results: HealthCheckResult[]): void {
|
|
467
|
+
const summary = summarizeResults(results);
|
|
468
|
+
const overallStatus = summary.fail > 0 ? 'fail' : summary.warn > 0 ? 'warn' : 'pass';
|
|
469
|
+
const icon = overallStatus === 'pass' ? 'ā
' : overallStatus === 'warn' ? 'ā ļø' : 'ā';
|
|
470
|
+
|
|
471
|
+
console.log(chalk.blue.bold(`\n${icon} Health: ${overallStatus.toUpperCase()}`));
|
|
472
|
+
console.log(` ${chalk.green(`${summary.pass} passed`)}, ${chalk.yellow(`${summary.warn} warnings`)}, ${chalk.red(`${summary.fail} failed`)}`);
|
|
473
|
+
results
|
|
474
|
+
.filter((result) => result.status !== 'pass')
|
|
475
|
+
.forEach((result) => console.log(` ${result.category}: ${result.message}${result.recommendation ? ` ā ${result.recommendation}` : ''}`));
|
|
476
|
+
console.log(chalk.dim('Run `dhruv health --details` for full diagnostics.'));
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function summarizeResults(results: HealthCheckResult[]) {
|
|
480
|
+
return {
|
|
481
|
+
pass: results.filter(r => r.status === 'pass').length,
|
|
482
|
+
warn: results.filter(r => r.status === 'warn').length,
|
|
483
|
+
fail: results.filter(r => r.status === 'fail').length,
|
|
484
|
+
};
|
|
485
|
+
}
|
package/src/commands/init.ts
CHANGED
|
@@ -1,54 +1,69 @@
|
|
|
1
1
|
import inquirer from 'inquirer';
|
|
2
2
|
import { saveConfig, loadConfig } from '../config/config.js';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
|
+
import { listModels } from '../core/ai.js';
|
|
4
5
|
|
|
5
6
|
export async function init() {
|
|
6
7
|
const current = loadConfig();
|
|
7
8
|
let modelChoices = [current.model];
|
|
9
|
+
|
|
8
10
|
try {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
// Fetch models from Ollama REST API
|
|
13
|
-
const res = await fetch('http://localhost:11434/api/tags');
|
|
14
|
-
if (res.ok) {
|
|
15
|
-
const data = (await res.json()) as { models?: { name: string }[] };
|
|
16
|
-
if (Array.isArray(data.models) && data.models.length > 0) {
|
|
17
|
-
modelChoices = data.models.map((m) => m.name);
|
|
18
|
-
}
|
|
11
|
+
const models = await listModels();
|
|
12
|
+
if (models.length > 0) {
|
|
13
|
+
modelChoices = models;
|
|
19
14
|
}
|
|
20
15
|
} catch {
|
|
21
|
-
|
|
16
|
+
console.log(chalk.yellow('Warning: Could not fetch available models from Ollama.'));
|
|
17
|
+
console.log(chalk.yellow('Using default model choices.'));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const answers = await inquirer.prompt([
|
|
22
|
+
{
|
|
23
|
+
type: 'list',
|
|
24
|
+
name: 'model',
|
|
25
|
+
message: 'Which Ollama model do you want to use?',
|
|
26
|
+
choices: modelChoices,
|
|
27
|
+
default: current.model,
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
type: 'list',
|
|
31
|
+
name: 'scope',
|
|
32
|
+
message: 'Where should Dhruv save these settings?',
|
|
33
|
+
choices: ['local', 'global'],
|
|
34
|
+
default: 'local',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
type: 'list',
|
|
38
|
+
name: 'responseFormat',
|
|
39
|
+
message: 'Preferred response format?',
|
|
40
|
+
choices: ['text', 'json', 'markdown'],
|
|
41
|
+
default: current.responseFormat,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
type: 'confirm',
|
|
45
|
+
name: 'verbose',
|
|
46
|
+
message: 'Enable verbose output?',
|
|
47
|
+
default: current.verbose,
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
type: 'list',
|
|
51
|
+
name: 'theme',
|
|
52
|
+
message: 'Choose a color theme:',
|
|
53
|
+
choices: ['default', 'dark', 'light', 'mono'],
|
|
54
|
+
default: current.theme || 'default',
|
|
55
|
+
},
|
|
56
|
+
]);
|
|
57
|
+
|
|
58
|
+
const { scope, ...settings } = answers;
|
|
59
|
+
saveConfig(settings, { scope });
|
|
60
|
+
console.log(chalk.green(`Configuration saved ${scope === 'global' ? 'for your user account' : 'in this project'}!`));
|
|
61
|
+
} catch (error) {
|
|
62
|
+
process.exitCode = 130;
|
|
63
|
+
if ((error as { isTtyError?: boolean })?.isTtyError) {
|
|
64
|
+
console.log(chalk.red('This command requires an interactive terminal.'));
|
|
65
|
+
} else {
|
|
66
|
+
console.log(chalk.red('Configuration cancelled or failed.'));
|
|
67
|
+
}
|
|
22
68
|
}
|
|
23
|
-
const answers = await inquirer.prompt([
|
|
24
|
-
{
|
|
25
|
-
type: 'list',
|
|
26
|
-
name: 'model',
|
|
27
|
-
message: 'Which Ollama model do you want to use?',
|
|
28
|
-
choices: modelChoices,
|
|
29
|
-
default: current.model,
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
type: 'list',
|
|
33
|
-
name: 'responseFormat',
|
|
34
|
-
message: 'Preferred response format?',
|
|
35
|
-
choices: ['text', 'json', 'markdown'],
|
|
36
|
-
default: current.responseFormat,
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
type: 'confirm',
|
|
40
|
-
name: 'verbose',
|
|
41
|
-
message: 'Enable verbose output?',
|
|
42
|
-
default: current.verbose,
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
type: 'list',
|
|
46
|
-
name: 'theme',
|
|
47
|
-
message: 'Choose a color theme:',
|
|
48
|
-
choices: ['default', 'dark', 'light', 'mono'],
|
|
49
|
-
default: current.theme || 'default',
|
|
50
|
-
},
|
|
51
|
-
]);
|
|
52
|
-
saveConfig(answers);
|
|
53
|
-
console.log(chalk.green('Configuration saved!'));
|
|
54
69
|
}
|