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.
Files changed (94) hide show
  1. package/README.md +7 -12
  2. package/dist/app/index.d.ts +6 -14
  3. package/dist/app/index.d.ts.map +1 -1
  4. package/dist/app/index.js +29 -31
  5. package/dist/app/index.js.map +1 -1
  6. package/dist/cmd/index.d.ts +1 -2
  7. package/dist/cmd/index.d.ts.map +1 -1
  8. package/dist/cmd/index.js +58 -47
  9. package/dist/cmd/index.js.map +1 -1
  10. package/dist/config/index.d.ts.map +1 -1
  11. package/dist/config/index.js +1 -0
  12. package/dist/config/index.js.map +1 -1
  13. package/dist/config/types.d.ts +1 -0
  14. package/dist/config/types.d.ts.map +1 -1
  15. package/dist/llm/index.d.ts.map +1 -1
  16. package/dist/llm/index.js +0 -1
  17. package/dist/llm/index.js.map +1 -1
  18. package/dist/tui/app.d.ts +9 -0
  19. package/dist/tui/app.d.ts.map +1 -0
  20. package/dist/tui/app.js +40 -0
  21. package/dist/tui/app.js.map +1 -0
  22. package/dist/tui/components/ChatList.d.ts +14 -0
  23. package/dist/tui/components/ChatList.d.ts.map +1 -0
  24. package/dist/tui/components/ChatList.js +11 -0
  25. package/dist/tui/components/ChatList.js.map +1 -0
  26. package/dist/tui/components/ChatMessage.d.ts +14 -0
  27. package/dist/tui/components/ChatMessage.d.ts.map +1 -0
  28. package/dist/tui/components/ChatMessage.js +17 -0
  29. package/dist/tui/components/ChatMessage.js.map +1 -0
  30. package/dist/tui/components/Header.d.ts +9 -0
  31. package/dist/tui/components/Header.d.ts.map +1 -0
  32. package/dist/tui/components/Header.js +6 -0
  33. package/dist/tui/components/Header.js.map +1 -0
  34. package/dist/tui/components/Markdown.d.ts +8 -0
  35. package/dist/tui/components/Markdown.d.ts.map +1 -0
  36. package/dist/tui/components/Markdown.js +31 -0
  37. package/dist/tui/components/Markdown.js.map +1 -0
  38. package/dist/tui/components/Thinking.d.ts +8 -0
  39. package/dist/tui/components/Thinking.d.ts.map +1 -0
  40. package/dist/tui/components/Thinking.js +7 -0
  41. package/dist/tui/components/Thinking.js.map +1 -0
  42. package/dist/tui/components/ToolCall.d.ts +14 -0
  43. package/dist/tui/components/ToolCall.d.ts.map +1 -0
  44. package/dist/tui/components/ToolCall.js +37 -0
  45. package/dist/tui/components/ToolCall.js.map +1 -0
  46. package/dist/tui/components/UserInput.d.ts +9 -0
  47. package/dist/tui/components/UserInput.d.ts.map +1 -0
  48. package/dist/tui/components/UserInput.js +82 -0
  49. package/dist/tui/components/UserInput.js.map +1 -0
  50. package/dist/tui/hooks/useChat.d.ts +11 -0
  51. package/dist/tui/hooks/useChat.d.ts.map +1 -0
  52. package/dist/tui/hooks/useChat.js +138 -0
  53. package/dist/tui/hooks/useChat.js.map +1 -0
  54. package/dist/tui/hooks/useCommands.d.ts +10 -0
  55. package/dist/tui/hooks/useCommands.d.ts.map +1 -0
  56. package/dist/tui/hooks/useCommands.js +51 -0
  57. package/dist/tui/hooks/useCommands.js.map +1 -0
  58. package/dist/tui/index.d.ts +2 -4
  59. package/dist/tui/index.d.ts.map +1 -1
  60. package/dist/tui/index.js +2 -4
  61. package/dist/tui/index.js.map +1 -1
  62. package/dist/tui/output.d.ts +12 -3
  63. package/dist/tui/output.d.ts.map +1 -1
  64. package/dist/tui/output.js +43 -47
  65. package/dist/tui/output.js.map +1 -1
  66. package/dist/tui/repl.d.ts.map +1 -1
  67. package/dist/tui/repl.js +42 -28
  68. package/dist/tui/repl.js.map +1 -1
  69. package/dist/tui/welcome.d.ts.map +1 -1
  70. package/dist/tui/welcome.js +23 -5
  71. package/dist/tui/welcome.js.map +1 -1
  72. package/install.ps1 +171 -192
  73. package/package.json +9 -1
  74. package/src/app/index.ts +29 -34
  75. package/src/cmd/{index.ts → index.tsx} +52 -47
  76. package/src/config/index.ts +1 -0
  77. package/src/config/types.ts +1 -0
  78. package/src/llm/index.ts +0 -1
  79. package/src/tui/app.tsx +60 -0
  80. package/src/tui/components/ChatList.tsx +29 -0
  81. package/src/tui/components/ChatMessage.tsx +51 -0
  82. package/src/tui/components/Header.tsx +22 -0
  83. package/src/tui/components/Markdown.tsx +35 -0
  84. package/src/tui/components/Thinking.tsx +19 -0
  85. package/src/tui/components/ToolCall.tsx +72 -0
  86. package/src/tui/components/UserInput.tsx +101 -0
  87. package/src/tui/hooks/useChat.ts +154 -0
  88. package/src/tui/hooks/useCommands.ts +63 -0
  89. package/src/tui/index.ts +2 -4
  90. package/src/tui/output.ts +43 -44
  91. package/src/tui/repl.ts +40 -29
  92. package/src/tui/welcome.ts +25 -6
  93. package/tsconfig.json +5 -3
  94. package/src/app/agent.ts +0 -140
package/install.ps1 CHANGED
@@ -1,238 +1,217 @@
1
1
  # Thatgfsj Code Installer for Windows
2
- # Usage:
3
- # powershell -c "irm https://raw.githubusercontent.com/Thatgfsj/thatgfsj-code/main/install.ps1 | iex"
4
- # powershell -c "& ([scriptblock]::Create((irm https://raw.githubusercontent.com/Thatgfsj/thatgfsj-code/main/install.ps1)))"
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
- # Colors
14
- function Write-Step { param($msg) Write-Host "[*] $msg" -ForegroundColor Yellow }
15
- function Write-Success { param($msg) Write-Host "[✓] $msg" -ForegroundColor Green }
16
- function Write-Error { param($msg) Write-Host "[✗] $msg" -ForegroundColor Red }
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 安装向导" -ForegroundColor Cyan
21
- Write-Host " =======================" -ForegroundColor Cyan
15
+ Write-Host " Thatgfsj Code Installer" -ForegroundColor Cyan
16
+ Write-Host " ────────────────────────" -ForegroundColor DarkGray
22
17
  Write-Host ""
23
18
 
24
- if ($DryRun) {
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
- if ($PSVersionTable.PSVersion.Major -lt 5) {
33
- Write-Error "需要 PowerShell 5.0 或更高版本"
34
- Write-Host "请升级您的 PowerShell: https://aka.ms/powershell" -ForegroundColor Gray
35
- exit 1
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
- # ============== Step 2: Check/Fetch Node.js ==============
40
- Write-Step "检查 Node.js..."
27
+ # ── Helper: Download file with progress ───────────────────
41
28
 
42
- function Test-NodeInstalled {
29
+ function Download-File {
30
+ param($url, $dest)
31
+ Log "Downloading $url ..."
43
32
  try {
44
- $nodeVersion = node --version 2>$null
45
- if ($nodeVersion) {
46
- $version = [int]($nodeVersion -replace 'v(\d+)\..*', '$1')
47
- return @{ installed = $true; version = $nodeVersion; major = $version }
48
- }
49
- } catch {}
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
- $nodeStatus = Test-NodeInstalled
54
- if ($nodeStatus.installed -and $nodeStatus.major -ge 18) {
55
- Write-Success "Node.js $($nodeStatus.version) 已安装"
56
- } else {
57
- Write-Host " 未检测到 Node.js 18+,开始安装..." -ForegroundColor Gray
58
-
59
- # Try winget first
60
- if (Get-Command winget -ErrorAction SilentlyContinue) {
61
- Write-Info "使用 winget 安装..."
62
- winget install OpenJS.NodeJS.LTS --source winget --accept-package-agreements --accept-source-agreements
63
-
64
- # Refresh PATH
65
- $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
66
-
67
- $nodeStatus = Test-NodeInstalled
68
- if ($nodeStatus.installed) {
69
- Write-Success "Node.js 安装完成"
70
- } else {
71
- Write-Host " 需要重启 PowerShell 后重新运行安装" -ForegroundColor Yellow
72
- exit 0
73
- }
74
- }
75
- # Try choco
76
- elseif (Get-Command choco -ErrorAction SilentlyContinue) {
77
- Write-Info "使用 Chocolatey 安装..."
78
- choco install nodejs-lts -y
79
- Write-Success "Node.js 安装完成 (可能需要重启)"
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
- # Manual fallback
82
- else {
83
- Write-Host ""
84
- Write-Error "未检测到包管理器 (winget/choco/scoop)"
85
- Write-Host ""
86
- Write-Host "请手动安装 Node.js:" -ForegroundColor Yellow
87
- Write-Host " 1. 访问 https://nodejs.org" -ForegroundColor Gray
88
- Write-Host " 2. 下载 LTS 版本 (推荐 v20 或 v22)" -ForegroundColor Gray
89
- Write-Host " 3. 运行安装程序" -ForegroundColor Gray
90
- Write-Host " 4. 重新运行此安装脚本" -ForegroundColor Gray
91
- Write-Host ""
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
- # ============== Step 3: Clone/Update Repository ==============
97
- Write-Step "准备安装 Thatgfsj Code..."
94
+ # ── Step 1: Check / Install Node.js ───────────────────────
95
+
96
+ Step "Checking Node.js..."
98
97
 
99
- $installDir = Join-Path $env:USERPROFILE "thatgfsj-code"
98
+ $nodeOk = $false
100
99
 
101
- if (Test-Path $installDir) {
102
- Write-Info "检测到已有安装,正在更新..."
103
- if (-not $DryRun) {
104
- Set-Location $installDir
105
- git pull origin main 2>$null
106
- if ($LASTEXITCODE -ne 0) {
107
- # If pull fails, re-clone
108
- Remove-Item $installDir -Recurse -Force
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
- } else {
112
- if (-not $DryRun) {
113
- Write-Info "正在克隆仓库..."
114
- git clone https://github.com/Thatgfsj/thatgfsj-code.git $installDir
115
- Set-Location $installDir
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
- # ============== Step 4: Install Dependencies ==============
127
- Write-Step "安装依赖..."
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
- if (-not $DryRun) {
130
- Set-Location $installDir
131
- npm install
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
- Write-Info "编译 TypeScript..."
138
- npm run build
139
- if ($LASTEXITCODE -ne 0) {
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
- # ============== Step 5: Link Command ==============
147
- Write-Step "设置命令..."
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
- if (-not $DryRun) {
150
- npm link
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
- # ============== Step 6: Setup API Key ==============
155
- Write-Host ""
156
- Write-Step "配置 API Key (可选)..."
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
- Write-Host ""
169
- Write-Host " 选择 AI Provider:" -ForegroundColor White
170
- Write-Host ""
171
- foreach ($key in $providers.Keys | Sort-Object) {
172
- $p = $providers[$key]
173
- Write-Host " $key. $($p.name)" -ForegroundColor Gray
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
- if ($NoOpen) {
178
- Write-Host " 使用 --NoOpen 跳过配置" -ForegroundColor Gray
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
- $choice = Read-Host " 请选择 (1-6, 直接回车跳过)"
181
-
182
- if ($choice -and $providers.ContainsKey($choice)) {
183
- $selected = $providers[$choice]
184
- if ($selected.var) {
185
- Write-Host ""
186
- Write-Host " 访问 $($selected.url) 获取 API Key" -ForegroundColor Cyan
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
- # ============== Done ==============
206
+ # ── Done ──────────────────────────────────────────────────
207
+
225
208
  Write-Host ""
226
- Write-Host " ======================================" -ForegroundColor Cyan
227
- Write-Success " 安装完成!"
228
- Write-Host " ======================================" -ForegroundColor Cyan
209
+ Write-Host " ────────────────────────────────" -ForegroundColor DarkGray
210
+ Write-Host " Done!" -ForegroundColor Green
229
211
  Write-Host ""
230
- Write-Host " 使用方法:" -ForegroundColor White
231
- Write-Host " gfcode init - 重新配置" -ForegroundColor Gray
232
- Write-Host " gfcode - 启动交互模式" -ForegroundColor Gray
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 " 文档: https://github.com/Thatgfsj/thatgfsj-code" -ForegroundColor Gray
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.4.1",
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
- * Assembles all services: config, LLM, session, tools, hooks, prompts
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 { Agent } from './agent.js';
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
- // Create agent
63
- const agent = new Agent(llm, tools, { hooks, maxIterations: 10 });
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, agent);
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
- for await (const chunk of this.agent.run(this.session.getMessages())) {
79
- process.stdout.write(chunk);
80
- fullResponse += chunk;
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(); // newline
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
  }