@rahul05ranjan/dhruv-cli 1.8.1 → 1.8.2

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/CHANGELOG.md CHANGED
@@ -1,9 +1,9 @@
1
- # [1.4.0](https://github.com/rahul05ranjan/dhruv-cli/compare/v1.8.0...v1.4.0) (2026-09-21)
1
+ # [1.4.0](https://github.com/rahul05ranjan/dhruv-cli/compare/v1.8.1...v1.4.0) (2026-09-22)
2
2
 
3
3
 
4
4
  ### Bug Fixes
5
5
 
6
- * **health:** exit with code 1 on FAIL in text mode (closes [#93](https://github.com/rahul05ranjan/dhruv-cli/issues/93)) ([272fdd8](https://github.com/rahul05ranjan/dhruv-cli/commit/272fdd8e14d6f46227ee47756c1e556c49897c12))
6
+ * **cli:** exit with code 1 on missing or unreadable input (closes [#94](https://github.com/rahul05ranjan/dhruv-cli/issues/94)) ([90c19fa](https://github.com/rahul05ranjan/dhruv-cli/commit/90c19fa926f1ef781c4137dc1713626854b2ca08))
7
7
  # Changelog
8
8
 
9
9
  ## [1.1.1](https://github.com/rahul05ranjan/dhruv-cli/compare/v1.1.0...v1.1.1) (2025-06-29)
@@ -231,4 +231,60 @@ describe('file analysis commands', () => {
231
231
  expect(client.requests[0].prompt).toContain('expected impact');
232
232
  expect(client.requests[0].prompt).toContain('trade-offs');
233
233
  });
234
+
235
+ it('exits with code 1 and skips AI call when review target does not exist', async () => {
236
+ process.exitCode = 0;
237
+ const nonExistent = path.join(root, 'nonexistent.ts');
238
+
239
+ await review(nonExistent);
240
+
241
+ expect(process.exitCode).toBe(1);
242
+ expect(client.requests).toHaveLength(0);
243
+ process.exitCode = 0;
244
+ });
245
+
246
+ it('exits with code 1 and skips AI call when review --diff has no uncommitted changes', async () => {
247
+ process.exitCode = 0;
248
+ const { execFileSync } = await import('child_process');
249
+ execFileSync('git', ['init'], { cwd: root });
250
+
251
+ await review(root, { diff: true });
252
+
253
+ expect(process.exitCode).toBe(1);
254
+ expect(client.requests).toHaveLength(0);
255
+ process.exitCode = 0;
256
+ });
257
+
258
+ it('exits with code 1 and skips AI call when security-check target does not exist', async () => {
259
+ process.exitCode = 0;
260
+ const nonExistent = path.join(root, 'nonexistent.ts');
261
+
262
+ await securityCheck(nonExistent);
263
+
264
+ expect(process.exitCode).toBe(1);
265
+ expect(client.requests).toHaveLength(0);
266
+ process.exitCode = 0;
267
+ });
268
+
269
+ it('exits with code 1 and skips AI call when generate target does not exist', async () => {
270
+ process.exitCode = 0;
271
+ const nonExistent = path.join(root, 'nonexistent.ts');
272
+
273
+ await generate('tests', nonExistent);
274
+
275
+ expect(process.exitCode).toBe(1);
276
+ expect(client.requests).toHaveLength(0);
277
+ process.exitCode = 0;
278
+ });
279
+
280
+ it('exits with code 1 and skips AI call when optimize target does not exist', async () => {
281
+ process.exitCode = 0;
282
+ const nonExistent = path.join(root, 'nonexistent.ts');
283
+
284
+ await optimize(nonExistent);
285
+
286
+ expect(process.exitCode).toBe(1);
287
+ expect(client.requests).toHaveLength(0);
288
+ process.exitCode = 0;
289
+ });
234
290
  });
@@ -45,6 +45,7 @@ function extractTestCode(response) {
45
45
  export async function generate(type, target, options = {}) {
46
46
  if (!fs.existsSync(target)) {
47
47
  printError(`Target file "${target}" does not exist.`);
48
+ process.exitCode = 1;
48
49
  return;
49
50
  }
50
51
  const content = fs.readFileSync(target, 'utf-8');
@@ -21,10 +21,12 @@ function optimizationType(file) {
21
21
  export async function optimize(file) {
22
22
  if (!file || file.trim().length === 0) {
23
23
  printError('Please provide a file path to optimize.');
24
+ process.exitCode = 1;
24
25
  return;
25
26
  }
26
27
  if (!fs.existsSync(file)) {
27
28
  printError(`File "${file}" does not exist.`);
29
+ process.exitCode = 1;
28
30
  return;
29
31
  }
30
32
  let content;
@@ -33,6 +35,7 @@ export async function optimize(file) {
33
35
  }
34
36
  catch (err) {
35
37
  printError(`Error reading file "${file}": ${err.message}`);
38
+ process.exitCode = 1;
36
39
  return;
37
40
  }
38
41
  const type = optimizationType(file);
@@ -96,8 +96,10 @@ function readGitDiff(fileOrDir) {
96
96
  }
97
97
  export async function review(fileOrDir, options = {}) {
98
98
  const code = options.diff ? readGitDiff(fileOrDir) : readCode(fileOrDir);
99
- if (code === undefined)
99
+ if (code === undefined) {
100
+ process.exitCode = 1;
100
101
  return;
102
+ }
101
103
  const projectRoot = fs.existsSync(fileOrDir) && fs.statSync(fileOrDir).isDirectory() ? fileOrDir : path.dirname(fileOrDir);
102
104
  const projectType = detectProjectType(projectRoot);
103
105
  const scope = options.diff ? 'the current uncommitted git diff' : 'the supplied source files';
@@ -128,8 +128,10 @@ function readDirectory(dir) {
128
128
  }
129
129
  export async function securityCheck(fileOrDir = '.', options = {}) {
130
130
  const code = readCode(fileOrDir);
131
- if (code === undefined)
131
+ if (code === undefined) {
132
+ process.exitCode = 1;
132
133
  return;
134
+ }
133
135
  const findings = findHighConfidenceFindings(code);
134
136
  const safeCode = redactSensitiveContent(code);
135
137
  const findingSummary = findings.length === 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rahul05ranjan/dhruv-cli",
3
- "version": "1.8.1",
3
+ "version": "1.8.2",
4
4
  "description": "AI-powered CLI assistant for developers using Ollama",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -54,6 +54,7 @@ export interface GenerateOptions {
54
54
  export async function generate(type: string, target: string, options: GenerateOptions = {}) {
55
55
  if (!fs.existsSync(target)) {
56
56
  printError(`Target file "${target}" does not exist.`);
57
+ process.exitCode = 1;
57
58
  return;
58
59
  }
59
60
 
@@ -18,10 +18,12 @@ function optimizationType(file: string): string {
18
18
  export async function optimize(file: string) {
19
19
  if (!file || file.trim().length === 0) {
20
20
  printError('Please provide a file path to optimize.');
21
+ process.exitCode = 1;
21
22
  return;
22
23
  }
23
24
  if (!fs.existsSync(file)) {
24
25
  printError(`File "${file}" does not exist.`);
26
+ process.exitCode = 1;
25
27
  return;
26
28
  }
27
29
 
@@ -30,6 +32,7 @@ export async function optimize(file: string) {
30
32
  content = fs.readFileSync(file, 'utf-8');
31
33
  } catch (err) {
32
34
  printError(`Error reading file "${file}": ${(err as Error).message}`);
35
+ process.exitCode = 1;
33
36
  return;
34
37
  }
35
38
 
@@ -105,7 +105,10 @@ export interface ReviewOptions {
105
105
 
106
106
  export async function review(fileOrDir: string, options: ReviewOptions = {}) {
107
107
  const code = options.diff ? readGitDiff(fileOrDir) : readCode(fileOrDir);
108
- if (code === undefined) return;
108
+ if (code === undefined) {
109
+ process.exitCode = 1;
110
+ return;
111
+ }
109
112
  const projectRoot = fs.existsSync(fileOrDir) && fs.statSync(fileOrDir).isDirectory() ? fileOrDir : path.dirname(fileOrDir);
110
113
  const projectType = detectProjectType(projectRoot);
111
114
  const scope = options.diff ? 'the current uncommitted git diff' : 'the supplied source files';
@@ -141,7 +141,10 @@ function readDirectory(dir: string): string | undefined {
141
141
 
142
142
  export async function securityCheck(fileOrDir: string = '.', options: SecurityCheckOptions = {}) {
143
143
  const code = readCode(fileOrDir);
144
- if (code === undefined) return;
144
+ if (code === undefined) {
145
+ process.exitCode = 1;
146
+ return;
147
+ }
145
148
  const findings = findHighConfidenceFindings(code);
146
149
  const safeCode = redactSensitiveContent(code);
147
150
  const findingSummary = findings.length === 0