dgxay-code-cli 1.0.0 → 1.1.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-zh.md +50 -8
- package/README.md +50 -9
- package/package.json +1 -1
- package/src/cli/ansi.js +38 -8
- package/src/cli/approval.js +11 -11
- package/src/cli/editor.js +456 -202
- package/src/cli/i18n.js +276 -0
- package/src/cli/index.js +34 -34
- package/src/cli/print.js +5 -5
- package/src/cli/render.js +147 -76
- package/src/cli/repl.js +205 -141
- package/src/cli/slash.js +29 -28
- package/src/cli/ui.js +8 -8
- package/src/commands/chat.js +45 -45
- package/src/commands/config.js +81 -81
- package/src/commands/context.js +5 -5
- package/src/commands/doctor.js +34 -34
- package/src/commands/export.js +13 -13
- package/src/commands/mcp.js +51 -51
- package/src/commands/model.js +38 -38
- package/src/commands/oauth.js +61 -61
- package/src/commands/permission.js +19 -19
- package/src/commands/provider.js +70 -70
- package/src/commands/run.js +20 -20
- package/src/commands/skill.js +15 -15
- package/src/commands/workspace.js +18 -18
- package/src/core/agent.js +19 -18
- package/src/core/llm.js +10 -10
- package/src/core/mcp.js +5 -5
- package/src/core/oauth.js +36 -36
- package/src/core/prompt.js +46 -46
- package/src/core/sessions.js +7 -7
- package/src/core/tools.js +24 -24
- package/src/services/input.js +1 -1
- package/src/services/settings.js +28 -28
- package/src/services/skills.js +1 -1
- package/src/services/terminal.js +3 -3
package/README-zh.md
CHANGED
|
@@ -66,17 +66,48 @@ dgxay run -p "总结这个仓库的架构" -o json | jq -r .text
|
|
|
66
66
|
|
|
67
67
|
## 交互模式
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
进入 `dgxay` 后,界面由三部分组成:
|
|
70
|
+
|
|
71
|
+
- **输入框**:一个带边框的方框,宽度随终端自适应,长文本自动折行
|
|
72
|
+
- **命令菜单**:输入 `/` 时在输入框上方/下方弹出,可上下选择
|
|
73
|
+
- **思考区**:模型思考时实时显示计时与思考内容
|
|
70
74
|
|
|
71
75
|
| 输入 | 作用 |
|
|
72
76
|
| --- | --- |
|
|
73
77
|
| 直接输入文字 | 发送给模型 |
|
|
78
|
+
| `/` | 弹出命令菜单 |
|
|
79
|
+
| `↑` / `↓` | 菜单打开时选择命令;菜单关闭时 `↑` 回填上一条提问 |
|
|
80
|
+
| `Tab` | 补全当前选中的命令 |
|
|
81
|
+
| `Enter` | 菜单打开时执行选中命令;否则发送消息 |
|
|
82
|
+
| `Esc` | 关闭菜单 |
|
|
74
83
|
| `!命令` | 执行终端命令,输出进入对话上下文,例如 `!git status` |
|
|
75
|
-
| `/` | 打开斜杠命令补全 |
|
|
76
84
|
| 行尾 `\` | 续行,输入多行消息 |
|
|
77
85
|
| `Ctrl+C` | 生成中中断;空闲时退出;输入框有内容时清空 |
|
|
78
86
|
| `Ctrl+D` | 退出 |
|
|
79
|
-
|
|
87
|
+
|
|
88
|
+
### 语言
|
|
89
|
+
|
|
90
|
+
界面默认英文。想切回中文:`DGXAY_LANG=zh dgxay`。
|
|
91
|
+
AI 的回复语言跟随你的提问语言。
|
|
92
|
+
|
|
93
|
+
### 思考过程
|
|
94
|
+
|
|
95
|
+
模型返回思考内容时,终端会实时显示:
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
⠿ Thinking 2.4s 当前正在考虑的部分…
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
思考结束后折叠为一行摘要,并把完整思考内容以灰色缩进段落列出:
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
⌁ Thought for 12.8s · 1450 chars
|
|
105
|
+
│ 先看一下目录结构……
|
|
106
|
+
│ 然后读入口文件……
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
想只看摘要不显示详细内容:`DGXAY_SHOW_REASONING=0`。
|
|
110
|
+
|
|
80
111
|
|
|
81
112
|
### 斜杠命令
|
|
82
113
|
|
|
@@ -155,7 +186,11 @@ dgxay doctor 自检
|
|
|
155
186
|
| `DGXAY_HOME` | 覆盖数据目录 |
|
|
156
187
|
| `DGXAY_API_KEY` | `config init` 时用作 API Key |
|
|
157
188
|
| `DGXAY_TOKEN_KEY` | 给 OAuth 令牌加解密口令(不设置则按明文 base64 保存) |
|
|
158
|
-
| `
|
|
189
|
+
| `DGXAY_LANG=zh` | 界面切成中文(默认英文) |
|
|
190
|
+
| `DGXAY_SHOW_REASONING=0` | 只显示思考摘要,不列出思考内容 |
|
|
191
|
+
| `DGXAY_REASONING_LINES=6` | 思考内容最多显示几行 |
|
|
192
|
+
| `DGXAY_NO_CURSOR_UP=1` | 强制降级为单行输入框 |
|
|
193
|
+
| `DGXAY_ASCII=1` | 强制用 ASCII 框线(+ - |) |
|
|
159
194
|
| `DGXAY_COLOR=always\|never` | 强制开关颜色 |
|
|
160
195
|
| `NO_COLOR` | 关闭颜色 |
|
|
161
196
|
| `DGXAY_DEBUG=1` | 输出调试信息与堆栈 |
|
|
@@ -163,11 +198,18 @@ dgxay doctor 自检
|
|
|
163
198
|
|
|
164
199
|
## 终端兼容性
|
|
165
200
|
|
|
166
|
-
|
|
167
|
-
|
|
201
|
+
启动时会探测终端能力,分两种模式:
|
|
202
|
+
|
|
203
|
+
**支持光标上移(Windows Terminal、VSCode、iTerm、大多数 Linux 终端)**
|
|
204
|
+
输入框是真正的方框,命令菜单可以原地刷新、上下选择不残留。
|
|
205
|
+
|
|
206
|
+
**不支持光标上移(老式 conhost)**
|
|
207
|
+
自动降级为单行 `❯` 提示,只做行内刷新,不会出现无法擦除的叠加错位。
|
|
208
|
+
|
|
209
|
+
流式正文始终只使用 `\r` 回行首 + 空格擦尾,不依赖光标上移;生成过程中只在最后一行刷新预览,
|
|
210
|
+
结束时一次性按 Markdown 完整排版。所以正文每段只会出现一次。
|
|
168
211
|
|
|
169
|
-
|
|
170
|
-
(标题、列表、表格、代码块对齐)。所以在任何终端里每段正文都只会出现一次,不会堆积。
|
|
212
|
+
想强制降级:`DGXAY_NO_CURSOR_UP=1`;想强制 ASCII 框线:`DGXAY_ASCII=1`。
|
|
171
213
|
|
|
172
214
|
## 与桌面版的差异
|
|
173
215
|
|
package/README.md
CHANGED
|
@@ -66,15 +66,47 @@ dgxay run -p "summarize this repo's architecture" -o json | jq -r .text
|
|
|
66
66
|
|
|
67
67
|
## Interactive mode
|
|
68
68
|
|
|
69
|
+
The interface has three parts:
|
|
70
|
+
|
|
71
|
+
- **Input box** — a bordered box that adapts to the terminal width and wraps long input
|
|
72
|
+
- **Command menu** — appears when you type `/`, navigable with the arrow keys
|
|
73
|
+
- **Thinking area** — shows a live timer and the model's reasoning while it works
|
|
74
|
+
|
|
69
75
|
| Input | Action |
|
|
70
76
|
| --- | --- |
|
|
71
77
|
| text | Send to the model |
|
|
78
|
+
| `/` | Open the command menu |
|
|
79
|
+
| `↑` / `↓` | Menu open: move the selection. Menu closed: `↑` recalls your previous prompt |
|
|
80
|
+
| `Tab` | Complete the selected command |
|
|
81
|
+
| `Enter` | Menu open: run the selected command. Otherwise: send the message |
|
|
82
|
+
| `Esc` | Close the menu |
|
|
72
83
|
| `!command` | Run a shell command; output joins the context, e.g. `!git status` |
|
|
73
|
-
| `/` | Slash-command completion |
|
|
74
84
|
| trailing `\` | Continue on the next line |
|
|
75
85
|
| `Ctrl+C` | Abort while generating; clear the input when non-empty; exit when idle |
|
|
76
86
|
| `Ctrl+D` | Exit |
|
|
77
|
-
|
|
87
|
+
|
|
88
|
+
### Language
|
|
89
|
+
|
|
90
|
+
The interface is English by default. Switch to Chinese with `DGXAY_LANG=zh dgxay`.
|
|
91
|
+
The AI replies in the language you write in.
|
|
92
|
+
|
|
93
|
+
### Thinking
|
|
94
|
+
|
|
95
|
+
While the model reasons, the terminal shows a live timer:
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
⠿ Thinking 2.4s latest reasoning fragment…
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
When it finishes, that folds into a one-line summary followed by the full reasoning in grey:
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
⌁ Thought for 12.8s · 1450 chars
|
|
105
|
+
│ First I'll look at the directory layout…
|
|
106
|
+
│ Then read the entry file…
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Set `DGXAY_SHOW_REASONING=0` to keep only the summary.
|
|
78
110
|
|
|
79
111
|
### Slash commands
|
|
80
112
|
|
|
@@ -138,7 +170,11 @@ API keys are stored **in plain text** in `config.json`. Use `DGXAY_HOME=/tmp/tes
|
|
|
138
170
|
| `DGXAY_HOME` | Override the data directory |
|
|
139
171
|
| `DGXAY_API_KEY` | API key used by `config init` |
|
|
140
172
|
| `DGXAY_TOKEN_KEY` | Passphrase that encrypts OAuth tokens (plain base64 otherwise) |
|
|
141
|
-
| `
|
|
173
|
+
| `DGXAY_LANG=zh` | Use the Chinese interface (English by default) |
|
|
174
|
+
| `DGXAY_SHOW_REASONING=0` | Show only the thinking summary, not the reasoning text |
|
|
175
|
+
| `DGXAY_REASONING_LINES=6` | How many reasoning lines to display |
|
|
176
|
+
| `DGXAY_NO_CURSOR_UP=1` | Force the single-line fallback input |
|
|
177
|
+
| `DGXAY_ASCII=1` | Force ASCII box drawing characters |
|
|
142
178
|
| `DGXAY_COLOR=always\|never` | Force colours on/off |
|
|
143
179
|
| `NO_COLOR` | Disable colours |
|
|
144
180
|
| `DGXAY_DEBUG=1` | Debug output and stack traces |
|
|
@@ -146,13 +182,18 @@ API keys are stored **in plain text** in `config.json`. Use `DGXAY_HOME=/tmp/tes
|
|
|
146
182
|
|
|
147
183
|
## Terminal compatibility
|
|
148
184
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
185
|
+
The terminal is probed at startup and the UI adapts:
|
|
186
|
+
|
|
187
|
+
**Cursor-up supported** (Windows Terminal, VSCode, iTerm, most Linux terminals)
|
|
188
|
+
The input is a real box and the command menu refreshes in place without leftovers.
|
|
189
|
+
|
|
190
|
+
**Cursor-up unsupported** (legacy conhost)
|
|
191
|
+
Falls back to a single-line `❯` prompt with in-place redraw, so nothing can stack up.
|
|
192
|
+
|
|
193
|
+
Streaming text always uses only `\r` and space padding; while generating, just the last line is refreshed, and
|
|
194
|
+
the whole reply is laid out once as Markdown at the end. Every paragraph appears exactly once.
|
|
152
195
|
|
|
153
|
-
|
|
154
|
-
when generation ends, the whole reply is laid out once as Markdown (headings, lists, tables, code blocks).
|
|
155
|
-
Every paragraph therefore appears exactly once in any terminal.
|
|
196
|
+
Force the fallback with `DGXAY_NO_CURSOR_UP=1`, or ASCII borders with `DGXAY_ASCII=1`.
|
|
156
197
|
|
|
157
198
|
## Differences from the desktop app
|
|
158
199
|
|
package/package.json
CHANGED
package/src/cli/ansi.js
CHANGED
|
@@ -87,25 +87,51 @@ function isInteractive() {
|
|
|
87
87
|
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
function termWidth() {
|
|
91
|
-
const width = Number(
|
|
90
|
+
function termWidth(stream = process.stdout) {
|
|
91
|
+
const width = Number(stream.columns) || 0;
|
|
92
92
|
if (width > 0) return Math.max(20, width);
|
|
93
93
|
return 80;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
/**
|
|
97
|
+
* 终端能否安全输出 Unicode 框线字符。
|
|
98
|
+
* Windows Terminal / VSCode / 现代 shell 都支持;只有老的 conhost 需要降级成 ASCII。
|
|
99
|
+
*/
|
|
100
|
+
function useUnicode(stream = process.stdout) {
|
|
97
101
|
if (process.env.DGXAY_ASCII === '1') return false;
|
|
98
|
-
if (process.platform
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
return
|
|
102
|
+
if (process.platform !== 'win32') return true;
|
|
103
|
+
if (process.env.WT_SESSION || process.env.TERM_PROGRAM || process.env.ConEmuANSI) return true;
|
|
104
|
+
if (/UTF-?8/i.test(process.env.LANG || process.env.LC_ALL || '')) return true;
|
|
105
|
+
// 能拿到列数说明是真实交互终端;Windows 10+ 的 PowerShell 用 Consolas 渲染框线没有问题
|
|
106
|
+
return Boolean(stream.columns);
|
|
103
107
|
}
|
|
104
108
|
|
|
105
109
|
function clearLine() {
|
|
106
110
|
if (process.stdout.isTTY) process.stdout.write('\r\x1b[2K');
|
|
107
111
|
}
|
|
108
112
|
|
|
113
|
+
/* --- 光标与屏幕控制(供输入框 / 区域重绘使用) --- */
|
|
114
|
+
|
|
115
|
+
/** 上移 n 行(n <= 0 时返回空串) */
|
|
116
|
+
function cursorUp(n = 1) {
|
|
117
|
+
return n > 0 ? `\x1b[${n}A` : '';
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** 下移 n 行 */
|
|
121
|
+
function cursorDown(n = 1) {
|
|
122
|
+
return n > 0 ? `\x1b[${n}B` : '';
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** 清整行(光标不动) */
|
|
126
|
+
function eraseLine() {
|
|
127
|
+
return '\x1b[2K';
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** 清屏并把光标移到左上角 */
|
|
131
|
+
function clearScreen(stream = process.stdout) {
|
|
132
|
+
stream.write('\x1b[2J\x1b[H');
|
|
133
|
+
}
|
|
134
|
+
|
|
109
135
|
function hideCursor() {
|
|
110
136
|
if (process.stdout.isTTY) process.stdout.write('\x1b[?25l');
|
|
111
137
|
}
|
|
@@ -493,6 +519,10 @@ module.exports = {
|
|
|
493
519
|
termWidth,
|
|
494
520
|
useUnicode,
|
|
495
521
|
clearLine,
|
|
522
|
+
eraseLine,
|
|
523
|
+
clearScreen,
|
|
524
|
+
cursorUp,
|
|
525
|
+
cursorDown,
|
|
496
526
|
hideCursor,
|
|
497
527
|
showCursor,
|
|
498
528
|
stripAnsi,
|
package/src/cli/approval.js
CHANGED
|
@@ -126,14 +126,14 @@ async function askApproval(request, options = {}) {
|
|
|
126
126
|
if (!interactive) {
|
|
127
127
|
// 非交互:一律拒绝,并提示如何绕过
|
|
128
128
|
process.stderr.write(
|
|
129
|
-
`${COLORS.yellow('[
|
|
130
|
-
`${COLORS.dim('
|
|
129
|
+
`${COLORS.yellow('[permission]')} ${request.title}:${request.operation}\n` +
|
|
130
|
+
`${COLORS.dim(' Not an interactive terminal — denied. Use --permission full or run interactively.')}\n`
|
|
131
131
|
);
|
|
132
132
|
return false;
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
const lines = [];
|
|
136
|
-
lines.push({ text: `${COLORS.faint('
|
|
136
|
+
lines.push({ text: `${COLORS.faint('op')} ${COLORS.bold(request.operation)}`, raw: `op ${request.operation}` });
|
|
137
137
|
for (const line of String(request.detail || '').split('\n')) {
|
|
138
138
|
if (!line.trim()) continue;
|
|
139
139
|
const idx = line.indexOf(':');
|
|
@@ -152,12 +152,12 @@ async function askApproval(request, options = {}) {
|
|
|
152
152
|
}
|
|
153
153
|
if (request.workspace) {
|
|
154
154
|
lines.push({ text: '', raw: '' });
|
|
155
|
-
lines.push({ text: COLORS.faint(
|
|
155
|
+
lines.push({ text: COLORS.faint(`workspace ${request.workspace}`), raw: `workspace ${request.workspace}` });
|
|
156
156
|
}
|
|
157
157
|
if (request.review) {
|
|
158
158
|
lines.push({ text: '', raw: '' });
|
|
159
|
-
const who = request.reviewer ?
|
|
160
|
-
lines.push({ text: COLORS.red(`${who}
|
|
159
|
+
const who = request.reviewer ? `reviewer ${request.reviewer}` : 'auto-review';
|
|
160
|
+
lines.push({ text: COLORS.red(`${who} flagged this`), raw: `${who} flagged this` });
|
|
161
161
|
for (const piece of wrapText(request.review, 58)) {
|
|
162
162
|
lines.push({ text: COLORS.dim(` ${piece}`), raw: ` ${piece}` });
|
|
163
163
|
}
|
|
@@ -165,20 +165,20 @@ async function askApproval(request, options = {}) {
|
|
|
165
165
|
|
|
166
166
|
process.stdout.write(`${frameText(lines, { title: request.title })}\n`);
|
|
167
167
|
|
|
168
|
-
const promptText = `${COLORS.accent('
|
|
168
|
+
const promptText = `${COLORS.accent('Allow this?')} ${COLORS.faint('[y] yes [n] no')} `;
|
|
169
169
|
process.stdout.write(promptText);
|
|
170
170
|
const result = await readKey({ timeout: options.timeout || APPROVAL_TIMEOUT });
|
|
171
171
|
|
|
172
172
|
if (result.key === 'y') {
|
|
173
|
-
process.stdout.write(`${COLORS.green('
|
|
173
|
+
process.stdout.write(`${COLORS.green('allowed')}\n`);
|
|
174
174
|
return true;
|
|
175
175
|
}
|
|
176
176
|
const label =
|
|
177
177
|
result.key === 'timeout'
|
|
178
|
-
? COLORS.yellow('
|
|
178
|
+
? COLORS.yellow('timed out — denied')
|
|
179
179
|
: result.key === 'escape'
|
|
180
|
-
? COLORS.dim('
|
|
181
|
-
: COLORS.red('
|
|
180
|
+
? COLORS.dim('denied')
|
|
181
|
+
: COLORS.red('denied');
|
|
182
182
|
process.stdout.write(`${label}\n`);
|
|
183
183
|
return false;
|
|
184
184
|
}
|