goatchain-cli 0.0.2 → 0.0.3-beta.10
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 +93 -9
- package/dist/cli.mjs +112 -2
- package/dist/constants-C3H5Y2lA.js +2 -0
- package/dist/index.js +21 -19
- package/dist/package.json +44 -0
- package/dist/repl-Bqi7c_eT.js +80 -0
- package/dist/runner.mjs +4 -0
- package/dist/src-B_9q8eah.js +1 -0
- package/dist/{src-BkCVu6Lk.js → src-KHMs2spV.js} +113 -23
- package/dist/theme-BSlrp23z.js +1 -0
- package/dist/theme-CDmxdKhU.js +1 -0
- package/dist/{ui-B9ajujL6.js → ui-CR3huLeA.js} +1 -1
- package/dist/{ui-D4n45LUU.js → ui-h27qws5j.js} +2 -2
- package/package.json +2 -2
- package/dist/args-B4NUWmzz.js +0 -2
- package/dist/repl-lWj9AUT_.js +0 -76
- package/dist/src-Bcu1Lib4.js +0 -1
- package/dist/theme-4Sto2Osm.js +0 -1
- package/dist/theme-DvHvtqNs.js +0 -1
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
- **💾 Session Management** - Save and restore conversation sessions
|
|
16
16
|
- **🔧 Tool Integration** - Built-in tools for common development tasks
|
|
17
17
|
- **🖼️ Image Support** - Attach and analyze images in conversations
|
|
18
|
+
- **🎨 Beautiful UI Themes** - Multiple modern themes (neon, ocean, gradient, and more)
|
|
18
19
|
- **⚙️ Configurable** - Flexible configuration via environment and config files
|
|
19
20
|
|
|
20
21
|
## 🚀 Quick Start
|
|
@@ -83,6 +84,24 @@ npx github:zjywill/GoatChain#main:packages/cli
|
|
|
83
84
|
|
|
84
85
|
## ⚙️ Configuration
|
|
85
86
|
|
|
87
|
+
### Quick Setup with --save-config
|
|
88
|
+
|
|
89
|
+
Save your configuration once and reuse it across sessions:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# Save configuration with DeepSeek API
|
|
93
|
+
npx goatchain-cli \
|
|
94
|
+
--model deepseek-v3.1 \
|
|
95
|
+
--api-key sk-your-key \
|
|
96
|
+
--base-url https://api.deepseek.com/v1 \
|
|
97
|
+
--save-config
|
|
98
|
+
|
|
99
|
+
# Now run without any arguments
|
|
100
|
+
npx goatchain-cli
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The `--save-config` flag saves your settings to `~/.goatchain/config.json` (global configuration) and exits without starting the CLI.
|
|
104
|
+
|
|
86
105
|
### Environment Variables
|
|
87
106
|
|
|
88
107
|
```bash
|
|
@@ -102,26 +121,70 @@ export GOATCHAIN_OPENAI_BASE_URL=...
|
|
|
102
121
|
export GOATCHAIN_PLAN_MODE=1
|
|
103
122
|
```
|
|
104
123
|
|
|
105
|
-
###
|
|
124
|
+
### Global Configuration File
|
|
106
125
|
|
|
107
|
-
|
|
126
|
+
The CLI automatically creates `~/.goatchain/config.json` when you use `--save-config`:
|
|
108
127
|
|
|
109
128
|
```json
|
|
110
129
|
{
|
|
111
|
-
"
|
|
112
|
-
|
|
113
|
-
"
|
|
130
|
+
"schemaVersion": 1,
|
|
131
|
+
"openai": {
|
|
132
|
+
"apiKey": "sk-...",
|
|
133
|
+
"baseUrl": "https://api.deepseek.com/v1"
|
|
134
|
+
},
|
|
135
|
+
"defaults": {
|
|
136
|
+
"modelId": "deepseek-v3.1",
|
|
137
|
+
"requestDefaults": {
|
|
138
|
+
"temperature": 0.7,
|
|
139
|
+
"maxOutputTokens": 2000
|
|
140
|
+
}
|
|
114
141
|
},
|
|
115
142
|
"tools": {
|
|
116
143
|
"webSearch": {
|
|
117
144
|
"apiKey": "your-serper-key",
|
|
118
145
|
"numResults": 10
|
|
119
146
|
}
|
|
120
|
-
}
|
|
121
|
-
"planMode": "on"
|
|
147
|
+
}
|
|
122
148
|
}
|
|
123
149
|
```
|
|
124
150
|
|
|
151
|
+
**See [CLI Configuration Guide](../../docs/cli-configuration.md) for detailed configuration options.**
|
|
152
|
+
|
|
153
|
+
## 🎨 UI Themes
|
|
154
|
+
|
|
155
|
+
GoatChain CLI now comes with beautiful modern themes! Choose from:
|
|
156
|
+
|
|
157
|
+
- **dark** - Classic dark theme (default)
|
|
158
|
+
- **neon** - Cyberpunk neon style 🌃
|
|
159
|
+
- **ocean** - Deep sea blue-green theme 🌊
|
|
160
|
+
- **gradient** - Rainbow gradient theme 🎨
|
|
161
|
+
- **light** - Light theme for bright terminals ☀️
|
|
162
|
+
- **mono** - Minimalist monochrome ⬛
|
|
163
|
+
|
|
164
|
+
### Using Themes
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# Start with a theme
|
|
168
|
+
goatchain --theme neon
|
|
169
|
+
|
|
170
|
+
# Or set in config
|
|
171
|
+
/config theme ocean
|
|
172
|
+
|
|
173
|
+
# Or save in global config
|
|
174
|
+
goatchain --theme gradient --save-config
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Visual Features
|
|
178
|
+
|
|
179
|
+
- 🎯 **Dynamic spinners** - Different animations for thinking, tool calls, and responding
|
|
180
|
+
- 📦 **Modern box drawing** - Beautiful Unicode borders and separators
|
|
181
|
+
- 🔧 **Enhanced tool display** - Inline or box mode with rich icons
|
|
182
|
+
- 📋 **Beautiful TODO lists** - Clear task status with symbols (○ ▸ ✓)
|
|
183
|
+
- 📝 **File change tracking** - Pretty git-style diff summaries
|
|
184
|
+
- ✨ **Rich status indicators** - Colorful, animated status messages
|
|
185
|
+
|
|
186
|
+
**See [UI Themes Guide](../../docs/cli-ui-themes.md) for detailed documentation and customization options.**
|
|
187
|
+
|
|
125
188
|
## 🎯 Commands Reference
|
|
126
189
|
|
|
127
190
|
### Core Commands
|
|
@@ -178,6 +241,7 @@ Examples of model ids you can select via `/model`:
|
|
|
178
241
|
- **WebSearch** - Internet search for current information
|
|
179
242
|
- **AskUserQuestion** - Interactive user prompts for decisions
|
|
180
243
|
- **TodoWrite** - Structured task planning
|
|
244
|
+
- **TodoPlan** - Plan-only task drafts (plan mode)
|
|
181
245
|
- **Bash** - Execute shell commands
|
|
182
246
|
|
|
183
247
|
### Code Analysis
|
|
@@ -196,7 +260,7 @@ Plan mode enables a structured workflow for complex tasks:
|
|
|
196
260
|
# Workflow:
|
|
197
261
|
# 1. Agent explores codebase and researches request
|
|
198
262
|
# 2. May ask clarifying questions
|
|
199
|
-
# 3. Creates structured plan (3-8 steps)
|
|
263
|
+
# 3. Creates structured plan (3-8 steps) using TodoPlan
|
|
200
264
|
# 4. You review and approve plan
|
|
201
265
|
# 5. Agent executes approved plan
|
|
202
266
|
|
|
@@ -221,7 +285,7 @@ Sessions preserve conversation history and context:
|
|
|
221
285
|
/new
|
|
222
286
|
```
|
|
223
287
|
|
|
224
|
-
Sessions are stored in
|
|
288
|
+
Sessions are stored in `~/.goatchain/session/<workspace-name>/sessions/` and are isolated per project.
|
|
225
289
|
|
|
226
290
|
## 🔍 Web Search Integration
|
|
227
291
|
|
|
@@ -262,8 +326,28 @@ goatchain --base-url https://api.openai.com/v1
|
|
|
262
326
|
|
|
263
327
|
# Configure request parameters
|
|
264
328
|
goatchain --max-tokens 2000 --temperature 0.7
|
|
329
|
+
|
|
330
|
+
# Save configuration for future use
|
|
331
|
+
goatchain --model deepseek-v3.1 \
|
|
332
|
+
--api-key sk-your-key \
|
|
333
|
+
--base-url https://next-api.fazhiplus.com/v1 \
|
|
334
|
+
--save-config
|
|
335
|
+
|
|
336
|
+
# Auto-approve tool executions (for one-shot commands)
|
|
337
|
+
goatchain --auto-approval "read the README.md and summarize it"
|
|
265
338
|
```
|
|
266
339
|
|
|
340
|
+
### Configuration Priority
|
|
341
|
+
|
|
342
|
+
Settings are resolved in the following order (highest to lowest):
|
|
343
|
+
|
|
344
|
+
1. **Command-line arguments** - `--model`, `--api-key`, etc.
|
|
345
|
+
2. **Environment variables** - `GOATCHAIN_MODEL`, `OPENAI_API_KEY`, etc.
|
|
346
|
+
3. **Configuration file** - `~/.goatchain/config.json` (global)
|
|
347
|
+
4. **Default values**
|
|
348
|
+
|
|
349
|
+
When you pass CLI arguments (with or without `--save-config`), they automatically update the global configuration file for future sessions.
|
|
350
|
+
|
|
267
351
|
### Integration with Projects
|
|
268
352
|
|
|
269
353
|
```bash
|
package/dist/cli.mjs
CHANGED
|
@@ -1,4 +1,114 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { spawn } from 'node:child_process'
|
|
3
|
+
import os from 'node:os'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
import { fileURLToPath } from 'node:url'
|
|
3
6
|
|
|
4
|
-
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
8
|
+
const __dirname = path.dirname(__filename)
|
|
9
|
+
|
|
10
|
+
const runnerPath = path.join(__dirname, 'runner.mjs')
|
|
11
|
+
|
|
12
|
+
function restoreTerminal() {
|
|
13
|
+
try {
|
|
14
|
+
if (process.stdin.isTTY && typeof process.stdin.setRawMode === 'function')
|
|
15
|
+
process.stdin.setRawMode(false)
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
// ignore
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!process.stdout.isTTY)
|
|
22
|
+
return
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
// Best-effort terminal reset for interactive sessions:
|
|
26
|
+
// - show cursor, reset attributes
|
|
27
|
+
// - disable bracketed paste + mouse reporting
|
|
28
|
+
// - exit alt-screen
|
|
29
|
+
// - reset keypad/cursor application modes
|
|
30
|
+
process.stdout.write(
|
|
31
|
+
'\u001B[?25h\u001B[0m\u001B[?2004l\u001B[?1006l\u001B[?1002l\u001B[?1000l\u001B[?1049l\u001B[?1l\u001B>'
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// ignore
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const child = spawn(
|
|
40
|
+
process.execPath,
|
|
41
|
+
[runnerPath, ...process.argv.slice(2)],
|
|
42
|
+
{
|
|
43
|
+
stdio: 'inherit',
|
|
44
|
+
env: process.env,
|
|
45
|
+
},
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
// Debug-only: simulate hard crashes (OOM/abort) by killing the child after a delay.
|
|
49
|
+
// Usage:
|
|
50
|
+
// GOATCHAIN_DEBUG_KILL_CHILD_AFTER_MS=300 GOATCHAIN_DEBUG_KILL_CHILD_SIGNAL=SIGABRT node dist/cli.mjs
|
|
51
|
+
const killAfterRaw = process.env.GOATCHAIN_DEBUG_KILL_CHILD_AFTER_MS
|
|
52
|
+
const killAfterMs = typeof killAfterRaw === 'string' && killAfterRaw.trim()
|
|
53
|
+
? Number.parseInt(killAfterRaw.trim(), 10)
|
|
54
|
+
: Number.NaN
|
|
55
|
+
const killSignal = (typeof process.env.GOATCHAIN_DEBUG_KILL_CHILD_SIGNAL === 'string'
|
|
56
|
+
&& process.env.GOATCHAIN_DEBUG_KILL_CHILD_SIGNAL.trim())
|
|
57
|
+
? process.env.GOATCHAIN_DEBUG_KILL_CHILD_SIGNAL.trim()
|
|
58
|
+
: 'SIGABRT'
|
|
59
|
+
|
|
60
|
+
if (Number.isFinite(killAfterMs) && killAfterMs > 0) {
|
|
61
|
+
const t = setTimeout(() => {
|
|
62
|
+
try {
|
|
63
|
+
child.kill(killSignal)
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
try {
|
|
67
|
+
child.kill('SIGKILL')
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// ignore
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}, killAfterMs)
|
|
74
|
+
t.unref?.()
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const forwardSignal = (sig) => {
|
|
78
|
+
try {
|
|
79
|
+
child.kill(sig)
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// ignore
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
process.on('SIGINT', () => forwardSignal('SIGINT'))
|
|
87
|
+
process.on('SIGTERM', () => forwardSignal('SIGTERM'))
|
|
88
|
+
process.on('SIGHUP', () => forwardSignal('SIGHUP'))
|
|
89
|
+
|
|
90
|
+
child.on('error', (err) => {
|
|
91
|
+
restoreTerminal()
|
|
92
|
+
try {
|
|
93
|
+
process.stderr.write(String(err?.stack || err?.message || err) + '\n')
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// ignore
|
|
97
|
+
}
|
|
98
|
+
process.exit(1)
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
child.on('exit', (code, signal) => {
|
|
102
|
+
restoreTerminal()
|
|
103
|
+
if (typeof code === 'number')
|
|
104
|
+
process.exit(code)
|
|
105
|
+
|
|
106
|
+
if (typeof signal === 'string' && signal) {
|
|
107
|
+
const sigNum = os.constants?.signals?.[signal]
|
|
108
|
+
if (typeof sigNum === 'number' && Number.isFinite(sigNum))
|
|
109
|
+
process.exit(128 + sigNum)
|
|
110
|
+
process.exit(1)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
process.exit(1)
|
|
114
|
+
})
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{Command as e,InvalidArgumentError as t}from"commander";function n(e){let t=String(e??``).trim().toLowerCase().replace(/[-\s]+/g,`_`);return t===`read`||t===`readonly`||t===`read_only`?`read_only`:t===`agent`||t===`default`?`agent`:t===`full`||t===`full_access`||t===`agent_full`||t===`agent_full_access`?`full_access`:`read_only`}function r(e){return e===`read_only`?`Read Only`:e===`agent`?`Agent (current)`:`Agent (full access)`}function i(e){return{strategy:`high_risk`,autoApprove:e.envAutoApprove===`1`||e.envAutoApprove===!0||e.mode!==`read_only`}}function a(e){return{allowOutsideWorkspace:e===`full_access`,allowNetwork:e===`full_access`||e===`read_only`}}function o(){return[``,`Interactive slash commands:`,` /help Show help`,` /clear Clear pending attachments`,` /approvals Select approval mode (interactive)`,` /model [id] Show/set model id`,` /workspace Switch workspace root (interactive)`,` /files Attach file(s) (interactive)`,` /images Attach image(s) (interactive)`,` /settings Temperature / Token limits / Base URL / API key`,` /config [--json] Print full config (secrets redacted)`,` /output Output & UX options`,` /agent Switch to agent mode (normal operation)`,` /plan Switch to plan mode (read-only planning with TodoPlan)`,` /diag Show last turn stop reason + tool calls`,` /diagnostics Alias for /diag`,` /edit Compose a message in $EDITOR`,` /multiline Start multiline input (.send/.cancel)`,` /set <k> <v> Set request param (maxTokens/temperature/...)`,` /unset <k> Clear request param`,` /params Show current request params`,` /base-url <url> Set base URL (recreates client if needed)`,` /api-key <key> Set API key for this process (not printed)`,` /web-search-key <key> Set Serper API key for WebSearch tool (not printed)`,` /tools List enabled tools`,` /mcp [subcmd] MCP servers (connect/disconnect/tools)`,` /sessions List/pick/delete sessions`,` /use <sessionId> Switch session (restore history)`,` /rm <sessionId...> Delete session(s)`,` /save Save current session/config`,` /status Show current session/model info`,` /new Start a new conversation (clears history)`,` /compact Manually compress context (generate summary)`,` /exit Exit`,``,`Mentions:`,` @<query> Search workspace files/dirs (Enter inserts @path)`,``,`Keybindings:`,` (Type @ or / to show suggestions; filters as you type)`,` Tab Fill the selected suggestion`,` ↑/↓ Select a suggestion`,` Enter Run /command, or insert @workspace path selection`,` PgUp/PgDn/Home/End Scroll fullscreen log`,` Ctrl+K Open command palette`,` Ctrl+O Manage attachments`,` Ctrl+G Clear attachments`,``,`UI:`,` GOATCHAIN_UI=inline Disable fullscreen UI`,` GOATCHAIN_UI=legacy Disable interactive UI`,` GOATCHAIN_UI=full Force fullscreen UI`,` GOATCHAIN_THEME=dark Dark terminal theme (default)`,` GOATCHAIN_THEME=light Light terminal theme`,` GOATCHAIN_ANIMATE=0 Disable splash animation`,``,`Approvals:`,` GOATCHAIN_APPROVAL_MODE=read_only|agent|full_access`,` (You can also change this in-app via /approvals)`,``,`Image attachments:`,` (Also configurable via .goatchain/config.json -> ui.attachImages.*)`,` GOATCHAIN_ATTACH_IMAGES_NOTE_TEMPLATE=<text> Template supports {{items}}/{{count}}/{{json}}`,` GOATCHAIN_ATTACH_IMAGES_NOTE_POSITION=start|before_images|after_images|before_text|after_text|end`,` GOATCHAIN_ATTACH_IMAGES_IMAGES_POSITION=start|before_text|after_text|end`,` GOATCHAIN_ATTACH_IMAGES_DATA_URL_MODE=none|prefix|full`,` GOATCHAIN_ATTACH_IMAGES_MAX_DATA_URL_CHARS=8000`,``].join(`
|
|
2
|
+
`)}function s(e){return n=>{let r=Number(n);if(!Number.isFinite(r))throw new t(`Invalid ${e}: ${n}`);return r}}function c(e){return n=>{let r=Number.parseInt(n,10);if(!Number.isFinite(r))throw new t(`Invalid ${e}: ${n}`);return r}}function l(t){return new e().name(`goatchain`).description(`A tiny interactive chat CLI built on agent-loop`).argument(`[prompt...]`,`one-shot prompt (omit to start interactive mode)`).option(`-k, --api-key <key>`,`OpenAI API key (or set OPENAI_API_KEY)`).option(`-m, --model <id>`,`Default model id (OpenAI)`).option(`--system <prompt>`,`System prompt`).option(`--base-url <url>`,`Override OpenAI base URL (proxy)`).option(`--save-config`,`Save config and exit (use with --api-key, --model, --base-url, etc.)`).option(`--auto-approval`,`Auto-approve all tool executions (for one-shot prompts)`).option(`--serper-api-key <key>`,`Serper API key (enables WebSearch tool)`).option(`--web-search-api-key <key>`,`Alias for --serper-api-key`).option(`--serper-api-endpoint <url>`,`Serper API endpoint (WebSearch tool)`).option(`--web-search-api-endpoint <url>`,`Alias for --serper-api-endpoint`).option(`--serper-num-results <n>`,`Serper number of results (WebSearch tool)`,c(`serper-num-results`)).option(`--web-search-num-results <n>`,`Alias for --serper-num-results`,c(`web-search-num-results`)).option(`--max-tokens <n>`,`Max completion tokens`,c(`max-tokens`)).option(`--temperature <n>`,`Temperature (0-2)`,s(`temperature`)).option(`--top-p <n>`,`Top-p (0-1)`,s(`top-p`)).option(`--presence-penalty <n>`,`Presence penalty (-2..2)`,s(`presence-penalty`)).option(`--frequency-penalty <n>`,`Frequency penalty (-2..2)`,s(`frequency-penalty`)).option(`--seed <n>`,`Random seed`,c(`seed`)).option(`--timeout-ms <n>`,`Request timeout in ms`,c(`timeout-ms`)).option(`--session <id>`,`Use a saved session id (optional)`).version(t,`-v, --version`,`Show version`).helpOption(`-h, --help`,`Show help`).showHelpAfterError().showSuggestionAfterError().addHelpText(`after`,o())}function u(e,t){let n=l(t);n.parse(e,{from:`user`});let r=n.opts(),i=Array.isArray(n.args)?n.args.join(` `).trim():``,a=e=>typeof e==`number`&&Number.isFinite(e)?e:void 0,o=e=>typeof e==`number`&&Number.isFinite(e)?Math.trunc(e):void 0;return{apiKey:typeof r.apiKey==`string`&&r.apiKey.trim()?r.apiKey:void 0,modelId:typeof r.model==`string`&&r.model.trim()?r.model:void 0,system:typeof r.system==`string`&&r.system.trim()?r.system:void 0,baseUrl:typeof r.baseUrl==`string`&&r.baseUrl.trim()?r.baseUrl:void 0,saveConfigOnly:!!r.saveConfig,autoApproval:!!r.autoApproval,webSearchApiKey:typeof r.webSearchApiKey==`string`&&r.webSearchApiKey.trim()?r.webSearchApiKey:typeof r.serperApiKey==`string`&&r.serperApiKey.trim()?r.serperApiKey:void 0,webSearchApiEndpoint:typeof r.webSearchApiEndpoint==`string`&&r.webSearchApiEndpoint.trim()?r.webSearchApiEndpoint:typeof r.serperApiEndpoint==`string`&&r.serperApiEndpoint.trim()?r.serperApiEndpoint:void 0,webSearchNumResults:typeof r.webSearchNumResults==`number`&&Number.isFinite(r.webSearchNumResults)?Math.trunc(r.webSearchNumResults):o(r.serperNumResults),maxTokens:o(r.maxTokens),temperature:a(r.temperature),topP:a(r.topP),presencePenalty:a(r.presencePenalty),frequencyPenalty:a(r.frequencyPenalty),seed:o(r.seed),timeoutMs:o(r.timeoutMs),sessionId:typeof r.session==`string`&&r.session.trim()?r.session:void 0,prompt:i}}const d=1e4;export{r as a,n as c,u as i,l as n,a as o,o as r,i as s,d as t};
|