thatgfsj-code 0.2.1 → 0.2.2
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/CHANGELOG.md +131 -0
- package/DEVELOPMENT.md +286 -0
- package/README.md +131 -30
- package/dist/core/ai-engine.d.ts +17 -0
- package/dist/core/ai-engine.d.ts.map +1 -1
- package/dist/core/ai-engine.js +23 -7
- package/dist/core/ai-engine.js.map +1 -1
- package/dist/index.js +18 -27
- package/dist/index.js.map +1 -1
- package/dist/repl/input.d.ts +8 -0
- package/dist/repl/input.d.ts.map +1 -1
- package/dist/repl/input.js +12 -0
- package/dist/repl/input.js.map +1 -1
- package/dist/repl/loop.d.ts +56 -0
- package/dist/repl/loop.d.ts.map +1 -1
- package/dist/repl/loop.js +333 -4
- package/dist/repl/loop.js.map +1 -1
- package/dist/repl/output.d.ts.map +1 -1
- package/dist/repl/output.js +3 -1
- package/dist/repl/output.js.map +1 -1
- package/dist/repl/welcome.js +1 -1
- package/dist/repl/welcome.js.map +1 -1
- package/docs/API_KEY_GUIDE.md +6 -0
- package/docs/FAQ.md +25 -3
- package/package.json +35 -3
- package/.patches/0001-fix-repl-replace-readline-with-inquirer-input-for-ke.patch +0 -279
- package/.patches/0002-fix-repl-stream-AI-output-directly-to-stdout-so-term.patch +0 -564
- package/.patches/0003-fix-session-break-the-hallucination-loop-in-assistan.patch +0 -194
- package/.patches/0004-chore-release-bump-version-to-0.2.1.patch +0 -24
- package/ROADMAP.md +0 -107
- package/src/agent/core.ts +0 -179
- package/src/agent/index.ts +0 -8
- package/src/agent/intent.ts +0 -181
- package/src/agent/streaming.ts +0 -132
- package/src/core/ai-engine.ts +0 -437
- package/src/core/cli.ts +0 -171
- package/src/core/config.ts +0 -147
- package/src/core/context-compactor.ts +0 -245
- package/src/core/hooks.ts +0 -196
- package/src/core/permissions.ts +0 -308
- package/src/core/session.ts +0 -165
- package/src/core/skills.ts +0 -208
- package/src/core/state.ts +0 -120
- package/src/core/subagent.ts +0 -195
- package/src/core/system-prompt.ts +0 -163
- package/src/core/tool-registry.ts +0 -157
- package/src/core/types.ts +0 -280
- package/src/index.ts +0 -544
- package/src/mcp/client.ts +0 -330
- package/src/repl/index.ts +0 -8
- package/src/repl/input.ts +0 -139
- package/src/repl/loop.ts +0 -280
- package/src/repl/output.ts +0 -222
- package/src/repl/welcome.ts +0 -296
- package/src/tools/file.ts +0 -117
- package/src/tools/git.ts +0 -132
- package/src/tools/index.ts +0 -48
- package/src/tools/search.ts +0 -263
- package/src/tools/shell.ts +0 -181
- package/src/utils/diff-preview.ts +0 -202
- package/src/utils/index.ts +0 -8
- package/src/utils/memory.ts +0 -223
- package/src/utils/project-context.ts +0 -207
- package/tsconfig.json +0 -19
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to **Thatgfsj Code** are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/) and the project adheres to
|
|
5
|
+
[Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
### Planned
|
|
10
|
+
- 实现真实的 provider 工具调用解析(OpenAI `delta.tool_calls`、Anthropic
|
|
11
|
+
`tool_use` block、Gemini `functionCall`),让已注册的 Tool 真正参与 Agent Loop
|
|
12
|
+
- `install.sh` / `install.ps1` 默认分支自适应(`master` ↔ `main`)
|
|
13
|
+
- 移除 README 中失效的 `git.io/thatgfsj` 短链,全部改回 `raw.githubusercontent.com`
|
|
14
|
+
- MCP 客户端 `this.process?.connected` → `this.process?.stdin?.writable`
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## [0.2.2] - 2026-07-06
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
- **`/model` / `/provider` REPL 命令**:1.0.4 老版本里这两个交互式切换命令
|
|
22
|
+
在 0.2.x 系列中曾经被删掉,这次重新实现并对齐新代码结构,并且从
|
|
23
|
+
`thatgfsj-code@1.0.4` 的 TUI 版本里借鉴并移植了两项核心体验:
|
|
24
|
+
- `/model` 列出当前 provider 的所有模型(含 `✓` 标记当前选中),输入编号
|
|
25
|
+
或完整 model id 切换;立即写入 `~/.thatgfsj/config.json` 并通过
|
|
26
|
+
`AIEngine.updateConfig()` 让下一次流式请求生效。空输入回车保持当前,
|
|
27
|
+
Ctrl+C 取消。
|
|
28
|
+
- **历史记录持久化**(借鉴 1.0.4 的 `saveModelToHistory`):切换过的
|
|
29
|
+
model id 写入 `~/.thatgfsj/models.json`,下次打开 `/model` 时会在
|
|
30
|
+
内置列表上方显示「最近使用」段(最多 5 条,按时间排序,可通过 `r1`..`r5`
|
|
31
|
+
快捷选取,自动 dedup)。
|
|
32
|
+
- **任意 model id**:输入既不在内置列表也不在历史里的字符串,按自定义模型
|
|
33
|
+
处理(适合 OpenAI-compatible 中转站 / Ollama 自定义模型名场景)。
|
|
34
|
+
- `/provider` 列出 9 个支持的 provider,选完自动跟进 `/model` 让用户选
|
|
35
|
+
新 provider 下的模型;如果新 provider 缺少对应的 API Key 环境变量,会提示
|
|
36
|
+
用户先 `gfcode init` 或 `export`。
|
|
37
|
+
- 两个命令都接受带或不带前导 `/`(与 1.0.4 的 `/模型` 老习惯兼容)。
|
|
38
|
+
- 切换后会把一行 `[system: ... switched to ...]` 注入 `SessionManager`,
|
|
39
|
+
让 LLM 在下一轮看到变更而不丢失上下文。
|
|
40
|
+
- **`AIEngine.updateConfig()` / `getConfig()`**:暴露给 REPL 内部用,支持
|
|
41
|
+
在不重启引擎的情况下替换 `model` / `provider` / `baseUrl` / `temperature` /
|
|
42
|
+
`maxTokens`。
|
|
43
|
+
- **34 个单元 / 冒烟测试**(`tests/`):npm 内置 `node:test`,覆盖:
|
|
44
|
+
- `smoke-cli.test.js` — `--version` / `--help` / 子命令空参守卫 / `bash -n`。
|
|
45
|
+
- `unit-session.test.js` — `SessionManager` 拦截「已中断」、相邻助手去重、
|
|
46
|
+
`truncate` 保留 system。
|
|
47
|
+
- `unit-diff.test.js` — `DiffPreview.compare` 边界(unicode / 大输入 / CRLF /
|
|
48
|
+
空输入)。
|
|
49
|
+
- `unit-switcher.test.js` — `/model` 的纯 helper(编号 / id 解析)
|
|
50
|
+
与 `AIEngine.updateConfig` 行为。
|
|
51
|
+
- **新增** `unit-model-history.test.js` — `~/.thatgfsj/models.json` 的
|
|
52
|
+
读写、dedup、corrupt-file 容错(`tests` 5 个 case)。
|
|
53
|
+
|
|
54
|
+
### Fixed
|
|
55
|
+
- **ESM `require()` 报错**:`src/index.ts` 顶层原先使用
|
|
56
|
+
`require('child_process')` / `require('fs')`,在 `"type": "module"` 项目里
|
|
57
|
+
直接抛 `ReferenceError: require is not defined`,导致 CLI 在包含 `package.json`
|
|
58
|
+
的项目目录下完全无法启动。改为 `createRequire(import.meta.url)` 兼容。
|
|
59
|
+
- **`--version` 与 banner 打印老版本号**:`src/index.ts:102` 与
|
|
60
|
+
`src/repl/output.ts:198` 硬编码 `0.2.0`/`v0.2.0`,而 `package.json` 是 `0.2.1`
|
|
61
|
+
甚至后续 release。两处都改为从 `package.json` 动态读取。
|
|
62
|
+
- **REPL Ctrl+C 无法退出**:`src/repl/loop.ts` 的全局 SIGINT 处理只打
|
|
63
|
+
warning,从来不调用 `this.running = false`。新增 `REPLInput.requestCancel()`,
|
|
64
|
+
在空输入连按两次 Ctrl+C 时正确退出。
|
|
65
|
+
- **`executeTask` 助手消息丢失**:单命令模式下助手回复从来没写回
|
|
66
|
+
`SessionManager`,导致同名 SessionManager 实例对多轮 prompt 拿不到上下文。
|
|
67
|
+
- **`(this.ai as any).chatStream`** 多余 `any` 强转:`this.ai` 已经过
|
|
68
|
+
null-check,删掉即可。
|
|
69
|
+
- **`extractToolCalls` 死分支**:`src/core/ai-engine.ts` 中 `if (…includes('tool_use'))
|
|
70
|
+
return undefined; return undefined;` 等价于永远返回 `undefined`。改为单行
|
|
71
|
+
`return undefined;` 并添加注释说明当前实际无法解析 tool call(属于已知限制)。
|
|
72
|
+
- **欢迎页 banner 字样写错**:`src/repl/welcome.ts` 顶部仍写着 `Claude Code`,
|
|
73
|
+
与项目名 `Thatgfsj Code` 不一致。已更正。
|
|
74
|
+
|
|
75
|
+
### Changed
|
|
76
|
+
- **`init` 命令动态 import 去重**:`src/index.ts:54` 的
|
|
77
|
+
`await import('./repl/welcome.js')` 与顶部静态 import 重复,删除。
|
|
78
|
+
- **`handleModelSwitch`** 在 `src/index.ts` 中属于死代码(无调用者),删除。
|
|
79
|
+
- **欢迎页标题框宽度自适应**:把 banner 顶部的 `Claude Code ` 换成
|
|
80
|
+
`Thatgfsj Code ` 后调整间隔符长度,保证盒子保持 62 列对齐。
|
|
81
|
+
|
|
82
|
+
### Documentation
|
|
83
|
+
- 新增 `DEVELOPMENT.md`:覆盖架构图、构建/测试流程、添加 provider / 命令 / tool
|
|
84
|
+
的步骤、跨平台注意事项、发布流程。
|
|
85
|
+
- 新增本文件 `CHANGELOG.md`。
|
|
86
|
+
- 更新 `README.md`:替换失效的 `git.io/thatgfsj` 安装链接为 GitHub raw 直链;
|
|
87
|
+
补上 `gfcode init` 使用示例与所有顶层 / 子命令 flag;附 REPL 内置命令列表
|
|
88
|
+
以及 Node ≥ 18 的前置条件。
|
|
89
|
+
- `docs/FAQ.md`、`docs/API_KEY_GUIDE.md` 交叉链接到 `DEVELOPMENT.md`。
|
|
90
|
+
|
|
91
|
+
### Packaging
|
|
92
|
+
- `package.json` 新增 `files`、`engines`、`repository`、`bugs`、`homepage`
|
|
93
|
+
字段;原先没有 `files` 白名单会导致 `npm publish` 不会自动包含 `dist/`
|
|
94
|
+
(dist 在 `.gitignore` 里),现在通过显式白名单确保 tarball 包含
|
|
95
|
+
构建产物与文档。
|
|
96
|
+
- 新增 `scripts.test`、`scripts.test:silent`、`scripts.prepublishOnly`,
|
|
97
|
+
发布前自动跑测试和 build,防止 release 出错。
|
|
98
|
+
|
|
99
|
+
### Tests
|
|
100
|
+
- 新增 `tests/` 目录,使用 Node 内置 `node --test` 跑通:
|
|
101
|
+
- `tests/smoke-cli.test.js` — `dist/index.js --version` / `--help` / `chat`
|
|
102
|
+
/ `explain` / `debug` / `template` 空参守卫;`bash -n install.sh` 语法。
|
|
103
|
+
- `tests/unit-session.test.js` — `SessionManager` 拦截「已中断」污染、
|
|
104
|
+
相邻助手消息去重、`truncate` 保留 system。
|
|
105
|
+
- `tests/unit-diff.test.js` — `DiffPreview.compare` 在相同 / 不同 /
|
|
106
|
+
空 / unicode / CRLF 输入下的行为。
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## [0.2.1] - 2026-07-XX
|
|
111
|
+
|
|
112
|
+
### Fixed
|
|
113
|
+
- REPL 数字小键盘方向键响应(升级到 `@inquirer/input`,不再用 Node `readline`
|
|
114
|
+
直接 `.question()`)。
|
|
115
|
+
- 流式输出保留完整内容、不再 reset 终端 / 跳顶,用户可滚动查看历史。
|
|
116
|
+
- 修复 AI 输出 `[已中断]` 等污染字符串时反复循环同一段的 bug(`SessionManager`
|
|
117
|
+
拦截 `已中断` / `think` block,`getMessages()` 做相邻助手去重)。
|
|
118
|
+
|
|
119
|
+
### Added
|
|
120
|
+
- `src/repl/welcome.ts` Claude Code 风格欢迎页与 `gfcode init` 配置向导。
|
|
121
|
+
- `src/tools/{file,shell,git,search}.ts` 完整工具实现,并通过
|
|
122
|
+
`ToolRegistry` 注册到 AI Engine。
|
|
123
|
+
- `src/agent/{core,intent,streaming}.ts` Agent Loop、意图识别、流式输出。
|
|
124
|
+
- `src/utils/{diff-preview,memory,project-context}.ts`。
|
|
125
|
+
- `src/mcp/client.ts`:MCP 客户端雏形。
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## [0.1.0] - initial
|
|
130
|
+
|
|
131
|
+
- 初版发布:基础 `explain` / `debug` / `chat` / `template` 命令与 REPL 雏形。
|
package/DEVELOPMENT.md
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# Development Guide — Thatgfsj Code
|
|
2
|
+
|
|
3
|
+
This document is for contributors / maintainers. End users should read
|
|
4
|
+
[`README.md`](./README.md) instead.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 1. Overview
|
|
9
|
+
|
|
10
|
+
**Thatgfsj Code** is a Claude Code–style interactive AI coding assistant that
|
|
11
|
+
runs entirely in your terminal. It is written in TypeScript, published as an
|
|
12
|
+
ESM npm package, distributed under MIT, and targets **Node.js ≥ 18**.
|
|
13
|
+
|
|
14
|
+
The tool exposes:
|
|
15
|
+
|
|
16
|
+
- A non-interactive single-shot CLI (`gfcode "..."`).
|
|
17
|
+
- An interactive REPL (`gfcode` with no args).
|
|
18
|
+
- Subcommands: `init`, `explain`, `debug`, `chat`, `template`.
|
|
19
|
+
|
|
20
|
+
The release pipeline is: **bug fix → docs → smoke test → git commit + tag →
|
|
21
|
+
push → GitHub release → `npm publish`**. Everything in `src/` compiles to
|
|
22
|
+
`dist/` via `tsc`; the `package.json` `files` whitelist decides what ships
|
|
23
|
+
in the npm tarball.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 2. Architecture
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
31
|
+
│ src/index.ts │
|
|
32
|
+
│ CLI entry · commander program · subcommand dispatch │
|
|
33
|
+
│ (init / explain / debug / chat / template / default REPL) │
|
|
34
|
+
└─────────────────────────┬────────────────────┬───────────────┘
|
|
35
|
+
│ │
|
|
36
|
+
┌───────────▼──────────┐ ┌───────▼─────────────┐
|
|
37
|
+
│ AI Engine + Tools │ │ REPL Loop │
|
|
38
|
+
│ src/core/ai-engine │ │ src/repl/{loop,...} │
|
|
39
|
+
│ src/tools/{file,…} │ │ @inquirer/input │
|
|
40
|
+
└───────────┬──────────┘ └───────┬─────────────┘
|
|
41
|
+
│ │
|
|
42
|
+
┌───────────▼────────────────────▼───────────────┐
|
|
43
|
+
│ Core / Utils / Agent / MCP │
|
|
44
|
+
│ src/core/{config,session,types,subagent,…} │
|
|
45
|
+
│ src/utils/{diff-preview,memory,project-…} │
|
|
46
|
+
│ src/agent/{core,intent,streaming} │
|
|
47
|
+
│ src/mcp/client.ts │
|
|
48
|
+
└────────────────────────────────────────────────┘
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Module responsibilities
|
|
52
|
+
|
|
53
|
+
| Layer | Files | Job |
|
|
54
|
+
|-----------|----------------------------------------------|--------------------------------------------------------------------|
|
|
55
|
+
| Entry | `src/index.ts` | commander program, subcommand handlers, REPL bootstrap |
|
|
56
|
+
| REPL | `src/repl/{input,output,loop,welcome}.ts` | interactive prompt, ANSI output, SIGINT, welcome/init wizard |
|
|
57
|
+
| Core | `src/core/{ai-engine,config,session,…}.ts` | AI provider abstraction, config loading, session state |
|
|
58
|
+
| Tools | `src/tools/{file,shell,git,search}.ts` | file I/O, shell exec, git ops, code search |
|
|
59
|
+
| Agent | `src/agent/{core,intent,streaming}.ts` | intent classification, agent loop, streamed terminal output |
|
|
60
|
+
| Utils | `src/utils/{diff-preview,memory,…}.ts` | side-effect-free helpers |
|
|
61
|
+
| MCP | `src/mcp/client.ts` | Model Context Protocol stdio client |
|
|
62
|
+
|
|
63
|
+
### Data flow on a single prompt
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
User ──▶ commander ──▶ executeTask(prompt)
|
|
67
|
+
│
|
|
68
|
+
├─▶ ConfigManager.load() (process.env + ~/.thatgfsj/config.json)
|
|
69
|
+
│
|
|
70
|
+
├─▶ AIEngine.chatStream(messages) ──▶ provider base URL /v1/chat/completions
|
|
71
|
+
│ or Anthropic /messages
|
|
72
|
+
│ or Gemini :generateContent
|
|
73
|
+
│
|
|
74
|
+
└─▶ process.stdout.write(chunks) ──▶ terminal (scrollable history)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 3. Build & install
|
|
80
|
+
|
|
81
|
+
### Prerequisites
|
|
82
|
+
|
|
83
|
+
- Node.js ≥ 18 (project uses native `fetch`, `AbortController`, top-level `await`,
|
|
84
|
+
`node --test`).
|
|
85
|
+
- npm ≥ 9 (so `npm publish` understands the `files` whitelist).
|
|
86
|
+
- Git (for the install scripts and during release).
|
|
87
|
+
|
|
88
|
+
### Local development
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
git clone https://github.com/Thatgfsj/thatgfsj-code.git
|
|
92
|
+
cd thatgfsj-code
|
|
93
|
+
npm install
|
|
94
|
+
npm run build # produces ./dist
|
|
95
|
+
npm test # node --test tests/
|
|
96
|
+
npm start # node dist/index.js (interactive REPL)
|
|
97
|
+
npm run dev # tsc && node dist/index.js
|
|
98
|
+
|
|
99
|
+
# Optional: link globally so `gfcode` is in your PATH
|
|
100
|
+
npm link
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`npm install` after cloning will trigger `prepublishOnly` if you have it
|
|
104
|
+
configured (we do). Always run `npm run build` after editing source so the
|
|
105
|
+
`dist/` matches `src/`.
|
|
106
|
+
|
|
107
|
+
### Platform notes
|
|
108
|
+
|
|
109
|
+
- **Windows**: `src/index.ts` invokes `chcp 65001` synchronously before any
|
|
110
|
+
output to force UTF-8 in the legacy Windows console. Done via
|
|
111
|
+
`createRequire(import.meta.url)` so it works under `"type": "module"`.
|
|
112
|
+
- **Numeric keypad arrows**: the REPL uses `@inquirer/input` instead of Node's
|
|
113
|
+
built-in `readline.question()` because the latter doesn't reliably translate
|
|
114
|
+
numeric-keypad ANSI escape sequences on Windows terminals (Bug #1 history).
|
|
115
|
+
- **Streaming output**: `process.stdout.write` directly, never `rl.question`
|
|
116
|
+
inside the loop — the latter would reset the terminal and lose scrollback.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## 4. Adding a provider
|
|
121
|
+
|
|
122
|
+
1. Add the env-var map to `src/core/config.ts` (`envKeys`).
|
|
123
|
+
2. Add a `*_MODELS` array to `src/repl/welcome.ts` and wire it through
|
|
124
|
+
`getModelsForProvider()` and `interactiveSetup()` (numbered choice).
|
|
125
|
+
3. Update `WelcomeScreen.hasApiKey()` in `src/repl/welcome.ts` to include the
|
|
126
|
+
new env-var name.
|
|
127
|
+
4. If the provider uses a non-OpenAI request format, extend the dispatcher
|
|
128
|
+
in `src/core/ai-engine.ts::streamRequest`.
|
|
129
|
+
5. Update `docs/API_KEY_GUIDE.md` and `README.md`'s provider list.
|
|
130
|
+
|
|
131
|
+
No other files should care about the specific provider.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## 5. Adding a built-in CLI command
|
|
136
|
+
|
|
137
|
+
`src/index.ts` is the single `commander` program. Register a new command
|
|
138
|
+
**before** `program.parse(process.argv)`:
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
program
|
|
142
|
+
.command('foo')
|
|
143
|
+
.description('...')
|
|
144
|
+
.argument('<bar>', '...')
|
|
145
|
+
.action(async (bar) => { /* ... */ });
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
For REPL-internal slash-style commands (visible only inside the interactive
|
|
149
|
+
loop), add a new `case` to `src/repl/loop.ts::handleCommand`.
|
|
150
|
+
|
|
151
|
+
### Built-in REPL commands (current)
|
|
152
|
+
|
|
153
|
+
| Command | Behavior |
|
|
154
|
+
| ---------------- | -------------------------------------------------------------------------------- |
|
|
155
|
+
| `help` | Show built-in command list |
|
|
156
|
+
| `exit` / `quit` | Leave the REPL |
|
|
157
|
+
| `clear` | Clear the screen |
|
|
158
|
+
| `context` | Show the current project context |
|
|
159
|
+
| `history` | Show the command history for this session |
|
|
160
|
+
| `tools` | List registered tools |
|
|
161
|
+
| `models` | Read-only listing of the current provider's models |
|
|
162
|
+
| `providers` | Read-only listing of all providers |
|
|
163
|
+
| `/model` | **Interactive picker — actually switches the active model** for the current provider. Persists to `~/.thatgfsj/config.json` and calls `AIEngine.updateConfig()`. |
|
|
164
|
+
| `/provider` | **Interactive picker — switches provider + chains into `/model`** for the new one. Warns if the corresponding env-var / saved API key is missing. |
|
|
165
|
+
| `Ctrl+C` | Aborts the in-flight stream once, exits after two empty-prompt cancels. |
|
|
166
|
+
|
|
167
|
+
Both `/model` and `/provider` accept either a numeric index or the exact
|
|
168
|
+
provider/model id; pressing Enter with no input keeps the current value. After
|
|
169
|
+
a switch, `REPLLoop` injects a one-line `[system: ... switched to ...]`
|
|
170
|
+
message into the session so the LLM can see the change in the next turn.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## 6. Adding a tool
|
|
175
|
+
|
|
176
|
+
Tools implement the `Tool` interface declared in `src/core/types.ts`:
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
export interface Tool {
|
|
180
|
+
name: string;
|
|
181
|
+
description: string;
|
|
182
|
+
parameters: ToolParameter[];
|
|
183
|
+
execute(params: any, ctx: ToolContext): Promise<ToolResult>;
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Steps:
|
|
188
|
+
1. Create `src/tools/<name>.ts` exporting a class implementing `Tool`.
|
|
189
|
+
2. Register it in `src/tools/index.ts::getBuiltInTools()` so both the REPL
|
|
190
|
+
`REPLLoop.init` and `executeTask` pick it up automatically.
|
|
191
|
+
|
|
192
|
+
> **Known limitation (0.2.2):** `src/core/ai-engine.ts::extractToolCalls`
|
|
193
|
+
> currently returns `undefined`. Tools are registered but never actually
|
|
194
|
+
> invoked from a streamed response. Wiring this up requires parsing each
|
|
195
|
+
> provider's native `tool_calls` delta and is tracked under "Unreleased" in
|
|
196
|
+
> `CHANGELOG.md`. Until then, `ToolRegistry` is a forward-compatible seam.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## 7. MCP integration
|
|
201
|
+
|
|
202
|
+
`src/mcp/client.ts` defines a small stdio MCP client. It spawns a child
|
|
203
|
+
process and exchanges JSON-RPC via newline-delimited messages. The current
|
|
204
|
+
shape is intentionally minimal — see the file for the protocol mapping.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## 8. Cross-platform & UX details
|
|
209
|
+
|
|
210
|
+
- **Encoding**: Windows-only `chcp 65001` in `src/index.ts:9-15`. Skipped
|
|
211
|
+
silently if it fails.
|
|
212
|
+
- **Color**: `chalk` v5 is ESM-only; the project is `"type": "module"` so
|
|
213
|
+
this is fine.
|
|
214
|
+
- **Spinner**: `ora` v7 with `dots` spinner; cyan.
|
|
215
|
+
- **History**: local to a single REPL session (not persisted across runs).
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## 9. Tests
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
npm test # runs node --test tests/ (full TAP output)
|
|
223
|
+
npm run test:silent # dot reporter, for noisy CI logs
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Tests live in `tests/` and use Node's built-in `node:test` runner — no
|
|
227
|
+
vitest / jest dev-dep. They cover:
|
|
228
|
+
|
|
229
|
+
- Smoke tests of the compiled `dist/index.js` binary (`--version`, `--help`,
|
|
230
|
+
subcommand empty-arg guards, `bash -n install.sh`).
|
|
231
|
+
- Boundary tests of pure modules (`SessionManager`, `DiffPreview`,
|
|
232
|
+
`REPLOutput`) — empty input, unicode, CRLF, very long input.
|
|
233
|
+
- No network. No mocked API keys. No shell-out side-effects other than the
|
|
234
|
+
install script's `bash -n` parse-check.
|
|
235
|
+
|
|
236
|
+
CI integration: if you set this up later, run `npm test && npm run build`
|
|
237
|
+
in the job; the `prepublishOnly` script already chains them.
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## 10. Release process
|
|
242
|
+
|
|
243
|
+
1. Edit `package.json` `version` (semver).
|
|
244
|
+
2. Add a new section at the top of `CHANGELOG.md`.
|
|
245
|
+
3. `npm test && npm run build` locally — make sure both are green.
|
|
246
|
+
4. `git add -A && git commit -m "release: vX.Y.Z"`
|
|
247
|
+
5. `git tag vX.Y.Z`
|
|
248
|
+
6. `git push origin main --follow-tags`
|
|
249
|
+
7. `gh release create vX.Y.Z --title "vX.Y.Z" --notes-file CHANGELOG.md`
|
|
250
|
+
(the section for the new version is the top entry; trim if needed).
|
|
251
|
+
8. `npm publish --access public` (a `prepublishOnly` step in `package.json`
|
|
252
|
+
re-runs tests + build, so you can't accidentally publish a stale build).
|
|
253
|
+
|
|
254
|
+
### Semver policy
|
|
255
|
+
|
|
256
|
+
- **patch** (`Z`): bug fixes, docs, refactors, no public API change.
|
|
257
|
+
- **minor** (`Y`): new public command, new provider, new tool.
|
|
258
|
+
- **major** (`X`): breaking change to the CLI surface or the JS API surface
|
|
259
|
+
(currently no JS API is stable).
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## 11. Troubleshooting
|
|
264
|
+
|
|
265
|
+
### `ReferenceError: require is not defined`
|
|
266
|
+
|
|
267
|
+
You're editing TypeScript and accidentally called `require(...)` outside of a
|
|
268
|
+
`createRequire(import.meta.url)` shim. The project is ESM. Use `import x from 'y'`
|
|
269
|
+
at the top of the file.
|
|
270
|
+
|
|
271
|
+
### `npm publish` ships no `dist/`
|
|
272
|
+
|
|
273
|
+
You forgot to run `npm run build` (or the `prepublishOnly` script was
|
|
274
|
+
removed). The `files` whitelist in `package.json` requires the `dist/`
|
|
275
|
+
directory to exist locally when publishing.
|
|
276
|
+
|
|
277
|
+
### `gfcode --version` shows the wrong number
|
|
278
|
+
|
|
279
|
+
Edit it in `package.json` only — `src/index.ts` and `src/repl/output.ts` both
|
|
280
|
+
read `VERSION` from `package.json` via `import pkg from '../package.json' …`.
|
|
281
|
+
|
|
282
|
+
### REPL keypresses / Ctrl+C misbehave on Windows
|
|
283
|
+
|
|
284
|
+
Make sure you're on Windows Terminal or any TTY that emits ANSI escape
|
|
285
|
+
sequences natively. The legacy `cmd.exe` console has incomplete ANSI
|
|
286
|
+
support — upgrade to Windows Terminal or use PowerShell.
|
package/README.md
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
# Thatgfsj Code 🤖
|
|
2
2
|
|
|
3
|
-
你的 CLI 工具
|
|
3
|
+
你的 CLI 工具 - 像 Claude Code 一样使用 AI 编程。
|
|
4
|
+
|
|
5
|
+
> 当前版本: **0.2.2** · Node.js ≥ 18 · MIT 许可证
|
|
4
6
|
|
|
5
7
|
---
|
|
6
8
|
|
|
7
9
|
## ⭐ 特性
|
|
8
10
|
|
|
9
|
-
- 🤖 **AI 对话**
|
|
10
|
-
- 📖 **代码解释**
|
|
11
|
-
- 🔧 **代码调试**
|
|
12
|
-
- 📦 **代码模板**
|
|
13
|
-
- 💬 **交互模式**
|
|
11
|
+
- 🤖 **AI 对话** — 用自然语言让 AI 帮你写代码
|
|
12
|
+
- 📖 **代码解释** — 通俗易懂地解释代码含义
|
|
13
|
+
- 🔧 **代码调试** — 找出 bug 并给出修复方案
|
|
14
|
+
- 📦 **代码模板** — 快速生成项目代码
|
|
15
|
+
- 💬 **交互模式** — 持续对话,像聊天一样编程
|
|
16
|
+
- 🧰 **内置工具** — 文件读写 / Shell 执行 / Git 操作 / 代码搜索
|
|
17
|
+
- 🚫 **抗污染防御** — `SessionManager` 自动过滤 `[已中断]` 等历史污染
|
|
14
18
|
|
|
15
19
|
---
|
|
16
20
|
|
|
@@ -18,83 +22,180 @@
|
|
|
18
22
|
|
|
19
23
|
### 🎯 一键安装(推荐)
|
|
20
24
|
|
|
21
|
-
|
|
25
|
+
安装脚本全部托管在 GitHub 上、`install.sh` / `install.ps1` / `install.bat`
|
|
26
|
+
可直接 curl / iwr:
|
|
22
27
|
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
**macOS / Linux**:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
curl -L https://raw.githubusercontent.com/Thatgfsj/thatgfsj-code/main/install.sh | bash
|
|
25
32
|
```
|
|
26
33
|
|
|
27
34
|
**Windows (PowerShell)**:
|
|
28
35
|
|
|
29
36
|
```powershell
|
|
30
|
-
|
|
37
|
+
irm https://raw.githubusercontent.com/Thatgfsj/thatgfsj-code/main/install.ps1 | iex
|
|
31
38
|
```
|
|
32
39
|
|
|
33
|
-
**
|
|
40
|
+
**Windows (CMD)**:
|
|
34
41
|
|
|
35
|
-
```
|
|
36
|
-
curl -
|
|
42
|
+
```cmd
|
|
43
|
+
curl -L https://raw.githubusercontent.com/Thatgfsj/thatgfsj-code/main/install.bat -o install.bat && install.bat
|
|
37
44
|
```
|
|
38
45
|
|
|
46
|
+
> ⚠️ 旧版本 README 里出现的 `https://git.io/thatgfsj` 短链已失效(GitHub 在 2022
|
|
47
|
+
> 年下线了 `git.io`),请使用上面的原始 GitHub 链接。
|
|
48
|
+
|
|
39
49
|
安装过程会自动完成:
|
|
40
|
-
1. 检查/安装 Node.js
|
|
41
|
-
2. 下载代码
|
|
42
|
-
3.
|
|
43
|
-
4.
|
|
44
|
-
5. 引导设置 API Key
|
|
50
|
+
1. 检查/安装 Node.js(≥ 18)
|
|
51
|
+
2. 下载代码 / 链接 npm 全局命令
|
|
52
|
+
3. 安装依赖并构建 `dist/`
|
|
53
|
+
4. 引导你设置 API Key
|
|
45
54
|
|
|
46
55
|
---
|
|
47
56
|
|
|
48
57
|
## 📖 使用方法
|
|
49
58
|
|
|
50
|
-
|
|
59
|
+
安装完成后,在终端里直接用 `gfcode`(或 `thatgfsj`,两个 bin 都指向同一入口):
|
|
51
60
|
|
|
52
61
|
```bash
|
|
53
62
|
# 交互模式(推荐)
|
|
54
63
|
gfcode
|
|
55
64
|
|
|
56
|
-
# 让 AI
|
|
65
|
+
# 让 AI 帮你写代码(单命令模式,自动流式输出)
|
|
57
66
|
gfcode "写一个 Python 的 Hello World"
|
|
67
|
+
gfcode -m Qwen/Qwen2.5-32B-Instruct "重构 src/index.ts"
|
|
58
68
|
|
|
59
|
-
#
|
|
69
|
+
# 解释代码:直接传字符串,或 -f 指定文件
|
|
60
70
|
gfcode explain "const add = (a, b) => a + b;"
|
|
71
|
+
gfcode explain -f src/index.ts
|
|
61
72
|
|
|
62
|
-
#
|
|
73
|
+
# 调试代码:可附加 -e 错误信息
|
|
63
74
|
gfcode debug "你的代码"
|
|
75
|
+
gfcode debug -f src/foo.ts -e "TypeError: x is not a function"
|
|
64
76
|
|
|
65
|
-
#
|
|
77
|
+
# 问答(带当前项目上下文)
|
|
66
78
|
gfcode chat "怎么用 Node.js 写 API?"
|
|
67
79
|
|
|
68
80
|
# 生成代码模板
|
|
69
|
-
gfcode template react -n MyButton
|
|
81
|
+
gfcode template react -n MyButton -o ./components
|
|
70
82
|
```
|
|
71
83
|
|
|
84
|
+
> `--stream` / `--no-auto` / `-m` 接受任意命令并对当次调用生效;`-i` 强制进入 REPL。
|
|
85
|
+
> 详细 flag 见 [`DEVELOPMENT.md`](./DEVELOPMENT.md) 第 5 节。
|
|
86
|
+
|
|
87
|
+
### REPL 内置命令
|
|
88
|
+
|
|
89
|
+
进入交互模式(直接运行 `gfcode`)后,可以输入下列命令(带或不带前导 `/` 都行):
|
|
90
|
+
|
|
91
|
+
| 命令 | 作用 |
|
|
92
|
+
| -------------- | --------------------------------------------------- |
|
|
93
|
+
| `help` | 显示所有内置命令 |
|
|
94
|
+
| `exit` | 退出 REPL |
|
|
95
|
+
| `clear` | 清屏 |
|
|
96
|
+
| `context` | 显示当前项目上下文 |
|
|
97
|
+
| `history` | 显示本次会话的命令历史 |
|
|
98
|
+
| `tools` | 列出已注册的工具(file / shell / git / search) |
|
|
99
|
+
| `models` | 只读地列出当前 provider 的可用模型 |
|
|
100
|
+
| `providers` | 只读地列出支持的 provider |
|
|
101
|
+
| `/model` | **交互式切换当前 provider 下的模型**,立即生效并持久化到 `~/.thatgfsj/config.json` |
|
|
102
|
+
| `/provider` | **交互式切换 provider**,切换后自动跟进选模型 |
|
|
103
|
+
| `Ctrl+C` | 单次:停止当前流式生成;空输入连续两次退出 REPL |
|
|
104
|
+
|
|
105
|
+
#### `/model` / `/provider` 示例
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
> /model
|
|
109
|
+
🤖 /model — 切换模型 (provider: siliconflow)
|
|
110
|
+
|
|
111
|
+
最近使用:
|
|
112
|
+
r1. Qwen/Qwen2.5-32B-Instruct
|
|
113
|
+
r2. Pro/moonshotai/Kimi-K2.5
|
|
114
|
+
|
|
115
|
+
内置模型:
|
|
116
|
+
1. Qwen2.5-7B (推荐) Qwen/Qwen2.5-7B-Instruct ✓
|
|
117
|
+
2. Qwen2.5-32B Qwen/Qwen2.5-32B-Instruct
|
|
118
|
+
3. Qwen2.5-72B Qwen/Qwen2.5-72B-Instruct
|
|
119
|
+
4. Kimi-K2.5 (私有) Pro/moonshotai/Kimi-K2.5
|
|
120
|
+
5. DeepSeek-V3 Pro/deepseek-ai/DeepSeek-V3
|
|
121
|
+
…
|
|
122
|
+
|
|
123
|
+
输入编号 (r1-r2 / 1-10)、完整 model id、或任意自定义 id。回车保持当前。
|
|
124
|
+
model > r2
|
|
125
|
+
✅ 模型已切换为: Pro/moonshotai/Kimi-K2.5 (Pro/moonshotai/Kimi-K2.5)
|
|
126
|
+
|
|
127
|
+
# 也可以直接粘一个自定义 model id(适合 OpenAI-compatible 中转/Ollama):
|
|
128
|
+
model > my-custom-fine-tune
|
|
129
|
+
✅ 模型已切换为: my-custom-fine-tune (自定义)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`/provider` 的工作流类似,但会让你先选 9 个提供商之一(SiliconFlow /
|
|
133
|
+
MiniMax / OpenAI / Anthropic / Gemini / Kimi / DeepSeek / ERNIE / Ollama),
|
|
134
|
+
然后自动跟进模型选择。如果新 provider 缺少对应的环境变量,会提示先
|
|
135
|
+
`gfcode init` 或手动 `export`。
|
|
136
|
+
|
|
137
|
+
切换过的 model 会写入 `~/.thatgfsj/models.json` 历史文件,下次打开
|
|
138
|
+
`/model` 时按时间倒序展示在「最近使用」段。
|
|
139
|
+
|
|
72
140
|
---
|
|
73
141
|
|
|
74
142
|
## ❓ 常见问题
|
|
75
143
|
|
|
76
144
|
### Q: 安装后命令找不到?
|
|
77
145
|
|
|
78
|
-
**A**: 重启终端,然后运行 `gfcode
|
|
146
|
+
**A**: 重启终端,然后运行 `gfcode`。Windows 上确认 PowerShell / CMD 已重新加载 `%PATH%`。
|
|
79
147
|
|
|
80
148
|
### Q: 怎么配置 API Key?
|
|
81
149
|
|
|
82
|
-
**A**: 运行 `gfcode init
|
|
150
|
+
**A**: 运行 `gfcode init`,按提示选择提供商、粘贴 key、挑选模型。也可以直接设置环境变量
|
|
151
|
+
(`SILICONFLOW_API_KEY` / `OPENAI_API_KEY` / `MINIMAX_API_KEY` /
|
|
152
|
+
`ANTHROPIC_API_KEY` / `GEMINI_API_KEY` / `KIMI_API_KEY` / `DEEPSEEK_API_KEY` /
|
|
153
|
+
`ERNIE_API_KEY`),或者把配置写到 `~/.thatgfsj/config.json`。
|
|
83
154
|
|
|
84
155
|
### Q: 哪个 Provider 最好?
|
|
85
156
|
|
|
86
|
-
**A**: 新手推荐 **SiliconFlow
|
|
157
|
+
**A**: 新手推荐 **SiliconFlow**(性价比高,国产),可选用 Qwen2.5 / Kimi-K2.5 / DeepSeek-V3 等模型。
|
|
158
|
+
|
|
159
|
+
### Q: `--version` 显示老版本号?
|
|
160
|
+
|
|
161
|
+
**A**: 升级到 0.2.2+,版本号现在从 `package.json` 动态读取,CI / re-install 都会自动同步。
|
|
162
|
+
|
|
163
|
+
### Q: 启动瞬间报错 `ReferenceError: require is not defined`?
|
|
164
|
+
|
|
165
|
+
**A**: 那是 0.2.1 之前的 bug,0.2.2 已经修复(使用 `createRequire(import.meta.url)` 兼容)。
|
|
166
|
+
|
|
167
|
+
更多问题 → [`docs/FAQ.md`](./docs/FAQ.md)。
|
|
87
168
|
|
|
88
169
|
---
|
|
89
170
|
|
|
90
171
|
## 📚 文档
|
|
91
172
|
|
|
92
|
-
- [
|
|
93
|
-
- [
|
|
173
|
+
- [DEVELOPMENT.md](./DEVELOPMENT.md) — 架构 / 构建 / 扩展 / 发布(贡献者必读)
|
|
174
|
+
- [CHANGELOG.md](./CHANGELOG.md) — 每次版本的变更说明
|
|
175
|
+
- [docs/API_KEY_GUIDE.md](./docs/API_KEY_GUIDE.md) — 9 个 Provider 的 API Key 获取教程
|
|
176
|
+
- [docs/FAQ.md](./docs/FAQ.md) — 详细 FAQ
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## 🧪 本地自检
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
npm install
|
|
184
|
+
npm test # node --test tests/
|
|
185
|
+
npm run build
|
|
186
|
+
node dist/index.js --version
|
|
187
|
+
```
|
|
94
188
|
|
|
95
189
|
---
|
|
96
190
|
|
|
97
191
|
## 🤝 问题反馈
|
|
98
192
|
|
|
99
|
-
有问题?去 GitHub 提 Issue:
|
|
193
|
+
有问题?去 GitHub 提 Issue:
|
|
194
|
+
|
|
100
195
|
https://github.com/Thatgfsj/thatgfsj-code/issues
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## 📜 License
|
|
200
|
+
|
|
201
|
+
MIT © Thatgfsj
|
package/dist/core/ai-engine.d.ts
CHANGED
|
@@ -11,6 +11,16 @@ export declare class AIEngine {
|
|
|
11
11
|
private config;
|
|
12
12
|
private tools;
|
|
13
13
|
constructor(config: AIConfig);
|
|
14
|
+
/**
|
|
15
|
+
* Replace the active configuration at runtime.
|
|
16
|
+
* Used by the REPL `/model` / `/provider` commands — the next streamed
|
|
17
|
+
* request will pick up the new model / baseUrl / provider.
|
|
18
|
+
*/
|
|
19
|
+
updateConfig(config: AIConfig): void;
|
|
20
|
+
/**
|
|
21
|
+
* Inspect a snapshot of the active configuration (read-only).
|
|
22
|
+
*/
|
|
23
|
+
getConfig(): Readonly<AIConfig>;
|
|
14
24
|
/**
|
|
15
25
|
* Register tools for function calling
|
|
16
26
|
*/
|
|
@@ -43,6 +53,13 @@ export declare class AIEngine {
|
|
|
43
53
|
private parseResponse;
|
|
44
54
|
/**
|
|
45
55
|
* Extract tool calls from response content
|
|
56
|
+
*
|
|
57
|
+
* NOTE: 当前为占位实现,实际不会从流式内容中解析工具调用。这意味着
|
|
58
|
+
* `chatStream` 内的 `if (response.tool_calls ...)` 分支永远不会进入,
|
|
59
|
+
* 已注册的 `Tool` 不会被调用,Agent 等价于一个聊天客户端。这是有意为之的
|
|
60
|
+
* 局限性 — 真实工具调用解析需要每个提供商走 JSON mode / native tool_calls
|
|
61
|
+
* 流(chunks 里的 `delta.tool_calls` 已被 `extractChunkText` 丢弃)。
|
|
62
|
+
* 修复此问题需要更大的重构,不属于 0.2.2 patch 的范围。
|
|
46
63
|
*/
|
|
47
64
|
private extractToolCalls;
|
|
48
65
|
/**
|