@rahul05ranjan/dhruv-cli 1.4.6 → 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.
Files changed (76) hide show
  1. package/.github/workflows/ci.yml +18 -238
  2. package/.github/workflows/contribution.yml +6 -141
  3. package/.github/workflows/dependabot-auto-merge.yml +1 -0
  4. package/.github/workflows/labeler.yml +1 -0
  5. package/.github/workflows/security.yml +3 -0
  6. package/CHANGELOG.md +9 -4
  7. package/README.md +145 -40
  8. package/__tests__/cli-contract.test.ts +104 -0
  9. package/__tests__/core.test.ts +121 -0
  10. package/__tests__/diagnostics.test.ts +155 -0
  11. package/__tests__/file-workflows.test.ts +195 -0
  12. package/__tests__/interactive.test.ts +119 -0
  13. package/__tests__/setup.ts +1 -0
  14. package/__tests__/workflows.test.ts +27 -4
  15. package/dist/commands/generate.d.ts +6 -1
  16. package/dist/commands/generate.js +18 -4
  17. package/dist/commands/health.d.ts +4 -1
  18. package/dist/commands/health.js +59 -16
  19. package/dist/commands/init.js +11 -2
  20. package/dist/commands/menu.js +125 -100
  21. package/dist/commands/metrics.d.ts +5 -1
  22. package/dist/commands/metrics.js +37 -8
  23. package/dist/commands/optimize.js +1 -1
  24. package/dist/commands/review.d.ts +4 -1
  25. package/dist/commands/review.js +48 -8
  26. package/dist/commands/security-check.d.ts +4 -1
  27. package/dist/commands/security-check.js +67 -6
  28. package/dist/commands/status.js +40 -2
  29. package/dist/config/config.d.ts +5 -1
  30. package/dist/config/config.js +24 -7
  31. package/dist/core/ai.d.ts +11 -0
  32. package/dist/core/ai.js +33 -9
  33. package/dist/core/command-catalog.d.ts +10 -0
  34. package/dist/core/command-catalog.js +27 -0
  35. package/dist/core/command-runner.js +100 -21
  36. package/dist/core/logger.js +1 -0
  37. package/dist/core/metrics.d.ts +28 -0
  38. package/dist/core/metrics.js +78 -0
  39. package/dist/index.js +46 -24
  40. package/dist/utils/projectType.d.ts +1 -1
  41. package/dist/utils/projectType.js +26 -7
  42. package/docs/api/assets/highlight.css +4 -4
  43. package/docs/api/index.html +161 -39
  44. package/docs/api/media/CONTRIBUTING.md +60 -0
  45. package/docs/api/media/SECURITY.md +8 -0
  46. package/docs/api/media/dhruv-cli-preview.svg +42 -0
  47. package/docs/api/media/publishing-fix.md +34 -0
  48. package/docs/dhruv-cli-preview.svg +42 -0
  49. package/docs/index.html +631 -533
  50. package/docs/publishing-fix.md +34 -0
  51. package/package.json +1 -1
  52. package/src/commands/generate.ts +23 -4
  53. package/src/commands/health.ts +62 -17
  54. package/src/commands/init.ts +11 -2
  55. package/src/commands/menu.ts +54 -30
  56. package/src/commands/metrics.ts +42 -7
  57. package/src/commands/optimize.ts +1 -1
  58. package/src/commands/review.ts +53 -8
  59. package/src/commands/security-check.ts +80 -6
  60. package/src/commands/status.ts +39 -3
  61. package/src/config/config.ts +26 -7
  62. package/src/core/ai.ts +36 -8
  63. package/src/core/command-catalog.ts +37 -0
  64. package/src/core/command-runner.ts +102 -22
  65. package/src/core/logger.ts +1 -0
  66. package/src/core/metrics.ts +103 -0
  67. package/src/index.ts +45 -24
  68. package/src/utils/projectType.ts +22 -7
  69. package/tsconfig.json +1 -1
  70. package/.github/workflows/auto-assign.yml +0 -14
  71. package/.github/workflows/build-publish.yml +0 -154
  72. package/.github/workflows/deploy.yml +0 -336
  73. package/.github/workflows/monitoring.yml +0 -270
  74. package/PUBLISHING_FIX.md +0 -92
  75. package/logs/.8a99b6cf655346317fdbf29f4fffcf91131432f3-audit.json +0 -15
  76. package/logs/.eee104bf8fff5ecd38a6a2842df260de6470a7c3-audit.json +0 -15
package/src/index.ts CHANGED
@@ -20,6 +20,7 @@ import { createRequire } from 'module';
20
20
  import { logger, logCommand, logInfo, logError } from './core/logger.js';
21
21
  import { metricsCollector } from './core/metrics.js';
22
22
  import { securityManager } from './core/security.js';
23
+ import { commandDescription, completionCommands, completionOptions } from './core/command-catalog.js';
23
24
  const require = createRequire(import.meta.url);
24
25
  const pkg = require('../package.json');
25
26
 
@@ -32,65 +33,73 @@ program
32
33
 
33
34
  program
34
35
  .command('explain <query>')
35
- .description('Explain a concept or command')
36
+ .description(commandDescription('explain'))
36
37
  .addHelpText('after', '\nExamples:\n $ dhruv explain "What is async/await?"\n $ dhruv explain "Docker containers vs VMs"')
37
38
  .action(explain);
38
39
 
39
40
  program
40
41
  .command('suggest <query>')
41
- .description('Get AI-powered suggestions')
42
+ .description(commandDescription('suggest'))
42
43
  .addHelpText('after', '\nExamples:\n $ dhruv suggest "React performance optimization"\n $ dhruv suggest "Node.js project structure"')
43
44
  .action(suggest);
44
45
 
45
46
  program
46
47
  .command('fix <query>')
47
- .description('Get a fix for a coding issue or error')
48
+ .description(commandDescription('fix'))
48
49
  .addHelpText('after', '\nExamples:\n $ dhruv fix "TypeError: Cannot read property of undefined"\n $ dhruv fix "CORS error in Express.js"')
49
50
  .action(fix);
50
51
 
51
52
  program
52
53
  .command('review <fileOrDir>')
53
- .description('Review code in a file or directory')
54
- .action(review);
54
+ .description(commandDescription('review'))
55
+ .option('--diff', 'Review the current uncommitted git diff')
56
+ .action((fileOrDir: string, command: Command) => review(fileOrDir, command.opts()));
55
57
 
56
58
  program
57
59
  .command('optimize <file>')
58
- .description('Optimize a file (e.g., package.json)')
60
+ .description(commandDescription('optimize'))
59
61
  .action(optimize);
60
62
 
61
63
  program
62
64
  .command('security-check [fileOrDir]')
63
- .description('Run a security check on code')
64
- .action(securityCheck);
65
+ .description(commandDescription('security-check'))
66
+ .option('--strict', 'Exit with failure when high-confidence findings are detected')
67
+ .action((fileOrDir: string | undefined, command: Command) => securityCheck(fileOrDir, command.opts()));
65
68
 
66
69
  program
67
70
  .command('generate <type> <target>')
68
- .description('Generate code/tests for a file')
69
- .action(generate);
71
+ .description(commandDescription('generate'))
72
+ .option('--apply', 'Write generated tests to disk (preview is the default)')
73
+ .option('--output <path>', 'Write generated tests to this path')
74
+ .option('--overwrite', 'Allow replacing an existing output file')
75
+ .action((type: string, target: string, command: Command) => generate(type, target, command.opts()));
70
76
 
71
77
  program
72
78
  .command('init')
73
- .description('Interactive setup/configuration wizard')
79
+ .description(commandDescription('init'))
74
80
  .action(init);
75
81
 
76
82
  program
77
83
  .command('status')
78
- .description('Check Ollama connection and available models')
84
+ .description(commandDescription('status'))
79
85
  .action(status);
80
86
 
81
87
  program
82
88
  .command('health')
83
- .description('Run comprehensive health check')
84
- .action(health);
89
+ .description(commandDescription('health'))
90
+ .option('--details', 'Show every health check and diagnostic detail')
91
+ .action((command: Command) => health(command.opts()));
85
92
 
86
93
  program
87
94
  .command('metrics')
88
- .description('Display CLI usage metrics')
89
- .action(metrics);
95
+ .description(commandDescription('metrics'))
96
+ .option('--raw', 'Export raw Prometheus metrics')
97
+ .option('--reset', 'Clear persisted local metrics')
98
+ .action((command: Command) => metrics(command.opts()));
90
99
 
91
100
  program
92
101
  .command('project-type')
93
- .description('Detect and print the current project type')
102
+ .description(commandDescription('project-type'))
94
103
  .action(() => {
95
104
  const type = detectProjectType();
96
105
  console.log(chalk.blue(`Detected project type: ${type}`));
@@ -98,20 +107,22 @@ program
98
107
 
99
108
  program
100
109
  .command('menu')
101
- .description('Interactive command palette')
110
+ .description(commandDescription('menu'))
102
111
  .action(menu);
103
112
 
104
113
  program
105
114
  .option('--model <model>', 'Set Ollama model')
106
115
  .option('--verbose', 'Enable verbose output')
107
116
  .option('--json', 'Output in JSON format')
117
+ .option('--timeout <milliseconds>', 'Set the AI request timeout')
108
118
  .hook('preAction', async (thisCommand) => {
109
119
  const opts = thisCommand.opts();
110
- if (opts.model || opts.verbose || opts.json) {
120
+ if (opts.model || opts.verbose || opts.json || opts.timeout) {
111
121
  const config: Record<string, unknown> = {};
112
122
  if (opts.model) config.model = opts.model;
113
123
  if (opts.verbose) config.verbose = true;
114
124
  if (opts.json) config.responseFormat = 'json';
125
+ if (opts.timeout) config.timeoutMs = Number(opts.timeout);
115
126
  // Save config for session
116
127
  const configModule = await import('./config/config.js');
117
128
  configModule.saveConfig(config);
@@ -165,23 +176,33 @@ async function loadPlugins(program: unknown) {
165
176
  // Autocomplete: Generate shell completion scripts
166
177
  program
167
178
  .command('completion')
168
- .description('Generate shell completion script')
179
+ .description(commandDescription('completion'))
169
180
  .argument('[shell]', 'shell type (bash|zsh|fish)', 'bash')
170
181
  .action((shell: string) => {
182
+ const commands = completionCommands();
183
+ const options = completionOptions();
171
184
  let script = '';
172
185
  switch (shell) {
173
186
  case 'zsh':
174
- script = `#compdef dhruv\n_dhruv_completion() {\n reply=( $(dhruv --help | awk '/Commands:/,/^$/ {if(NR>1)print $1}') )\n}\ncompctl -K _dhruv_completion dhruv`;
187
+ script = `#compdef dhruv\n_dhruv_completion() {\n _arguments '1:command:(${commands})' '*:option:(${options})'\n}\ncompdef _dhruv_completion dhruv`;
175
188
  break;
176
189
  case 'fish':
177
- script = `function __fish_dhruv_complete\n dhruv --help | awk '/Commands:/,/^$/ {if(NR>1)print $1}'\nend\ncomplete -c dhruv -a '(__fish_dhruv_complete)'`;
190
+ script = `complete -c dhruv -f -n '__fish_use_subcommand' -a '${commands}'\ncomplete -c dhruv -f -n 'not __fish_use_subcommand' -a '${options}'`;
178
191
  break;
179
- default:
192
+ case 'bash':
180
193
  script = String.raw`#!/bin/bash
181
194
  _dhruv_completion() {
182
- COMPREPLY=( $(compgen -W "$(dhruv --help | awk '/Commands:/,/^$/ {if(NR>1)print $1}')" -- \${COMP_WORDS[1]}) )
195
+ local commands="${commands}"
196
+ local options="${options}"
197
+ local choices="$commands $options"
198
+ COMPREPLY=( $(compgen -W "$choices" -- "\${COMP_WORDS[COMP_CWORD]}") )
183
199
  }
184
200
  complete -F _dhruv_completion dhruv`;
201
+ break;
202
+ default:
203
+ console.error(chalk.red(`Unsupported shell "${shell}". Choose bash, zsh, or fish.`));
204
+ process.exitCode = 2;
205
+ return;
185
206
  }
186
207
  console.log(script);
187
208
  console.log(`\n# To enable tab completion, add the above to your shell profile or source it directly.`);
@@ -1,14 +1,29 @@
1
1
  // Use .js extension for ESM compatibility
2
2
  import fs from 'fs';
3
+ import path from 'path';
3
4
 
4
- export function detectProjectType(): string {
5
- if (fs.existsSync('package.json')) {
6
- const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
7
- if (pkg.dependencies?.react || pkg.devDependencies?.react) return 'react';
8
- if (pkg.dependencies?.express || pkg.devDependencies?.express) return 'node-express';
5
+ export function detectProjectType(directory: string = process.cwd()): string {
6
+ const file = (name: string) => path.join(directory, name);
7
+
8
+ if (fs.existsSync(file('package.json'))) {
9
+ let pkg: { dependencies?: Record<string, string>; devDependencies?: Record<string, string> };
10
+ try {
11
+ pkg = JSON.parse(fs.readFileSync(file('package.json'), 'utf-8'));
12
+ } catch {
13
+ return 'unknown';
14
+ }
15
+
16
+ const dependencies = { ...pkg.dependencies, ...pkg.devDependencies };
17
+ if (dependencies.react) return 'react';
18
+ if (dependencies.next) return 'nextjs';
19
+ if (dependencies.express) return 'node-express';
20
+ if (dependencies.typescript || fs.existsSync(file('tsconfig.json'))) return 'node-typescript';
9
21
  return 'node';
10
22
  }
11
- if (fs.existsSync('requirements.txt')) return 'python';
12
- if (fs.existsSync('pyproject.toml')) return 'python';
23
+ if (fs.existsSync(file('requirements.txt'))) return 'python';
24
+ if (fs.existsSync(file('pyproject.toml'))) return 'python';
25
+ if (fs.existsSync(file('go.mod'))) return 'go';
26
+ if (fs.existsSync(file('Cargo.toml'))) return 'rust';
27
+ if (fs.existsSync(file('pom.xml')) || fs.existsSync(file('build.gradle'))) return 'java';
13
28
  return 'unknown';
14
29
  }
package/tsconfig.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "compilerOptions": {
3
3
  "target": "ES2020",
4
4
  "module": "ESNext",
5
- "moduleResolution": "Node",
5
+ "moduleResolution": "Bundler",
6
6
  "outDir": "./dist",
7
7
  "rootDir": "./src",
8
8
  "esModuleInterop": true,
@@ -1,14 +0,0 @@
1
- name: Auto Assign
2
-
3
- on:
4
- pull_request:
5
- types: [opened, reopened]
6
-
7
- jobs:
8
- add-assignees:
9
- runs-on: ubuntu-latest
10
- steps:
11
- - uses: kentaro-m/auto-assign-action@v1.2.4
12
- with:
13
- repo-token: ${{ secrets.GITHUB_TOKEN }}
14
- configuration-path: .github/auto_assign.yml
@@ -1,154 +0,0 @@
1
- name: Build & Publish
2
-
3
- on:
4
- # Automatic main releases are owned by release.yml.
5
- workflow_dispatch:
6
- inputs:
7
- version_type:
8
- description: 'Version bump type'
9
- required: true
10
- default: 'patch'
11
- type: choice
12
- options:
13
- - patch
14
- - minor
15
- - major
16
- - prerelease
17
-
18
- permissions:
19
- contents: write
20
- packages: write
21
- id-token: write
22
-
23
- jobs:
24
- build-and-publish:
25
- name: Build and Publish
26
- runs-on: ubuntu-latest
27
- timeout-minutes: 20
28
- environment: npm-publish
29
- steps:
30
- - name: Harden Runner
31
- uses: step-security/harden-runner@v2
32
- with:
33
- egress-policy: audit
34
-
35
- - uses: actions/checkout@v4
36
- with:
37
- fetch-depth: 0
38
- token: ${{ secrets.GITHUB_TOKEN }}
39
-
40
- - name: Set up Node.js
41
- uses: actions/setup-node@v4
42
- with:
43
- node-version: '22.14.0'
44
- cache: 'npm'
45
- registry-url: 'https://registry.npmjs.org'
46
-
47
- - name: Install npm with trusted publishing support
48
- run: npm install --global npm@11.17.0
49
-
50
- - name: Install dependencies
51
- run: npm ci --prefer-offline --no-audit
52
-
53
- - name: Run tests
54
- run: npm run test:ci
55
-
56
- - name: Run linting
57
- run: npm run lint
58
-
59
- - name: Build project
60
- run: npm run build
61
-
62
- - name: Generate documentation
63
- run: npm run docs
64
-
65
- - name: Configure Git
66
- run: |
67
- git config --global user.name "GitHub Actions"
68
- git config --global user.email "actions@github.com"
69
-
70
- - name: Check if version needs update
71
- id: check_version
72
- run: |
73
- CURRENT_VERSION=$(node -p "require('./package.json').version")
74
- echo "current_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
75
-
76
- # Check if this version exists on NPM
77
- if npm view @rahul05ranjan/dhruv-cli@$CURRENT_VERSION version 2>/dev/null; then
78
- echo "needs_version_bump=true" >> $GITHUB_OUTPUT
79
- echo "Version $CURRENT_VERSION already exists on NPM, needs bump"
80
- else
81
- echo "needs_version_bump=false" >> $GITHUB_OUTPUT
82
- echo "Version $CURRENT_VERSION is new, can publish"
83
- fi
84
-
85
- - name: Manual version bump
86
- if: github.event_name == 'workflow_dispatch'
87
- run: |
88
- npm version ${{ github.event.inputs.version_type }} --no-git-tag-version
89
- git add package.json package-lock.json
90
- git commit -m "chore: bump version to $(node -p "require('./package.json').version") [skip ci]"
91
- git push
92
-
93
- - name: Get final version
94
- id: final_version
95
- run: |
96
- VERSION=$(node -p "require('./package.json').version")
97
- echo "version=$VERSION" >> $GITHUB_OUTPUT
98
- echo "Final version to publish: $VERSION"
99
-
100
- - name: Create Git tag
101
- run: |
102
- TAG="v${{ steps.final_version.outputs.version }}"
103
- if git rev-parse "$TAG" >/dev/null 2>&1; then
104
- echo "Tag $TAG already exists, skipping tag creation"
105
- else
106
- git tag "$TAG"
107
- git push origin "$TAG"
108
- fi
109
-
110
- - name: Publish to NPM
111
- # npm 11.5.1+ uses the workflow's OIDC identity without a stored token.
112
- run: |
113
- echo "Publishing version ${{ steps.final_version.outputs.version }}"
114
- npm publish --access public --tag latest --provenance
115
-
116
- - name: Create GitHub Release
117
- uses: softprops/action-gh-release@v2
118
- with:
119
- tag_name: v${{ steps.final_version.outputs.version }}
120
- name: Release v${{ steps.final_version.outputs.version }}
121
- body: |
122
- ## What's Changed
123
-
124
- Version ${{ steps.final_version.outputs.version }} of dhruv-cli
125
-
126
- ### Installation
127
- ```bash
128
- npm install -g @rahul05ranjan/dhruv-cli@${{ steps.final_version.outputs.version }}
129
- ```
130
-
131
- **Full Changelog**: https://github.com/${{ github.repository }}/compare/v${{ steps.check_version.outputs.current_version }}...v${{ steps.final_version.outputs.version }}
132
- files: |
133
- dist/**
134
- docs/api/**
135
- draft: false
136
- prerelease: false
137
- env:
138
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
139
-
140
- - name: Update documentation
141
- uses: peaceiris/actions-gh-pages@v4
142
- with:
143
- github_token: ${{ secrets.GITHUB_TOKEN }}
144
- publish_dir: ./docs/api
145
- publish_branch: gh-pages
146
- enable_jekyll: false
147
-
148
- - name: Notify success
149
- if: success()
150
- run: |
151
- echo "✅ Successfully published @rahul05ranjan/dhruv-cli@${{ steps.final_version.outputs.version }}"
152
- echo "📦 NPM: https://www.npmjs.com/package/@rahul05ranjan/dhruv-cli"
153
- echo "📖 Documentation: https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}"
154
- echo "🏷️ Release: https://github.com/${{ github.repository }}/releases/tag/v${{ steps.final_version.outputs.version }}"