caroushell 0.1.2 → 0.1.4

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
@@ -10,22 +10,26 @@ history, and AI suggestions as you type.
10
10
  - Go up and down the carousel with arrow keys.
11
11
  - Press `Enter` to run the highlighted command.
12
12
  - Logs activity under `~/.caroushell/logs` for easy troubleshooting.
13
- - Extensible config file (`~/.caroushell/config.json`) so you can point the CLI
13
+ - Extensible config file (`~/.caroushell/config.toml`) so you can point the CLI
14
14
  at different AI providers.
15
15
 
16
16
  ## Requirements
17
17
 
18
18
  - Node.js 18 or newer.
19
19
  - On first launch Caroushell will prompt you for an OpenAI-compatible endpoint
20
- URL, API key, and model name, then store them in `~/.caroushell/config.json`.
20
+ URL, API key, and model name, then store them in `~/.caroushell/config.toml`.
21
21
  - You can also create the file manually:
22
22
 
23
- ```json
24
- {
25
- "apiUrl": "https://openrouter.ai/api/v1",
26
- "apiKey": "your-api-key",
27
- "model": "gpt-4o-mini"
28
- }
23
+ ```toml
24
+ apiUrl = "https://openrouter.ai/api/v1"
25
+ apiKey = "your-api-key"
26
+ model = "gpt-4o-mini"
27
+ ```
28
+
29
+ or
30
+
31
+ ```toml
32
+ GEMINI_API_KEY = "AIzaSyD...N-wK"
29
33
  ```
30
34
 
31
35
  Any endpoint that implements the OpenAI Chat Completions API (OpenRouter,
@@ -60,7 +64,7 @@ Caroushell opens an interactive prompt:
60
64
 
61
65
  Logs are written to `~/.caroushell/logs/MM-DD.txt`. Inspect these files if you
62
66
  need to debug AI suggestions or the terminal renderer. Configuration lives at
63
- `~/.caroushell/config.json` (override via `CAROUSHELL_CONFIG_PATH`).
67
+ `~/.caroushell/config.toml` (override via `CAROUSHELL_CONFIG_PATH`).
64
68
 
65
69
  ## Development
66
70
 
@@ -109,14 +109,6 @@ class AISuggester {
109
109
  this.apiUrl =
110
110
  this.apiUrl || config.apiUrl || process.env.CAROUSHELL_API_URL;
111
111
  this.model = this.model || config.model || process.env.CAROUSHELL_MODEL;
112
- // If the user provided only a Gemini key, default the URL/model accordingly.
113
- if (!this.apiUrl && config.GEMINI_API_KEY) {
114
- this.apiUrl =
115
- "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-lite:generateContent";
116
- }
117
- if (!this.model && config.GEMINI_API_KEY) {
118
- this.model = "gemini-2.5-flash-lite";
119
- }
120
112
  }
121
113
  descriptionForAi() {
122
114
  return "";
package/dist/app.js CHANGED
@@ -151,8 +151,8 @@ class App {
151
151
  this.terminal.write("\n");
152
152
  this.keyboard.pause();
153
153
  try {
154
- const success = await (0, spawner_1.runUserCommand)(cmd);
155
- if (success) {
154
+ const storeInHistory = await (0, spawner_1.runUserCommand)(cmd);
155
+ if (storeInHistory) {
156
156
  await this.history.add(cmd);
157
157
  }
158
158
  }
package/dist/config.js CHANGED
@@ -32,6 +32,9 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
35
38
  Object.defineProperty(exports, "__esModule", { value: true });
36
39
  exports.configFolder = configFolder;
37
40
  exports.getConfigPath = getConfigPath;
@@ -40,7 +43,8 @@ exports.getConfig = getConfig;
40
43
  const fs_1 = require("fs");
41
44
  const path = __importStar(require("path"));
42
45
  const os = __importStar(require("os"));
43
- const GEMINI_DEFAULT_API_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-lite:generateContent";
46
+ const toml_1 = __importDefault(require("toml"));
47
+ const GEMINI_DEFAULT_API_URL = "https://generativelanguage.googleapis.com/v1beta/openai";
44
48
  const GEMINI_DEFAULT_MODEL = "gemini-2.5-flash-lite";
45
49
  function configFolder(subpath) {
46
50
  const home = os.homedir();
@@ -48,19 +52,27 @@ function configFolder(subpath) {
48
52
  return path.join(home, ".caroushell", subpath);
49
53
  }
50
54
  function getConfigPath() {
51
- return process.env.CAROUSHELL_CONFIG_PATH || configFolder("config.json");
55
+ return process.env.CAROUSHELL_CONFIG_PATH || configFolder("config.toml");
52
56
  }
53
57
  async function readConfigFile() {
54
58
  const configPath = getConfigPath();
55
59
  const raw = await fs_1.promises.readFile(configPath, "utf8");
56
- return JSON.parse(raw);
60
+ if (!raw.trim()) {
61
+ return {};
62
+ }
63
+ try {
64
+ return toml_1.default.parse(raw);
65
+ }
66
+ catch (err) {
67
+ throw new Error(`Error parsing config file at ${configPath}: ${err}`);
68
+ }
57
69
  }
58
70
  async function doesConfigExist() {
59
71
  const configPath = getConfigPath();
60
72
  try {
61
73
  await fs_1.promises.access(configPath);
62
74
  const raw = await readConfigFile();
63
- if (!raw)
75
+ if (!raw || Object.keys(raw).length === 0)
64
76
  return false;
65
77
  return true;
66
78
  }
@@ -68,7 +80,7 @@ async function doesConfigExist() {
68
80
  if (err?.code === "ENOENT") {
69
81
  return false;
70
82
  }
71
- // detect empty json file syntax error
83
+ // Treat invalid TOML as missing so we can reprompt the user.
72
84
  if (err instanceof SyntaxError) {
73
85
  return false;
74
86
  }
@@ -113,7 +113,10 @@ async function runHelloNewUserFlow(configPath) {
113
113
  apiKey,
114
114
  model,
115
115
  };
116
- await fs_1.promises.writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf8");
116
+ const tomlBody = Object.entries(config)
117
+ .map(([key, value]) => `${key} = ${JSON.stringify(value)}`)
118
+ .join("\n");
119
+ await fs_1.promises.writeFile(configPath, tomlBody + "\n", "utf8");
117
120
  console.log(`\nSaved config to ${configPath}`);
118
121
  console.log("You can edit this file later if you want to switch providers.\n");
119
122
  return config;
package/dist/main.js CHANGED
@@ -15,6 +15,7 @@ async function main() {
15
15
  await app.run();
16
16
  }
17
17
  main().catch((err) => {
18
+ console.error("Caroushell uncaught error:");
18
19
  console.error(err);
19
20
  process.exit(1);
20
21
  });
package/dist/spawner.js CHANGED
@@ -61,5 +61,7 @@ async function runUserCommand(command) {
61
61
  proc.on("error", reject);
62
62
  proc.on("close", () => resolve());
63
63
  });
64
- return proc.exitCode === 0;
64
+ // Why save failed commands? Well eg sometimes we want to run a test
65
+ // many times until we fix it.
66
+ return true;
65
67
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "caroushell",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Terminal carousel that suggests commands from history, config, and AI.",
5
5
  "type": "commonjs",
6
6
  "main": "dist/main.js",
@@ -46,5 +46,8 @@
46
46
  "eslint": "^9.39.1",
47
47
  "tsx": "^4.19.2",
48
48
  "typescript": "^5.6.3"
49
+ },
50
+ "dependencies": {
51
+ "toml": "^3.0.0"
49
52
  }
50
53
  }