@koush/chatsh 1.0.11 → 1.0.13

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 (3) hide show
  1. package/README.md +84 -0
  2. package/dist/main.js +32 -5
  3. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # @koush/chatsh
2
+
3
+ A terminal-based shell assistant that connects your shell session to an LLM for context-aware help.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ $ npx @koush/chatsh
9
+ $ ls /nonexistent
10
+ ls: /nonexistent: No such file or directory
11
+
12
+ $ help why did this command fail
13
+
14
+ The command failed because the directory /nonexistent does not exist.
15
+ The ls command lists directory contents, but it cannot find a path
16
+ that doesn't exist on your filesystem. Try ls without arguments to
17
+ see your current directory, or use ls / to list the root directory.
18
+
19
+ ```
20
+
21
+ The LLM sees your entire terminal transcript and provides context-aware assistance.
22
+
23
+ ## Configuration
24
+
25
+ Create `~/.chatsh/chatsh.jsonc`:
26
+
27
+ ```jsonc
28
+ // OpenAI
29
+ {
30
+ "provider": "openai",
31
+ "model": "gpt-4-turbo",
32
+ "options": {
33
+ "apiKey": "sk-..." // or set OPENAI_API_KEY env var
34
+ }
35
+ }
36
+
37
+ // Anthropic (Claude)
38
+ {
39
+ "provider": "anthropic",
40
+ "model": "claude-sonnet-4-5",
41
+ "options": {
42
+ "apiKey": "sk-ant-..." // or set ANTHROPIC_API_KEY env var
43
+ }
44
+ }
45
+
46
+ // Google (Gemini)
47
+ {
48
+ "provider": "google",
49
+ "model": "gemini-2.5-flash",
50
+ "options": {
51
+ "apiKey": "..." // or set GOOGLE_GENERATIVE_AI_API_KEY env var
52
+ }
53
+ }
54
+
55
+ // OpenAI-Compatible (Custom Endpoint)
56
+ {
57
+ "provider": "openai-compatible",
58
+ "model": "your-model-name",
59
+ "options": {
60
+ "name": "custom",
61
+ "baseURL": "http://localhost:8000/v1",
62
+ "apiKey": "your-api-key"
63
+ }
64
+ }
65
+ ```
66
+
67
+ ## Features
68
+
69
+ - Wraps zsh/bash/fish in a PTY session
70
+ - Multiple LLM providers (OpenAI, Anthropic, Google, OpenAI-compatible)
71
+ - Maintains full terminal history
72
+ - Resets transcript on terminal clear
73
+
74
+ ## How it works
75
+
76
+ 1. Spawns an interactive shell in a pseudo-terminal (PTY)
77
+ 2. Starts a local HTTP server on a random port
78
+ 3. Tracks all terminal output in a transcript
79
+ 4. The `help` command sends the transcript + your question to an LLM
80
+ 5. LLM response streams back to your terminal
81
+
82
+ ## License
83
+
84
+ ISC
package/dist/main.js CHANGED
@@ -4,13 +4,15 @@ import { once } from 'node:events';
4
4
  import { readFileSync, existsSync } from 'node:fs';
5
5
  import { homedir } from 'node:os';
6
6
  import { join } from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
7
8
  import * as jsonc from 'jsonc-parser';
8
9
  import { createOpenAI } from '@ai-sdk/openai';
9
10
  import { createAnthropic } from '@ai-sdk/anthropic';
10
11
  import { createGoogleGenerativeAI } from '@ai-sdk/google';
11
12
  import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
12
13
  import { streamText } from 'ai';
13
- const CONFIG_PATH = join(homedir(), '.llmsh', 'llmsh.jsonc');
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const CONFIG_PATH = join(homedir(), '.chatsh', 'chatsh.jsonc');
14
16
  function loadConfig() {
15
17
  if (!existsSync(CONFIG_PATH)) {
16
18
  return null;
@@ -34,7 +36,7 @@ function loadConfig() {
34
36
  function showConfigHelp() {
35
37
  console.error(`Error: No valid config found at ${CONFIG_PATH}
36
38
 
37
- Create a config file at ~/.llmsh/llmsh.jsonc with one of the following formats:
39
+ Create a config file at ~/.chatsh/chatsh.jsonc with one of the following formats:
38
40
 
39
41
  // OpenAI
40
42
  {
@@ -180,7 +182,13 @@ async function main() {
180
182
  cols: process.stdout.columns || 80,
181
183
  rows: process.stdout.rows || 24,
182
184
  cwd: process.cwd(),
183
- env: { ...process.env, LLMSH_PORT: String(port) }
185
+ env: {
186
+ ...process.env,
187
+ CHATSH_PORT: String(port),
188
+ CHATSH_NODE: process.execPath,
189
+ CHATSH_SCRIPT: __filename,
190
+ CHATSH_TS: __filename.endsWith('.ts') ? '--experimental-strip-types' : ''
191
+ }
184
192
  });
185
193
  process.stdin.setRawMode(true);
186
194
  process.stdin.resume();
@@ -200,7 +208,7 @@ async function main() {
200
208
  }
201
209
  process.stdout.write(data);
202
210
  });
203
- ptyProcess.write('help() { curl -s -X POST -d "$*" http://localhost:$LLMSH_PORT }\n');
211
+ ptyProcess.write('help() { "$CHATSH_NODE" --no-warnings $CHATSH_TS "$CHATSH_SCRIPT" --help "$*"; }\n');
204
212
  if (shell.includes('zsh')) {
205
213
  ptyProcess.write('bindkey "^R" history-incremental-search-backward\n');
206
214
  }
@@ -214,4 +222,23 @@ async function main() {
214
222
  ptyProcess.resize(process.stdout.columns || 80, process.stdout.rows || 24);
215
223
  });
216
224
  }
217
- main();
225
+ // Handle --help flag for shell help command
226
+ if (process.argv[2] === '--help') {
227
+ const query = process.argv.slice(3).join(' ');
228
+ const port = process.env.CHATSH_PORT;
229
+ if (!port) {
230
+ console.error('CHATSH_PORT not set');
231
+ process.exit(1);
232
+ }
233
+ const response = await fetch(`http://localhost:${port}`, {
234
+ method: 'POST',
235
+ body: query
236
+ });
237
+ for await (const chunk of response.body) {
238
+ process.stdout.write(chunk);
239
+ }
240
+ process.exit(0);
241
+ }
242
+ else {
243
+ main();
244
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@koush/chatsh",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
4
4
  "type": "module",
5
5
  "main": "dist/main.js",
6
6
  "bin": {