thatgfsj-code 0.4.1 → 0.6.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.md +7 -12
- package/dist/app/index.d.ts +6 -14
- package/dist/app/index.d.ts.map +1 -1
- package/dist/app/index.js +29 -31
- package/dist/app/index.js.map +1 -1
- package/dist/cmd/index.d.ts +1 -2
- package/dist/cmd/index.d.ts.map +1 -1
- package/dist/cmd/index.js +58 -47
- package/dist/cmd/index.js.map +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +1 -0
- package/dist/config/index.js.map +1 -1
- package/dist/config/types.d.ts +1 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/llm/index.d.ts.map +1 -1
- package/dist/llm/index.js +0 -1
- package/dist/llm/index.js.map +1 -1
- package/dist/tui/app.d.ts +9 -0
- package/dist/tui/app.d.ts.map +1 -0
- package/dist/tui/app.js +40 -0
- package/dist/tui/app.js.map +1 -0
- package/dist/tui/components/ChatList.d.ts +14 -0
- package/dist/tui/components/ChatList.d.ts.map +1 -0
- package/dist/tui/components/ChatList.js +11 -0
- package/dist/tui/components/ChatList.js.map +1 -0
- package/dist/tui/components/ChatMessage.d.ts +14 -0
- package/dist/tui/components/ChatMessage.d.ts.map +1 -0
- package/dist/tui/components/ChatMessage.js +17 -0
- package/dist/tui/components/ChatMessage.js.map +1 -0
- package/dist/tui/components/Header.d.ts +9 -0
- package/dist/tui/components/Header.d.ts.map +1 -0
- package/dist/tui/components/Header.js +6 -0
- package/dist/tui/components/Header.js.map +1 -0
- package/dist/tui/components/Markdown.d.ts +8 -0
- package/dist/tui/components/Markdown.d.ts.map +1 -0
- package/dist/tui/components/Markdown.js +31 -0
- package/dist/tui/components/Markdown.js.map +1 -0
- package/dist/tui/components/Thinking.d.ts +8 -0
- package/dist/tui/components/Thinking.d.ts.map +1 -0
- package/dist/tui/components/Thinking.js +7 -0
- package/dist/tui/components/Thinking.js.map +1 -0
- package/dist/tui/components/ToolCall.d.ts +14 -0
- package/dist/tui/components/ToolCall.d.ts.map +1 -0
- package/dist/tui/components/ToolCall.js +37 -0
- package/dist/tui/components/ToolCall.js.map +1 -0
- package/dist/tui/components/UserInput.d.ts +9 -0
- package/dist/tui/components/UserInput.d.ts.map +1 -0
- package/dist/tui/components/UserInput.js +82 -0
- package/dist/tui/components/UserInput.js.map +1 -0
- package/dist/tui/hooks/useChat.d.ts +11 -0
- package/dist/tui/hooks/useChat.d.ts.map +1 -0
- package/dist/tui/hooks/useChat.js +138 -0
- package/dist/tui/hooks/useChat.js.map +1 -0
- package/dist/tui/hooks/useCommands.d.ts +10 -0
- package/dist/tui/hooks/useCommands.d.ts.map +1 -0
- package/dist/tui/hooks/useCommands.js +51 -0
- package/dist/tui/hooks/useCommands.js.map +1 -0
- package/dist/tui/index.d.ts +2 -4
- package/dist/tui/index.d.ts.map +1 -1
- package/dist/tui/index.js +2 -4
- package/dist/tui/index.js.map +1 -1
- package/dist/tui/output.d.ts +12 -3
- package/dist/tui/output.d.ts.map +1 -1
- package/dist/tui/output.js +43 -47
- package/dist/tui/output.js.map +1 -1
- package/dist/tui/repl.d.ts.map +1 -1
- package/dist/tui/repl.js +42 -28
- package/dist/tui/repl.js.map +1 -1
- package/dist/tui/welcome.d.ts.map +1 -1
- package/dist/tui/welcome.js +23 -5
- package/dist/tui/welcome.js.map +1 -1
- package/install.ps1 +171 -192
- package/package.json +9 -1
- package/src/app/index.ts +29 -34
- package/src/cmd/{index.ts → index.tsx} +52 -47
- package/src/config/index.ts +1 -0
- package/src/config/types.ts +1 -0
- package/src/llm/index.ts +0 -1
- package/src/tui/app.tsx +60 -0
- package/src/tui/components/ChatList.tsx +29 -0
- package/src/tui/components/ChatMessage.tsx +51 -0
- package/src/tui/components/Header.tsx +22 -0
- package/src/tui/components/Markdown.tsx +35 -0
- package/src/tui/components/Thinking.tsx +19 -0
- package/src/tui/components/ToolCall.tsx +72 -0
- package/src/tui/components/UserInput.tsx +101 -0
- package/src/tui/hooks/useChat.ts +154 -0
- package/src/tui/hooks/useCommands.ts +63 -0
- package/src/tui/index.ts +2 -4
- package/src/tui/output.ts +43 -44
- package/src/tui/repl.ts +40 -29
- package/src/tui/welcome.ts +25 -6
- package/tsconfig.json +5 -3
- package/src/app/agent.ts +0 -140
package/install.ps1
CHANGED
|
@@ -1,238 +1,217 @@
|
|
|
1
1
|
# Thatgfsj Code Installer for Windows
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
|
|
6
|
-
param(
|
|
7
|
-
[switch]$NoOpen,
|
|
8
|
-
[switch]$DryRun
|
|
9
|
-
)
|
|
2
|
+
# Works on any Windows machine - auto-downloads Node.js if needed
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# irm https://raw.githubusercontent.com/Thatgfsj/thatgfsj-code/master/install.ps1 | iex
|
|
10
6
|
|
|
11
7
|
$ErrorActionPreference = "Stop"
|
|
12
8
|
|
|
13
|
-
|
|
14
|
-
function
|
|
15
|
-
function
|
|
16
|
-
function
|
|
17
|
-
function Write-Info { param($msg) Write-Host " $msg" -ForegroundColor Gray }
|
|
9
|
+
function Log($msg) { Write-Host " $msg" -ForegroundColor Gray }
|
|
10
|
+
function Step($msg) { Write-Host " > $msg" -ForegroundColor Cyan }
|
|
11
|
+
function Ok($msg) { Write-Host " OK $msg" -ForegroundColor Green }
|
|
12
|
+
function Fail($msg) { Write-Host " !! $msg" -ForegroundColor Red }
|
|
18
13
|
|
|
19
14
|
Write-Host ""
|
|
20
|
-
Write-Host " Thatgfsj Code
|
|
21
|
-
Write-Host "
|
|
15
|
+
Write-Host " Thatgfsj Code Installer" -ForegroundColor Cyan
|
|
16
|
+
Write-Host " ────────────────────────" -ForegroundColor DarkGray
|
|
22
17
|
Write-Host ""
|
|
23
18
|
|
|
24
|
-
|
|
25
|
-
Write-Host "[DRY RUN] 仅显示将要执行的操作" -ForegroundColor Yellow
|
|
26
|
-
Write-Host ""
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
# ============== Step 1: Check PowerShell ==============
|
|
30
|
-
Write-Step "检查 PowerShell 版本..."
|
|
19
|
+
# ── Helper: Refresh PATH from registry ────────────────────
|
|
31
20
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
21
|
+
function Refresh-Path {
|
|
22
|
+
$machinePath = [System.Environment]::GetEnvironmentVariable("Path", "Machine")
|
|
23
|
+
$userPath = [System.Environment]::GetEnvironmentVariable("Path", "User")
|
|
24
|
+
$env:Path = "$machinePath;$userPath"
|
|
36
25
|
}
|
|
37
|
-
Write-Success "PowerShell $($PSVersionTable.PSVersion) 检测正常"
|
|
38
26
|
|
|
39
|
-
#
|
|
40
|
-
Write-Step "检查 Node.js..."
|
|
27
|
+
# ── Helper: Download file with progress ───────────────────
|
|
41
28
|
|
|
42
|
-
function
|
|
29
|
+
function Download-File {
|
|
30
|
+
param($url, $dest)
|
|
31
|
+
Log "Downloading $url ..."
|
|
43
32
|
try {
|
|
44
|
-
$
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
return @{ installed = $false; version = $null; major = 0 }
|
|
33
|
+
$wc = New-Object System.Net.WebClient
|
|
34
|
+
$wc.DownloadFile($url, $dest)
|
|
35
|
+
} catch {
|
|
36
|
+
# Fallback to Invoke-WebRequest
|
|
37
|
+
Invoke-WebRequest -Uri $url -OutFile $dest -UseBasicParsing
|
|
38
|
+
}
|
|
51
39
|
}
|
|
52
40
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
41
|
+
# ── Helper: Install Node.js by downloading binary ─────────
|
|
42
|
+
|
|
43
|
+
function Install-NodeJs-Binary {
|
|
44
|
+
Log "Downloading Node.js LTS binary..."
|
|
45
|
+
|
|
46
|
+
$nodeVersion = "v22.16.0"
|
|
47
|
+
$arch = if ([Environment]::Is64BitOperatingSystem) { "x64" } else { "x86" }
|
|
48
|
+
$filename = "node-$nodeVersion-win-$arch"
|
|
49
|
+
$zipUrl = "https://nodejs.org/dist/$nodeVersion/$filename.zip"
|
|
50
|
+
|
|
51
|
+
$tempDir = Join-Path $env:TEMP "gfcode-node-install"
|
|
52
|
+
$zipFile = Join-Path $tempDir "$filename.zip"
|
|
53
|
+
$extractDir = Join-Path $tempDir $filename
|
|
54
|
+
|
|
55
|
+
# Clean up previous attempts
|
|
56
|
+
if (Test-Path $tempDir) { Remove-Item $tempDir -Recurse -Force }
|
|
57
|
+
New-Item -ItemType Directory -Path $tempDir -Force | Out-Null
|
|
58
|
+
|
|
59
|
+
# Download
|
|
60
|
+
Download-File $zipUrl $zipFile
|
|
61
|
+
|
|
62
|
+
# Extract
|
|
63
|
+
Log "Extracting..."
|
|
64
|
+
Expand-Archive -Path $zipFile -DestinationPath $tempDir -Force
|
|
65
|
+
|
|
66
|
+
# Move to install location
|
|
67
|
+
$installBase = Join-Path $env:USERPROFILE ".gfcode-node"
|
|
68
|
+
if (Test-Path $installBase) { Remove-Item $installBase -Recurse -Force }
|
|
69
|
+
Move-Item $extractDir $installBase
|
|
70
|
+
|
|
71
|
+
# Add to PATH (current session + user env)
|
|
72
|
+
$nodeBin = $installBase
|
|
73
|
+
$env:Path = "$nodeBin;$env:Path"
|
|
74
|
+
|
|
75
|
+
# Persist to user PATH
|
|
76
|
+
$userPath = [System.Environment]::GetEnvironmentVariable("Path", "User")
|
|
77
|
+
if ($userPath -notlike "*$nodeBin*") {
|
|
78
|
+
[System.Environment]::SetEnvironmentVariable("Path", "$nodeBin;$userPath", "User")
|
|
80
79
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
exit 1
|
|
80
|
+
|
|
81
|
+
# Cleanup
|
|
82
|
+
Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue
|
|
83
|
+
|
|
84
|
+
# Verify
|
|
85
|
+
try {
|
|
86
|
+
$ver = & "$nodeBin\node.exe" --version
|
|
87
|
+
Ok "Node.js $ver (portable)"
|
|
88
|
+
return $true
|
|
89
|
+
} catch {
|
|
90
|
+
return $false
|
|
93
91
|
}
|
|
94
92
|
}
|
|
95
93
|
|
|
96
|
-
#
|
|
97
|
-
|
|
94
|
+
# ── Step 1: Check / Install Node.js ───────────────────────
|
|
95
|
+
|
|
96
|
+
Step "Checking Node.js..."
|
|
98
97
|
|
|
99
|
-
$
|
|
98
|
+
$nodeOk = $false
|
|
100
99
|
|
|
101
|
-
if
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
if ($
|
|
107
|
-
|
|
108
|
-
|
|
100
|
+
# Check if node is available
|
|
101
|
+
try {
|
|
102
|
+
$ver = node --version 2>$null
|
|
103
|
+
if ($ver) {
|
|
104
|
+
$major = [int]($ver -replace 'v(\d+)\..*', '$1')
|
|
105
|
+
if ($major -ge 18) {
|
|
106
|
+
Ok "Node.js $ver"
|
|
107
|
+
$nodeOk = $true
|
|
108
|
+
} else {
|
|
109
|
+
Log "Node.js $ver found but too old (need 18+)"
|
|
109
110
|
}
|
|
110
111
|
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
112
|
+
} catch {}
|
|
113
|
+
|
|
114
|
+
if (-not $nodeOk) {
|
|
115
|
+
Log "Node.js 18+ not found."
|
|
116
|
+
|
|
117
|
+
# Try 1: winget
|
|
118
|
+
if (-not $nodeOk -and (Get-Command winget -ErrorAction SilentlyContinue)) {
|
|
119
|
+
Log "Trying winget..."
|
|
120
|
+
try {
|
|
121
|
+
winget install OpenJS.NodeJS.LTS --accept-package-agreements --accept-source-agreements 2>&1 | Out-Null
|
|
122
|
+
Refresh-Path
|
|
123
|
+
$ver = node --version 2>$null
|
|
124
|
+
if ($ver -and [int]($ver -replace 'v(\d+)\..*', '$1') -ge 18) {
|
|
125
|
+
Ok "Node.js $ver via winget"
|
|
126
|
+
$nodeOk = $true
|
|
127
|
+
}
|
|
128
|
+
} catch {}
|
|
116
129
|
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (-not (Test-Path (Join-Path $installDir "package.json"))) {
|
|
120
|
-
Write-Error "安装目录无效: $installDir"
|
|
121
|
-
exit 1
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
Write-Success "代码准备完成: $installDir"
|
|
125
130
|
|
|
126
|
-
#
|
|
127
|
-
|
|
131
|
+
# Try 2: choco
|
|
132
|
+
if (-not $nodeOk -and (Get-Command choco -ErrorAction SilentlyContinue)) {
|
|
133
|
+
Log "Trying Chocolatey..."
|
|
134
|
+
try {
|
|
135
|
+
choco install nodejs-lts -y 2>&1 | Out-Null
|
|
136
|
+
Refresh-Path
|
|
137
|
+
$ver = node --version 2>$null
|
|
138
|
+
if ($ver -and [int]($ver -replace 'v(\d+)\..*', '$1') -ge 18) {
|
|
139
|
+
Ok "Node.js $ver via Chocolatey"
|
|
140
|
+
$nodeOk = $true
|
|
141
|
+
}
|
|
142
|
+
} catch {}
|
|
143
|
+
}
|
|
128
144
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if ($LASTEXITCODE -ne 0) {
|
|
133
|
-
Write-Error "npm install 失败"
|
|
134
|
-
exit 1
|
|
145
|
+
# Try 3: Direct download (works on any machine)
|
|
146
|
+
if (-not $nodeOk) {
|
|
147
|
+
$nodeOk = Install-NodeJs-Binary
|
|
135
148
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
Write-Error "编译失败"
|
|
149
|
+
|
|
150
|
+
if (-not $nodeOk) {
|
|
151
|
+
Fail "Failed to install Node.js"
|
|
152
|
+
Log "Please install manually: https://nodejs.org"
|
|
141
153
|
exit 1
|
|
142
154
|
}
|
|
143
155
|
}
|
|
144
|
-
Write-Success "依赖安装完成"
|
|
145
156
|
|
|
146
|
-
#
|
|
147
|
-
|
|
157
|
+
# ── Step 2: Install gfcode via npm ────────────────────────
|
|
158
|
+
|
|
159
|
+
Step "Installing thatgfsj-code..."
|
|
160
|
+
|
|
161
|
+
$npmPath = Join-Path (Split-Path (Get-Command node).Source) "npm.cmd"
|
|
162
|
+
$npmGlobalPrefix = & node -e "console.log(require('path').resolve(process.execPath, '..','..'))" 2>$null
|
|
163
|
+
$npmBinDir = Join-Path $npmGlobalPrefix "bin"
|
|
148
164
|
|
|
149
|
-
|
|
150
|
-
|
|
165
|
+
# Install globally
|
|
166
|
+
$env:npm_config_prefix = $npmGlobalPrefix
|
|
167
|
+
& node (Join-Path (Split-Path (Get-Command node).Source) "node_modules\npm\bin\npm-cli.js") install -g thatgfsj-code 2>&1 | Out-Null
|
|
168
|
+
|
|
169
|
+
if ($LASTEXITCODE -ne 0) {
|
|
170
|
+
# Fallback: try npm directly
|
|
171
|
+
npm install -g thatgfsj-code 2>&1 | Out-Null
|
|
151
172
|
}
|
|
152
|
-
Write-Success "命令 'gfcode' 已可用"
|
|
153
173
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
$apiKeySet = $false
|
|
159
|
-
$providers = @{
|
|
160
|
-
"1" = @{name="SiliconFlow (推荐)"; var="SILICONFLOW_API_KEY"; url="https://siliconflow.cn"}
|
|
161
|
-
"2" = @{name="MiniMax"; var="MINIMAX_API_KEY"; url="https://platform.minimax.io"}
|
|
162
|
-
"3" = @{name="OpenAI"; var="OPENAI_API_KEY"; url="https://platform.openai.com"}
|
|
163
|
-
"4" = @{name="Anthropic"; var="ANTHROPIC_API_KEY"; url="https://www.anthropic.com"}
|
|
164
|
-
"5" = @{name="Google Gemini"; var="GEMINI_API_KEY"; url="https://aistudio.google.com/app/apikey"}
|
|
165
|
-
"6" = @{name="跳过 (稍后配置)"; var=""; url=""}
|
|
174
|
+
if ($LASTEXITCODE -ne 0) {
|
|
175
|
+
Fail "npm install failed"
|
|
176
|
+
exit 1
|
|
166
177
|
}
|
|
167
178
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
179
|
+
Ok "thatgfsj-code installed"
|
|
180
|
+
|
|
181
|
+
# Make sure gfcode is in PATH for this session
|
|
182
|
+
$npmBin = Join-Path $npmGlobalPrefix "bin"
|
|
183
|
+
if (Test-Path $npmBin) {
|
|
184
|
+
$env:Path = "$npmBin;$env:Path"
|
|
174
185
|
}
|
|
175
|
-
Write-Host ""
|
|
176
186
|
|
|
177
|
-
|
|
178
|
-
|
|
187
|
+
# ── Step 3: Verify ────────────────────────────────────────
|
|
188
|
+
|
|
189
|
+
Step "Verifying..."
|
|
190
|
+
|
|
191
|
+
$gfcodePath = Get-Command gfcode -ErrorAction SilentlyContinue
|
|
192
|
+
if ($gfcodePath) {
|
|
193
|
+
$cliVer = gfcode --version 2>$null
|
|
194
|
+
Ok "gfcode v$cliVer"
|
|
179
195
|
} else {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
if (
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
Write-Host " 获取后粘贴到下方" -ForegroundColor Gray
|
|
188
|
-
Write-Host ""
|
|
189
|
-
|
|
190
|
-
$key = Read-Host " 请输入 API Key (输入后回车)"
|
|
191
|
-
|
|
192
|
-
if ($key) {
|
|
193
|
-
# Save to config file
|
|
194
|
-
$configDir = Join-Path $env:USERPROFILE ".thatgfsj"
|
|
195
|
-
if (-not (Test-Path $configDir)) {
|
|
196
|
-
New-Item -ItemType Directory -Path $configDir -Force | Out-Null
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
$configFile = Join-Path $configDir "config.json"
|
|
200
|
-
$config = @{
|
|
201
|
-
model = "Qwen/Qwen2.5-7B-Instruct"
|
|
202
|
-
apiKey = $key
|
|
203
|
-
provider = "siliconflow"
|
|
204
|
-
temperature = 0.7
|
|
205
|
-
maxTokens = 4096
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
# Set provider-specific defaults
|
|
209
|
-
switch ($choice) {
|
|
210
|
-
"1" { $config.provider = "siliconflow"; $config.model = "Qwen/Qwen2.5-7B-Instruct" }
|
|
211
|
-
"2" { $config.provider = "minimax"; $config.model = "MiniMax-M2.5" }
|
|
212
|
-
"3" { $config.provider = "openai"; $config.model = "gpt-4o-mini" }
|
|
213
|
-
"4" { $config.provider = "anthropic"; $config.model = "claude-3-haiku-20240307" }
|
|
214
|
-
"5" { $config.provider = "gemini"; $config.model = "gemini-1.5-flash-8b" }
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
$config | ConvertTo-Json | Set-Content $configFile -Encoding UTF8
|
|
218
|
-
Write-Success "配置已保存到: $configFile"
|
|
219
|
-
}
|
|
220
|
-
}
|
|
196
|
+
# Try direct path
|
|
197
|
+
$directPath = Join-Path $npmGlobalPrefix "bin\gfcode.cmd"
|
|
198
|
+
if (Test-Path $directPath) {
|
|
199
|
+
Ok "gfcode installed at $directPath"
|
|
200
|
+
Log "You may need to restart your terminal."
|
|
201
|
+
} else {
|
|
202
|
+
Ok "gfcode installed (restart terminal if command not found)"
|
|
221
203
|
}
|
|
222
204
|
}
|
|
223
205
|
|
|
224
|
-
#
|
|
206
|
+
# ── Done ──────────────────────────────────────────────────
|
|
207
|
+
|
|
225
208
|
Write-Host ""
|
|
226
|
-
Write-Host "
|
|
227
|
-
Write-
|
|
228
|
-
Write-Host " ======================================" -ForegroundColor Cyan
|
|
209
|
+
Write-Host " ────────────────────────────────" -ForegroundColor DarkGray
|
|
210
|
+
Write-Host " Done!" -ForegroundColor Green
|
|
229
211
|
Write-Host ""
|
|
230
|
-
Write-Host "
|
|
231
|
-
Write-Host " gfcode init
|
|
232
|
-
Write-Host " gfcode
|
|
233
|
-
Write-Host " gfcode '你的问题' - 直接提问" -ForegroundColor Gray
|
|
234
|
-
Write-Host " gfcode explain '代码' - 解释代码" -ForegroundColor Gray
|
|
235
|
-
Write-Host " gfcode debug '代码' - 调试代码" -ForegroundColor Gray
|
|
212
|
+
Write-Host " Next steps:" -ForegroundColor White
|
|
213
|
+
Write-Host " gfcode init " -ForegroundColor Cyan -NoNewline; Write-Host "# Configure API key" -ForegroundColor DarkGray
|
|
214
|
+
Write-Host " gfcode " -ForegroundColor Cyan -NoNewline; Write-Host "# Start coding" -ForegroundColor DarkGray
|
|
236
215
|
Write-Host ""
|
|
237
|
-
Write-Host "
|
|
216
|
+
Write-Host " Docs: https://github.com/Thatgfsj/thatgfsj-code" -ForegroundColor DarkGray
|
|
238
217
|
Write-Host ""
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thatgfsj-code",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Thatgfsj Code - AI Coding Assistant",
|
|
5
5
|
"main": "dist/cmd/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -25,12 +25,20 @@
|
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"chalk": "^5.3.0",
|
|
27
27
|
"commander": "^11.1.0",
|
|
28
|
+
"ink": "^7.1.0",
|
|
29
|
+
"ink-spinner": "^5.0.0",
|
|
30
|
+
"ink-text-input": "^6.0.0",
|
|
28
31
|
"inquirer": "^13.3.2",
|
|
32
|
+
"marked": "^15.0.12",
|
|
33
|
+
"marked-terminal": "^7.3.0",
|
|
29
34
|
"ora": "^7.0.1",
|
|
35
|
+
"react": "^19.2.7",
|
|
30
36
|
"readline": "^1.3.0"
|
|
31
37
|
},
|
|
32
38
|
"devDependencies": {
|
|
39
|
+
"@types/marked-terminal": "^6.1.1",
|
|
33
40
|
"@types/node": "^20.10.0",
|
|
41
|
+
"@types/react": "^19.2.17",
|
|
34
42
|
"typescript": "^5.3.0"
|
|
35
43
|
}
|
|
36
44
|
}
|
package/src/app/index.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* App - Core application singleton
|
|
3
|
-
*
|
|
4
|
-
* Replaces the scattered initialization in old src/index.ts
|
|
3
|
+
* Simplified: directly uses LLMService (which has built-in agent loop)
|
|
5
4
|
*/
|
|
6
5
|
|
|
7
6
|
import { ConfigManager } from '../config/index.js';
|
|
@@ -10,8 +9,7 @@ import { SessionManager } from '../session/index.js';
|
|
|
10
9
|
import { ToolRegistry } from '../tools/index.js';
|
|
11
10
|
import { HookManager } from '../hooks/index.js';
|
|
12
11
|
import { SystemPromptBuilder } from '../prompts/index.js';
|
|
13
|
-
import {
|
|
14
|
-
import type { ChatMessage } from '../types.js';
|
|
12
|
+
import type { ChatMessage, ChatResponse } from '../types.js';
|
|
15
13
|
|
|
16
14
|
export class App {
|
|
17
15
|
config: ConfigManager;
|
|
@@ -20,7 +18,6 @@ export class App {
|
|
|
20
18
|
tools: ToolRegistry;
|
|
21
19
|
hooks: HookManager;
|
|
22
20
|
prompts: SystemPromptBuilder;
|
|
23
|
-
private agent: Agent;
|
|
24
21
|
|
|
25
22
|
private constructor(
|
|
26
23
|
config: ConfigManager,
|
|
@@ -29,7 +26,6 @@ export class App {
|
|
|
29
26
|
tools: ToolRegistry,
|
|
30
27
|
hooks: HookManager,
|
|
31
28
|
prompts: SystemPromptBuilder,
|
|
32
|
-
agent: Agent,
|
|
33
29
|
) {
|
|
34
30
|
this.config = config;
|
|
35
31
|
this.llm = llm;
|
|
@@ -37,20 +33,14 @@ export class App {
|
|
|
37
33
|
this.tools = tools;
|
|
38
34
|
this.hooks = hooks;
|
|
39
35
|
this.prompts = prompts;
|
|
40
|
-
this.agent = agent;
|
|
41
36
|
}
|
|
42
37
|
|
|
43
|
-
/**
|
|
44
|
-
* Create and initialize the App
|
|
45
|
-
*/
|
|
46
38
|
static async create(): Promise<App> {
|
|
47
|
-
// Load config
|
|
48
39
|
const config = await ConfigManager.load();
|
|
49
40
|
const aiConfig = config.getAIConfig();
|
|
50
41
|
|
|
51
|
-
// Create services
|
|
52
42
|
const llm = LLMService.fromConfig(aiConfig);
|
|
53
|
-
const session = new SessionManager();
|
|
43
|
+
const session = new SessionManager(config.get().contextLength || 50);
|
|
54
44
|
const tools = new ToolRegistry();
|
|
55
45
|
const hooks = new HookManager();
|
|
56
46
|
const prompts = new SystemPromptBuilder({
|
|
@@ -59,13 +49,24 @@ export class App {
|
|
|
59
49
|
permissionMode: 'ask',
|
|
60
50
|
});
|
|
61
51
|
|
|
62
|
-
//
|
|
63
|
-
|
|
52
|
+
// Register tools with LLM service
|
|
53
|
+
llm.registerTools(tools.list());
|
|
64
54
|
|
|
65
55
|
// Set up system prompt
|
|
66
56
|
session.addMessage('system', prompts.build());
|
|
67
57
|
|
|
68
|
-
return new App(config, llm, session, tools, hooks, prompts
|
|
58
|
+
return new App(config, llm, session, tools, hooks, prompts);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Stream a response for the current session messages.
|
|
63
|
+
* The LLMService handles the full agent loop internally.
|
|
64
|
+
*/
|
|
65
|
+
async *streamResponse(messages?: ChatMessage[]): AsyncGenerator<string, void> {
|
|
66
|
+
const msgs = messages || this.session.getMessages();
|
|
67
|
+
for await (const chunk of this.llm.chatStream(msgs)) {
|
|
68
|
+
yield chunk;
|
|
69
|
+
}
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
/**
|
|
@@ -75,27 +76,21 @@ export class App {
|
|
|
75
76
|
this.session.addMessage('user', prompt);
|
|
76
77
|
|
|
77
78
|
let fullResponse = '';
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
try {
|
|
80
|
+
for await (const chunk of this.streamResponse()) {
|
|
81
|
+
process.stdout.write(chunk);
|
|
82
|
+
fullResponse += chunk;
|
|
83
|
+
}
|
|
84
|
+
} catch (err) {
|
|
85
|
+
// Re-throw after saving partial response
|
|
86
|
+
if (fullResponse) {
|
|
87
|
+
this.session.addMessage('assistant', fullResponse);
|
|
88
|
+
}
|
|
89
|
+
throw err;
|
|
81
90
|
}
|
|
82
91
|
|
|
83
|
-
console.log();
|
|
92
|
+
console.log();
|
|
84
93
|
this.session.addMessage('assistant', fullResponse);
|
|
85
94
|
return fullResponse;
|
|
86
95
|
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Get the agent for streaming control
|
|
90
|
-
*/
|
|
91
|
-
getAgent(): Agent {
|
|
92
|
-
return this.agent;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Get current config
|
|
97
|
-
*/
|
|
98
|
-
getConfig(): ConfigManager {
|
|
99
|
-
return this.config;
|
|
100
|
-
}
|
|
101
96
|
}
|