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 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
- ### Local Configuration File
124
+ ### Global Configuration File
106
125
 
107
- Create `.goatchain/config.json` in your project:
126
+ The CLI automatically creates `~/.goatchain/config.json` when you use `--save-config`:
108
127
 
109
128
  ```json
110
129
  {
111
- "model": {
112
- "provider": "openai",
113
- "modelId": "gpt-4o"
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 `.goatchain/sessions/` (gitignored).
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 { main } from './index.js'
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
- await main(process.argv.slice(2))
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};