@ganakailabs/cloudeval-cli 0.18.5 → 0.19.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/README.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # CloudEval CLI
2
2
 
3
+ <p align="center">
4
+ <img src="https://raw.githubusercontent.com/ganakailabs/cloudeval-cli/main/docs/assets/cloudeval-cli-banner.png" alt="CloudEval CLI terminal banner" width="100%">
5
+ </p>
6
+
7
+ <p align="center">
8
+ <a href="https://www.npmjs.com/package/@ganakailabs/cloudeval-cli"><img alt="npm" src="https://img.shields.io/npm/v/@ganakailabs/cloudeval-cli?style=flat-square"></a>
9
+ <a href="https://github.com/ganakailabs/cloudeval-cli/releases"><img alt="release" src="https://img.shields.io/github/v/release/ganakailabs/cloudeval-cli?sort=semver&style=flat-square"></a>
10
+ <a href="https://github.com/ganakailabs/cloudeval-cli/blob/main/LICENSE"><img alt="license" src="https://img.shields.io/badge/license-CloudEval%20CLI%20License-blue?style=flat-square"></a>
11
+ <a href="https://docs.cloudeval.ai/reference/cli-overview"><img alt="docs" src="https://img.shields.io/badge/docs-docs.cloudeval.ai-2d6cdf?style=flat-square"></a>
12
+ </p>
13
+
3
14
  CloudEval CLI brings CloudEval into your terminal, scripts, and agent tools. It
4
15
  supports cloud chat, Agent mode, Agent Profiles, project and report inspection,
5
16
  template validation, recipes, local hooks, and MCP server workflows.
@@ -42,6 +53,33 @@ cloudeval mcp serve --toolset readonly
42
53
  cloudeval capabilities --format json
43
54
  ```
44
55
 
56
+ ## Uninstall
57
+
58
+ To remove local installer-owned artifacts while keeping CloudEval config,
59
+ sessions, and auth:
60
+
61
+ ```bash
62
+ cloudeval uninstall --yes
63
+ ```
64
+
65
+ To preview cleanup first:
66
+
67
+ ```bash
68
+ cloudeval uninstall --dry-run
69
+ ```
70
+
71
+ To remove local config and session state too:
72
+
73
+ ```bash
74
+ cloudeval uninstall --yes --remove-config
75
+ ```
76
+
77
+ If you installed through npm, remove the npm package as the final step:
78
+
79
+ ```bash
80
+ npm uninstall -g @ganakailabs/cloudeval-cli
81
+ ```
82
+
45
83
  ## What It Covers
46
84
 
47
85
  - Terminal UI for chat, Agent mode, projects, reports, billing, and settings.
@@ -63,3 +101,14 @@ then provide it as `CLOUDEVAL_ACCESS_KEY`.
63
101
  - Command reference: <https://docs.cloudeval.ai/reference/cli-command-reference>
64
102
  - MCP setup: <https://docs.cloudeval.ai/reference/mcp-client-setup>
65
103
  - Release smoke tests: <https://github.com/ganakailabs/cloudeval-cli/blob/main/docs/release-smoke-tests.md>
104
+
105
+ ## License And Notices
106
+
107
+ CloudEval CLI first-party code is provided under the
108
+ [CloudEval CLI License](https://github.com/ganakailabs/cloudeval-cli/blob/main/LICENSE).
109
+ Production third-party package attribution is published in
110
+ [THIRD_PARTY_NOTICES.md](https://github.com/ganakailabs/cloudeval-cli/blob/main/THIRD_PARTY_NOTICES.md),
111
+ and the release SBOM is published as
112
+ [sbom.spdx.json](https://github.com/ganakailabs/cloudeval-cli/blob/main/sbom.spdx.json).
113
+ Installer releases also place these notice files under
114
+ `~/.local/share/cloudeval/licenses`.
@@ -31,10 +31,10 @@ import {
31
31
  } from "./chunk-4QIKW5TJ.js";
32
32
  import {
33
33
  Banner
34
- } from "./chunk-Z5AYM56N.js";
34
+ } from "./chunk-T5TH36K2.js";
35
35
  import {
36
36
  CLI_VERSION
37
- } from "./chunk-NF3ETGO7.js";
37
+ } from "./chunk-ULQTRACZ.js";
38
38
  import {
39
39
  raisedButtonStyle,
40
40
  terminalTheme
@@ -2,8 +2,8 @@ import {
2
2
  Banner,
3
3
  bannerSegmentColor,
4
4
  splitBannerLineSegments
5
- } from "./chunk-Z5AYM56N.js";
6
- import "./chunk-NF3ETGO7.js";
5
+ } from "./chunk-T5TH36K2.js";
6
+ import "./chunk-ULQTRACZ.js";
7
7
  import "./chunk-UOCT7M4J.js";
8
8
  export {
9
9
  Banner,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  CLI_VERSION
3
- } from "./chunk-NF3ETGO7.js";
3
+ } from "./chunk-ULQTRACZ.js";
4
4
  import {
5
5
  shouldUseColor,
6
6
  terminalTheme
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var CLI_VERSION = "0.18.5";
2
+ var CLI_VERSION = "0.19.0";
3
3
 
4
4
  export {
5
5
  CLI_VERSION
package/dist/cli.js CHANGED
@@ -16,7 +16,7 @@ import {
16
16
  } from "./chunk-4QIKW5TJ.js";
17
17
  import {
18
18
  CLI_VERSION
19
- } from "./chunk-NF3ETGO7.js";
19
+ } from "./chunk-ULQTRACZ.js";
20
20
 
21
21
  // src/runtime/prepareInk.ts
22
22
  import fs from "fs";
@@ -115,9 +115,9 @@ ensureInkRuntimeEnvironment();
115
115
  // src/cli.tsx
116
116
  import React from "react";
117
117
  import { Command } from "commander";
118
- import { promises as fs11 } from "fs";
119
- import os4 from "os";
120
- import path9 from "path";
118
+ import { promises as fs12 } from "fs";
119
+ import os5 from "os";
120
+ import path10 from "path";
121
121
 
122
122
  // src/shellCompletion.ts
123
123
  var normalizeCompletionShell = (shell) => {
@@ -780,6 +780,25 @@ var cliCommands = [
780
780
  ],
781
781
  workflows: ["update"]
782
782
  },
783
+ {
784
+ name: "uninstall",
785
+ description: "Remove local CloudEval CLI installation artifacts",
786
+ domain: "setup",
787
+ options: [
788
+ "--yes",
789
+ "-y",
790
+ "--dry-run",
791
+ "--keep-config",
792
+ "--remove-config",
793
+ "--format",
794
+ "-f",
795
+ "--output",
796
+ "-o",
797
+ "--profile",
798
+ "--help"
799
+ ],
800
+ workflows: ["uninstall"]
801
+ },
783
802
  {
784
803
  name: "banner",
785
804
  description: "Preview the startup banner and terminal capabilities",
@@ -1371,10 +1390,10 @@ var writeFormattedOutput = async (input) => {
1371
1390
  process.stdout.write(text);
1372
1391
  };
1373
1392
  var writePrivateOutputFile = async (output, text) => {
1374
- const fs12 = await import("fs/promises");
1375
- await fs12.writeFile(output, text, { encoding: "utf8", mode: 384 });
1393
+ const fs13 = await import("fs/promises");
1394
+ await fs13.writeFile(output, text, { encoding: "utf8", mode: 384 });
1376
1395
  if (process.platform !== "win32") {
1377
- await fs12.chmod(output, 384);
1396
+ await fs13.chmod(output, 384);
1378
1397
  }
1379
1398
  };
1380
1399
 
@@ -1722,8 +1741,8 @@ var writeReport = async (report, options, tuiDefault) => {
1722
1741
  const textFormat = format === "text" || format === "table" ? "summary" : format;
1723
1742
  const text = serializeReportOutput(report, { format: textFormat, mode });
1724
1743
  if (options.output) {
1725
- const fs12 = await import("fs/promises");
1726
- await fs12.writeFile(options.output, text, "utf8");
1744
+ const fs13 = await import("fs/promises");
1745
+ await fs13.writeFile(options.output, text, "utf8");
1727
1746
  return;
1728
1747
  }
1729
1748
  process.stdout.write(text);
@@ -1758,16 +1777,16 @@ var writeDownloadPayload = async (input) => {
1758
1777
  );
1759
1778
  return [];
1760
1779
  }
1761
- const fs12 = await import("fs/promises");
1762
- const path10 = await import("path");
1763
- await fs12.mkdir(path10.dirname(input.output), { recursive: true });
1780
+ const fs13 = await import("fs/promises");
1781
+ const path11 = await import("path");
1782
+ await fs13.mkdir(path11.dirname(input.output), { recursive: true });
1764
1783
  const text = formatOutput({
1765
1784
  command: input.command,
1766
1785
  data: input.payload,
1767
1786
  format: input.format,
1768
1787
  frontendUrl: input.frontendUrl
1769
1788
  });
1770
- await fs12.writeFile(input.output, text, "utf8");
1789
+ await fs13.writeFile(input.output, text, "utf8");
1771
1790
  return [input.output];
1772
1791
  };
1773
1792
  var resolveMachineFormat = (requested) => {
@@ -1916,14 +1935,14 @@ var registerReportsCommand = (program2, deps) => {
1916
1935
  );
1917
1936
  const data = reportTypes.length === 1 ? payload[reportTypes[0] === "architecture" ? "waf" : reportTypes[0]] : payload;
1918
1937
  if (options.output && reportTypes.length > 1) {
1919
- const fs12 = await import("fs/promises");
1920
- const path10 = await import("path");
1921
- const stat = await fs12.stat(options.output).catch(() => void 0);
1922
- if (stat?.isDirectory() || !path10.extname(options.output)) {
1923
- await fs12.mkdir(options.output, { recursive: true });
1938
+ const fs13 = await import("fs/promises");
1939
+ const path11 = await import("path");
1940
+ const stat = await fs13.stat(options.output).catch(() => void 0);
1941
+ if (stat?.isDirectory() || !path11.extname(options.output)) {
1942
+ await fs13.mkdir(options.output, { recursive: true });
1924
1943
  const files = [];
1925
1944
  for (const [key, value] of Object.entries(payload)) {
1926
- const file = path10.join(options.output, `${projectId}-${key}-report.json`);
1945
+ const file = path11.join(options.output, `${projectId}-${key}-report.json`);
1927
1946
  files.push(
1928
1947
  ...await writeDownloadPayload({
1929
1948
  command: "reports download",
@@ -4668,12 +4687,12 @@ var responseErrorMessage = async (response) => {
4668
4687
  var fetchCloudEvalJson = async ({
4669
4688
  baseUrl,
4670
4689
  authToken,
4671
- path: path10,
4690
+ path: path11,
4672
4691
  method = "GET",
4673
4692
  query = {},
4674
4693
  body
4675
4694
  }) => {
4676
- const url = new URL(`${normalizeApiBase(baseUrl)}${path10}`);
4695
+ const url = new URL(`${normalizeApiBase(baseUrl)}${path11}`);
4677
4696
  for (const [key, value] of Object.entries(query)) {
4678
4697
  if (value !== void 0 && value !== null && value !== "") {
4679
4698
  url.searchParams.set(key, String(value));
@@ -5264,8 +5283,8 @@ var writeConnectionsListOutput = async ({
5264
5283
  if (format === "text") {
5265
5284
  const text = renderConnectionsListText(data);
5266
5285
  if (options.output) {
5267
- const fs12 = await import("fs/promises");
5268
- await fs12.writeFile(options.output, text, "utf8");
5286
+ const fs13 = await import("fs/promises");
5287
+ await fs13.writeFile(options.output, text, "utf8");
5269
5288
  return;
5270
5289
  }
5271
5290
  process.stdout.write(text);
@@ -5599,8 +5618,8 @@ var write = async (command, data, options, frontendUrl) => {
5599
5618
  const text = renderBillingText(command, data);
5600
5619
  if (text) {
5601
5620
  if (options.output) {
5602
- const fs12 = await import("fs/promises");
5603
- await fs12.writeFile(options.output, text, "utf8");
5621
+ const fs13 = await import("fs/promises");
5622
+ await fs13.writeFile(options.output, text, "utf8");
5604
5623
  return;
5605
5624
  }
5606
5625
  process.stdout.write(text);
@@ -11450,10 +11469,10 @@ var registerConfigCommand = (program2) => {
11450
11469
  const profile = resolveProfile(options, command);
11451
11470
  const current = await loadCliConfig(profile);
11452
11471
  const next = writeCliConfigValue(current, key, value);
11453
- const path10 = await saveCliConfig(next, profile);
11472
+ const path11 = await saveCliConfig(next, profile);
11454
11473
  await writeFormattedOutput({
11455
11474
  command: "config set",
11456
- data: { profile, path: path10, config: next },
11475
+ data: { profile, path: path11, config: next },
11457
11476
  format: options.format,
11458
11477
  output: options.output
11459
11478
  });
@@ -11464,10 +11483,10 @@ var registerConfigCommand = (program2) => {
11464
11483
  const profile = resolveProfile(options, command);
11465
11484
  const current = await loadCliConfig(profile);
11466
11485
  const next = unsetCliConfigValue(current, key);
11467
- const path10 = await saveCliConfig(next, profile);
11486
+ const path11 = await saveCliConfig(next, profile);
11468
11487
  await writeFormattedOutput({
11469
11488
  command: "config unset",
11470
- data: { profile, path: path10, config: next },
11489
+ data: { profile, path: path11, config: next },
11471
11490
  format: options.format,
11472
11491
  output: options.output
11473
11492
  });
@@ -11764,8 +11783,8 @@ var writeModelsListOutput = async (input) => {
11764
11783
  defaultModel: input.defaultModel
11765
11784
  });
11766
11785
  if (input.options.output) {
11767
- const fs12 = await import("fs/promises");
11768
- await fs12.writeFile(input.options.output, text, "utf8");
11786
+ const fs13 = await import("fs/promises");
11787
+ await fs13.writeFile(input.options.output, text, "utf8");
11769
11788
  return;
11770
11789
  }
11771
11790
  process.stdout.write(text);
@@ -11831,10 +11850,10 @@ var registerModelsCommand = (program2, deps) => {
11831
11850
  const profile = options.profile || getActiveConfigProfile(command);
11832
11851
  const config = await loadCliConfig(profile);
11833
11852
  const next = { ...config, model };
11834
- const path10 = await saveCliConfig(next, profile);
11853
+ const path11 = await saveCliConfig(next, profile);
11835
11854
  await writeFormattedOutput({
11836
11855
  command: "models default set",
11837
- data: { profile, path: path10, model },
11856
+ data: { profile, path: path11, model },
11838
11857
  format: options.format,
11839
11858
  output: options.output
11840
11859
  });
@@ -11877,8 +11896,8 @@ var writeSessionTableOutput = async (command, data, options) => {
11877
11896
  if (format === "text") {
11878
11897
  const text = renderSessionsTable(data);
11879
11898
  if (options.output) {
11880
- const fs12 = await import("fs/promises");
11881
- await fs12.writeFile(options.output, text, "utf8");
11899
+ const fs13 = await import("fs/promises");
11900
+ await fs13.writeFile(options.output, text, "utf8");
11882
11901
  return;
11883
11902
  }
11884
11903
  process.stdout.write(text);
@@ -12050,12 +12069,12 @@ var registerSetupCommand = (program2, defaultBaseUrl = CLOUD_BASE_URL) => {
12050
12069
  rl.close();
12051
12070
  }
12052
12071
  }
12053
- const path10 = await saveCliConfig(next, profile);
12072
+ const path11 = await saveCliConfig(next, profile);
12054
12073
  await writeFormattedOutput({
12055
12074
  command: "setup",
12056
12075
  data: {
12057
12076
  profile,
12058
- path: path10,
12077
+ path: path11,
12059
12078
  config: next,
12060
12079
  nextSteps: [
12061
12080
  "Run `cloudeval auth status` to inspect authentication.",
@@ -12380,6 +12399,7 @@ var handleUpdateCommand = async (options, deps = {}) => {
12380
12399
  };
12381
12400
  var suppressedNudgeCommands = /* @__PURE__ */ new Set([
12382
12401
  "update",
12402
+ "uninstall",
12383
12403
  "completion",
12384
12404
  "help",
12385
12405
  "capabilities",
@@ -12470,8 +12490,274 @@ var registerUpdateCommand = (program2) => {
12470
12490
  });
12471
12491
  };
12472
12492
 
12473
- // src/hitlPrompt.ts
12493
+ // src/uninstallCommand.ts
12494
+ import fs11 from "fs/promises";
12495
+ import os4 from "os";
12496
+ import path9 from "path";
12474
12497
  import { createInterface as createInterface3 } from "readline/promises";
12498
+ var pathExists = async (candidate) => {
12499
+ try {
12500
+ await fs11.lstat(candidate);
12501
+ return true;
12502
+ } catch (error) {
12503
+ if (error?.code === "ENOENT") {
12504
+ return false;
12505
+ }
12506
+ throw error;
12507
+ }
12508
+ };
12509
+ var installerBinDir = (home) => path9.join(home, ".local", "bin");
12510
+ var completionPaths = (home) => [
12511
+ path9.join(home, ".local", "share", "bash-completion", "completions", "cloudeval"),
12512
+ path9.join(home, ".zsh", "completions", "_cloudeval"),
12513
+ path9.join(home, ".config", "fish", "completions", "cloudeval.fish"),
12514
+ path9.join(home, ".config", "powershell", "cloudeval-completion.ps1")
12515
+ ];
12516
+ var installerArtifactTargets = (home, platform) => {
12517
+ const binDir = installerBinDir(home);
12518
+ const executableName = platform === "win32" ? "cloudeval.exe" : "cloudeval";
12519
+ const targets = [
12520
+ {
12521
+ label: "cloudeval binary",
12522
+ path: path9.join(binDir, executableName),
12523
+ kind: "file",
12524
+ status: "missing"
12525
+ },
12526
+ {
12527
+ label: "cloudeval binary",
12528
+ path: path9.join(binDir, "cloudeval"),
12529
+ kind: "file",
12530
+ status: "missing"
12531
+ },
12532
+ {
12533
+ label: "eva alias",
12534
+ path: path9.join(binDir, "eva"),
12535
+ kind: "file",
12536
+ status: "missing"
12537
+ },
12538
+ {
12539
+ label: "cloud alias",
12540
+ path: path9.join(binDir, "cloud"),
12541
+ kind: "file",
12542
+ status: "missing"
12543
+ },
12544
+ {
12545
+ label: "Ink runtime asset",
12546
+ path: path9.join(binDir, "yoga.wasm"),
12547
+ kind: "file",
12548
+ status: "missing"
12549
+ },
12550
+ {
12551
+ label: "license notices",
12552
+ path: path9.join(home, ".local", "share", "cloudeval", "licenses"),
12553
+ kind: "directory",
12554
+ status: "missing"
12555
+ },
12556
+ ...completionPaths(home).map((completionPath) => ({
12557
+ label: "shell completion",
12558
+ path: completionPath,
12559
+ kind: "file",
12560
+ status: "missing"
12561
+ }))
12562
+ ];
12563
+ return targets.filter(
12564
+ (target, index) => targets.findIndex((candidate) => candidate.path === target.path) === index
12565
+ );
12566
+ };
12567
+ var shellProfilePaths = (home) => [
12568
+ path9.join(home, ".bashrc"),
12569
+ path9.join(home, ".bash_profile"),
12570
+ path9.join(home, ".zshrc"),
12571
+ path9.join(home, ".profile"),
12572
+ path9.join(home, ".config", "fish", "config.fish")
12573
+ ];
12574
+ var removeInstallerPathSnippet = (content, binDir) => {
12575
+ const exportLine = `export PATH="${binDir}:$PATH"`;
12576
+ const fishLine = `set -gx PATH "${binDir}" $PATH`;
12577
+ const lines = content.split(/\r?\n/);
12578
+ let changed = false;
12579
+ for (let index = 0; index < lines.length; index += 1) {
12580
+ if (lines[index] === "# Cloudeval CLI" && (lines[index + 1] === exportLine || lines[index + 1] === fishLine)) {
12581
+ const removeFrom = index > 0 && lines[index - 1] === "" ? index - 1 : index;
12582
+ lines.splice(removeFrom, index - removeFrom + 2);
12583
+ changed = true;
12584
+ index = Math.max(removeFrom - 1, -1);
12585
+ }
12586
+ }
12587
+ if (!changed) {
12588
+ return void 0;
12589
+ }
12590
+ return lines.join("\n");
12591
+ };
12592
+ var removeTarget = async (target, dryRun) => {
12593
+ if (!await pathExists(target.path)) {
12594
+ return { ...target, status: "missing" };
12595
+ }
12596
+ if (dryRun) {
12597
+ return { ...target, status: "would_remove" };
12598
+ }
12599
+ await fs11.rm(target.path, { recursive: target.kind === "directory", force: true });
12600
+ return { ...target, status: "removed" };
12601
+ };
12602
+ var updateShellProfile = async (profilePath, home, dryRun) => {
12603
+ if (!await pathExists(profilePath)) {
12604
+ return {
12605
+ label: "shell profile PATH entry",
12606
+ path: profilePath,
12607
+ kind: "shell-profile",
12608
+ status: "missing"
12609
+ };
12610
+ }
12611
+ const content = await fs11.readFile(profilePath, "utf8");
12612
+ const updated = removeInstallerPathSnippet(content, installerBinDir(home));
12613
+ if (updated === void 0) {
12614
+ return {
12615
+ label: "shell profile PATH entry",
12616
+ path: profilePath,
12617
+ kind: "shell-profile",
12618
+ status: "missing"
12619
+ };
12620
+ }
12621
+ if (dryRun) {
12622
+ return {
12623
+ label: "shell profile PATH entry",
12624
+ path: profilePath,
12625
+ kind: "shell-profile",
12626
+ status: "would_update"
12627
+ };
12628
+ }
12629
+ await fs11.writeFile(profilePath, updated, "utf8");
12630
+ return {
12631
+ label: "shell profile PATH entry",
12632
+ path: profilePath,
12633
+ kind: "shell-profile",
12634
+ status: "updated"
12635
+ };
12636
+ };
12637
+ var confirmUninstall = async ({
12638
+ input = process.stdin,
12639
+ output = process.stderr
12640
+ }) => {
12641
+ const rl = createInterface3({ input, output });
12642
+ try {
12643
+ const answer = await rl.question(
12644
+ "Remove CloudEval CLI local installation artifacts? Config is kept unless --remove-config is set. [y/N] "
12645
+ );
12646
+ return /^(y|yes)$/i.test(answer.trim());
12647
+ } finally {
12648
+ rl.close();
12649
+ }
12650
+ };
12651
+ var handleUninstallCommand = async (options, deps = {}) => {
12652
+ const home = deps.home ?? os4.homedir();
12653
+ const platform = deps.platform ?? process.platform;
12654
+ const dryRun = Boolean(options.dryRun);
12655
+ const removeConfig = Boolean(options.removeConfig);
12656
+ const input = deps.input ?? process.stdin;
12657
+ const output = deps.output ?? process.stderr;
12658
+ const inputIsTTY = deps.inputIsTTY ?? ("isTTY" in input ? Boolean(input.isTTY) : false);
12659
+ if (!dryRun && !options.yes) {
12660
+ if (!inputIsTTY) {
12661
+ throw new Error(
12662
+ "CloudEval uninstall requires confirmation. Re-run with --yes for non-interactive removal."
12663
+ );
12664
+ }
12665
+ const confirmed = await confirmUninstall({ input, output });
12666
+ if (!confirmed) {
12667
+ throw new Error("CloudEval uninstall cancelled.");
12668
+ }
12669
+ }
12670
+ const actions = [];
12671
+ for (const target of installerArtifactTargets(home, platform)) {
12672
+ actions.push(await removeTarget(target, dryRun));
12673
+ }
12674
+ for (const profilePath of shellProfilePaths(home)) {
12675
+ const action = await updateShellProfile(profilePath, home, dryRun);
12676
+ if (action.status !== "missing") {
12677
+ actions.push(action);
12678
+ }
12679
+ }
12680
+ const configTarget = {
12681
+ label: "config",
12682
+ path: path9.join(home, ".config", "cloudeval"),
12683
+ kind: "directory",
12684
+ status: "kept"
12685
+ };
12686
+ if (removeConfig) {
12687
+ actions.push(await removeTarget(configTarget, dryRun));
12688
+ } else {
12689
+ actions.push(configTarget);
12690
+ }
12691
+ return {
12692
+ dryRun,
12693
+ removeConfig,
12694
+ actions,
12695
+ notes: [
12696
+ "If you installed the npm package globally, finish with `npm uninstall -g @ganakailabs/cloudeval-cli`.",
12697
+ "MCP client configuration is left untouched. Update or remove MCP entries from each client if needed."
12698
+ ]
12699
+ };
12700
+ };
12701
+ var formatUninstallResultText = (result) => {
12702
+ const lines = [
12703
+ "CloudEval CLI Uninstall",
12704
+ `Mode: ${result.dryRun ? "dry run" : "applied"}`,
12705
+ `Config: ${result.removeConfig ? "removed when present" : "kept"}`
12706
+ ];
12707
+ const changed = result.actions.filter(
12708
+ (action) => ["removed", "updated", "would_remove", "would_update"].includes(action.status)
12709
+ );
12710
+ const kept = result.actions.filter((action) => action.status === "kept");
12711
+ if (changed.length) {
12712
+ lines.push("", result.dryRun ? "Would clean:" : "Cleaned:");
12713
+ for (const action of changed) {
12714
+ lines.push(`- ${action.label}: ${action.path}`);
12715
+ }
12716
+ } else {
12717
+ lines.push("", "Cleaned: nothing found");
12718
+ }
12719
+ if (kept.length) {
12720
+ lines.push("", "Kept:");
12721
+ for (const action of kept) {
12722
+ lines.push(`- ${action.label}: ${action.path}`);
12723
+ }
12724
+ }
12725
+ if (result.notes.length) {
12726
+ lines.push("", "Notes:");
12727
+ for (const note of result.notes) {
12728
+ lines.push(`- ${note}`);
12729
+ }
12730
+ }
12731
+ return `${lines.join("\n")}
12732
+ `;
12733
+ };
12734
+ var registerUninstallCommand = (program2) => {
12735
+ program2.command("uninstall").description("Remove local CloudEval CLI installation artifacts").option("-y, --yes", "Remove without prompting for confirmation", false).option("--dry-run", "Show what would be removed without deleting files", false).option("--keep-config", "Keep ~/.config/cloudeval settings, sessions, and auth (default)", true).option("--remove-config", "Also remove ~/.config/cloudeval settings, sessions, and auth", false).option(
12736
+ "-f, --format <format>",
12737
+ "Output format: text, json, ndjson, markdown",
12738
+ "text"
12739
+ ).option("-o, --output <file>", "Output file").action(async (options) => {
12740
+ const result = await handleUninstallCommand(options);
12741
+ if (options.format === "text" || !options.format) {
12742
+ const text = formatUninstallResultText(result);
12743
+ if (options.output) {
12744
+ await fs11.writeFile(options.output, text, "utf8");
12745
+ return;
12746
+ }
12747
+ process.stdout.write(text);
12748
+ return;
12749
+ }
12750
+ await writeFormattedOutput({
12751
+ command: "uninstall",
12752
+ data: result,
12753
+ format: options.format,
12754
+ output: options.output
12755
+ });
12756
+ });
12757
+ };
12758
+
12759
+ // src/hitlPrompt.ts
12760
+ import { createInterface as createInterface4 } from "readline/promises";
12475
12761
  var HITL_REQUIRED_EXIT_CODE = 6;
12476
12762
  var recommendedOption = (question) => question.options?.find((option) => option.id === question.recommended_option_id) ?? question.options?.find((option) => option.recommended) ?? question.options?.[0];
12477
12763
  var optionLines = (question) => (question.options ?? []).map((option, index) => {
@@ -12540,7 +12826,7 @@ var answerHitlQuestions = async (questions, ask) => {
12540
12826
  return responses;
12541
12827
  };
12542
12828
  var promptForHitlResponses = async (questions, input = process.stdin, output = process.stderr) => {
12543
- const rl = createInterface3({ input, output });
12829
+ const rl = createInterface4({ input, output });
12544
12830
  try {
12545
12831
  return await answerHitlQuestions(questions, (prompt2) => rl.question(prompt2));
12546
12832
  } finally {
@@ -12598,24 +12884,24 @@ var assertNoLegacyApiKeyUsage = () => {
12598
12884
  };
12599
12885
  assertNoLegacyApiKeyUsage();
12600
12886
  var completionScriptPath = (shell) => {
12601
- const home = os4.homedir();
12887
+ const home = os5.homedir();
12602
12888
  switch (shell) {
12603
12889
  case "bash":
12604
- return path9.join(home, ".local", "share", "bash-completion", "completions", "cloudeval");
12890
+ return path10.join(home, ".local", "share", "bash-completion", "completions", "cloudeval");
12605
12891
  case "zsh":
12606
- return path9.join(home, ".zsh", "completions", "_cloudeval");
12892
+ return path10.join(home, ".zsh", "completions", "_cloudeval");
12607
12893
  case "fish":
12608
- return path9.join(home, ".config", "fish", "completions", "cloudeval.fish");
12894
+ return path10.join(home, ".config", "fish", "completions", "cloudeval.fish");
12609
12895
  case "powershell":
12610
- return path9.join(home, ".config", "powershell", "cloudeval-completion.ps1");
12896
+ return path10.join(home, ".config", "powershell", "cloudeval-completion.ps1");
12611
12897
  }
12612
12898
  };
12613
12899
  var ZSH_FPATH_MARKER = "CloudEval CLI completions";
12614
12900
  var ensureZshCompletionFpath = async () => {
12615
- const zshrc = path9.join(os4.homedir(), ".zshrc");
12901
+ const zshrc = path10.join(os5.homedir(), ".zshrc");
12616
12902
  let existing = "";
12617
12903
  try {
12618
- existing = await fs11.readFile(zshrc, "utf8");
12904
+ existing = await fs12.readFile(zshrc, "utf8");
12619
12905
  } catch {
12620
12906
  existing = "";
12621
12907
  }
@@ -12626,12 +12912,12 @@ var ensureZshCompletionFpath = async () => {
12626
12912
  # ${ZSH_FPATH_MARKER}
12627
12913
  fpath=("$HOME/.zsh/completions" $fpath)
12628
12914
  `;
12629
- await fs11.appendFile(zshrc, snippet, "utf8");
12915
+ await fs12.appendFile(zshrc, snippet, "utf8");
12630
12916
  };
12631
12917
  var installCompletionScript = async (shell, binaryName) => {
12632
12918
  const scriptPath = completionScriptPath(shell);
12633
- await fs11.mkdir(path9.dirname(scriptPath), { recursive: true });
12634
- await fs11.writeFile(scriptPath, buildCompletionScript(shell, binaryName), "utf8");
12919
+ await fs12.mkdir(path10.dirname(scriptPath), { recursive: true });
12920
+ await fs12.writeFile(scriptPath, buildCompletionScript(shell, binaryName), "utf8");
12635
12921
  if (shell === "zsh") {
12636
12922
  await ensureZshCompletionFpath();
12637
12923
  }
@@ -12639,7 +12925,7 @@ var installCompletionScript = async (shell, binaryName) => {
12639
12925
  };
12640
12926
  var uninstallCompletionScript = async (shell) => {
12641
12927
  const scriptPath = completionScriptPath(shell);
12642
- await fs11.rm(scriptPath, { force: true });
12928
+ await fs12.rm(scriptPath, { force: true });
12643
12929
  return scriptPath;
12644
12930
  };
12645
12931
  var runInteractiveLoginOnboarding = async (baseUrl, token) => {
@@ -13164,6 +13450,7 @@ registerMcpCommand(program, {
13164
13450
  resolveBaseUrl
13165
13451
  });
13166
13452
  registerUpdateCommand(program);
13453
+ registerUninstallCommand(program);
13167
13454
  program.command("__complete").description("Internal completion endpoint").argument("[words...]", "Completion words").action((words = []) => {
13168
13455
  const candidates = completeCliWords(words);
13169
13456
  for (const candidate of candidates) {
@@ -13222,7 +13509,7 @@ program.command("tui").description("Open the CloudEval Terminal UI").option(
13222
13509
  const { assertSecureBaseUrl } = await import("./dist-I4IPYCRC.js");
13223
13510
  const [{ render }, { App }] = await Promise.all([
13224
13511
  import("ink"),
13225
- import("./App-ZAC2LGZ4.js")
13512
+ import("./App-CPX5VUMP.js")
13226
13513
  ]);
13227
13514
  const baseUrl = await resolveBaseUrl(options, command);
13228
13515
  assertSecureBaseUrl(baseUrl);
@@ -13272,7 +13559,7 @@ program.command("chat").description("Start an interactive chat session").option(
13272
13559
  const { assertSecureBaseUrl } = await import("./dist-I4IPYCRC.js");
13273
13560
  const [{ render }, { App }] = await Promise.all([
13274
13561
  import("ink"),
13275
- import("./App-ZAC2LGZ4.js")
13562
+ import("./App-CPX5VUMP.js")
13276
13563
  ]);
13277
13564
  const baseUrl = await resolveBaseUrl(options, command);
13278
13565
  assertSecureBaseUrl(baseUrl);
@@ -13378,7 +13665,7 @@ program.command("ask").alias("agent").description("Ask a single question or run
13378
13665
  });
13379
13666
  }
13380
13667
  try {
13381
- const fs12 = await import("fs");
13668
+ const fs13 = await import("fs");
13382
13669
  const fsPromises = await import("fs/promises");
13383
13670
  const { randomUUID: randomUUID5 } = await import("crypto");
13384
13671
  const core = await import("./dist-I4IPYCRC.js");
@@ -13568,11 +13855,11 @@ program.command("ask").alias("agent").description("Ask a single question or run
13568
13855
  console.error(`[${commandName}] Thread ID: ${threadId}`);
13569
13856
  }
13570
13857
  if (streamTextOutput && options.output) {
13571
- fileOutputStream = fs12.createWriteStream(options.output, { encoding: "utf-8" });
13858
+ fileOutputStream = fs13.createWriteStream(options.output, { encoding: "utf-8" });
13572
13859
  outputStream = fileOutputStream;
13573
13860
  }
13574
13861
  if (ndjsonOutput && options.output) {
13575
- ndjsonOutputStream = fs12.createWriteStream(options.output, { encoding: "utf-8" });
13862
+ ndjsonOutputStream = fs13.createWriteStream(options.output, { encoding: "utf-8" });
13576
13863
  }
13577
13864
  const writeAskDataEvent = (event) => {
13578
13865
  const line = `${JSON.stringify(event)}
@@ -14006,7 +14293,7 @@ Error: ${errorMsg}
14006
14293
  program.command("banner").description("Preview the startup banner and terminal capabilities").action(async () => {
14007
14294
  const { render } = await import("ink");
14008
14295
  const BannerPreview = React.lazy(async () => ({
14009
- default: (await import("./Banner-RPE5HIBA.js")).Banner
14296
+ default: (await import("./Banner-RTNNHPNK.js")).Banner
14010
14297
  }));
14011
14298
  render(
14012
14299
  /* @__PURE__ */ jsx(React.Suspense, { fallback: null, children: /* @__PURE__ */ jsx(BannerPreview, { disable: false }) })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ganakailabs/cloudeval-cli",
3
- "version": "0.18.5",
3
+ "version": "0.19.0",
4
4
  "license": "SEE LICENSE IN LICENSE",
5
5
  "type": "module",
6
6
  "description": "CloudEval CLI for cloud architecture, cost, report, automation, and MCP workflows.",
@@ -63,7 +63,7 @@
63
63
  "run": "node dist/cli.js",
64
64
  "dev": "tsx src/cli.tsx",
65
65
  "lint": "pnpm -C ../shared build && pnpm -C ../core build && tsc --noEmit -p tsconfig.json",
66
- "test": "pnpm -C ../shared build && pnpm -C ../core build && tsx --test src/askProgress.test.ts src/hitlPrompt.test.ts src/baseUrl.test.ts src/frontendLinks.test.ts src/outputFormatter.test.ts src/localHooks.test.ts src/projectDiagramImage.test.ts src/loginOnboardingMode.test.ts src/runtime/prepareInk.test.ts src/ui/animationPolicy.test.ts src/ui/components/Banner.test.ts src/ui/components/InputBox.test.ts src/ui/components/Transcript.test.ts src/ui/billingSummary.test.ts src/ui/inputSanitizer.test.ts src/ui/inputViewport.test.ts src/ui/keyBindings.test.ts src/ui/layout.test.ts src/ui/scrollBehavior.test.ts src/ui/promptSuggestions.test.ts src/ui/commandCompletion.test.ts src/ui/interactionModel.test.ts src/ui/userDisplayName.test.ts src/ui/workspaceTabs.test.ts src/ui/workspaceDataStore.test.ts src/ui/overviewDashboard.test.ts src/ui/reportsDashboard.test.ts src/completionEngine.test.ts src/shellCompletion.test.ts src/updateCommand.test.ts src/mcpSetupCommand.test.ts src/mcpCommand.test.ts src/sessionsStore.test.ts src/recipes/catalog.test.ts src/reports/reportRender.test.ts src/reports/reportCommand.test.ts",
66
+ "test": "pnpm -C ../shared build && pnpm -C ../core build && tsx --test src/askProgress.test.ts src/hitlPrompt.test.ts src/baseUrl.test.ts src/frontendLinks.test.ts src/outputFormatter.test.ts src/localHooks.test.ts src/projectDiagramImage.test.ts src/loginOnboardingMode.test.ts src/runtime/prepareInk.test.ts src/ui/animationPolicy.test.ts src/ui/components/Banner.test.ts src/ui/components/InputBox.test.ts src/ui/components/Transcript.test.ts src/ui/billingSummary.test.ts src/ui/inputSanitizer.test.ts src/ui/inputViewport.test.ts src/ui/keyBindings.test.ts src/ui/layout.test.ts src/ui/scrollBehavior.test.ts src/ui/promptSuggestions.test.ts src/ui/commandCompletion.test.ts src/ui/interactionModel.test.ts src/ui/userDisplayName.test.ts src/ui/workspaceTabs.test.ts src/ui/workspaceDataStore.test.ts src/ui/overviewDashboard.test.ts src/ui/reportsDashboard.test.ts src/completionEngine.test.ts src/shellCompletion.test.ts src/updateCommand.test.ts src/uninstallCommand.test.ts src/mcpSetupCommand.test.ts src/mcpCommand.test.ts src/sessionsStore.test.ts src/recipes/catalog.test.ts src/reports/reportRender.test.ts src/reports/reportCommand.test.ts",
67
67
  "test:cli:noninteractive": "pnpm -C ../shared build && pnpm -C ../core build && tsx --test src/nonInteractiveCli.test.ts",
68
68
  "test:cli:noninteractive:packaged": "pnpm build:executable:current && CLOUDEVAL_CLI_BIN=./dist/bin/cloudeval pnpm test:cli:noninteractive",
69
69
  "test:cli:noninteractive:live": "pnpm build:executable:current && CLOUDEVAL_CLI_BIN=./dist/bin/cloudeval tsx --test src/liveNonInteractiveCli.test.ts",
package/sbom.spdx.json CHANGED
@@ -14,7 +14,7 @@
14
14
  {
15
15
  "SPDXID": "SPDXRef-Package-CloudEval-CLI",
16
16
  "name": "CloudEval CLI",
17
- "versionInfo": "0.18.5",
17
+ "versionInfo": "0.19.0",
18
18
  "downloadLocation": "https://github.com/ganakailabs/cloudeval-cli",
19
19
  "filesAnalyzed": false,
20
20
  "licenseConcluded": "LicenseRef-CloudEval-CLI",