thatgfsj-code 0.4.0 → 0.5.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 (53) hide show
  1. package/dist/app/index.d.ts +6 -14
  2. package/dist/app/index.d.ts.map +1 -1
  3. package/dist/app/index.js +28 -30
  4. package/dist/app/index.js.map +1 -1
  5. package/dist/cmd/index.js +64 -8
  6. package/dist/cmd/index.js.map +1 -1
  7. package/dist/llm/anthropic.d.ts +4 -17
  8. package/dist/llm/anthropic.d.ts.map +1 -1
  9. package/dist/llm/anthropic.js +54 -12
  10. package/dist/llm/anthropic.js.map +1 -1
  11. package/dist/llm/gemini.d.ts +3 -16
  12. package/dist/llm/gemini.d.ts.map +1 -1
  13. package/dist/llm/gemini.js +26 -22
  14. package/dist/llm/gemini.js.map +1 -1
  15. package/dist/llm/index.d.ts +5 -19
  16. package/dist/llm/index.d.ts.map +1 -1
  17. package/dist/llm/index.js +86 -87
  18. package/dist/llm/index.js.map +1 -1
  19. package/dist/llm/openai.d.ts +9 -23
  20. package/dist/llm/openai.d.ts.map +1 -1
  21. package/dist/llm/openai.js +48 -14
  22. package/dist/llm/openai.js.map +1 -1
  23. package/dist/llm/provider.d.ts +11 -16
  24. package/dist/llm/provider.d.ts.map +1 -1
  25. package/dist/tui/input.d.ts +0 -1
  26. package/dist/tui/input.d.ts.map +1 -1
  27. package/dist/tui/input.js +2 -5
  28. package/dist/tui/input.js.map +1 -1
  29. package/dist/tui/output.d.ts +36 -12
  30. package/dist/tui/output.d.ts.map +1 -1
  31. package/dist/tui/output.js +172 -40
  32. package/dist/tui/output.js.map +1 -1
  33. package/dist/tui/repl.d.ts +0 -10
  34. package/dist/tui/repl.d.ts.map +1 -1
  35. package/dist/tui/repl.js +91 -43
  36. package/dist/tui/repl.js.map +1 -1
  37. package/dist/tui/welcome.d.ts +1 -11
  38. package/dist/tui/welcome.d.ts.map +1 -1
  39. package/dist/tui/welcome.js +60 -56
  40. package/dist/tui/welcome.js.map +1 -1
  41. package/package.json +1 -1
  42. package/src/app/index.ts +28 -33
  43. package/src/cmd/index.ts +53 -8
  44. package/src/llm/anthropic.ts +60 -14
  45. package/src/llm/gemini.ts +31 -24
  46. package/src/llm/index.ts +93 -92
  47. package/src/llm/openai.ts +58 -15
  48. package/src/llm/provider.ts +12 -16
  49. package/src/tui/input.ts +2 -5
  50. package/src/tui/output.ts +192 -40
  51. package/src/tui/repl.ts +94 -44
  52. package/src/tui/welcome.ts +62 -59
  53. package/src/app/agent.ts +0 -140
@@ -1,6 +1,5 @@
1
1
  /**
2
- * Welcome / Setup Wizard
3
- * Supports all providers + custom relay stations (中转站)
2
+ * Welcome / Setup Wizard - Clean UI
4
3
  */
5
4
  import chalk from 'chalk';
6
5
  import readline from 'readline';
@@ -8,103 +7,108 @@ import { existsSync, mkdirSync, writeFileSync } from 'fs';
8
7
  import { join } from 'path';
9
8
  import { homedir } from 'os';
10
9
  import { getModelsForProvider, listProviders, isCustomProvider } from '../config/providers.js';
10
+ const line = chalk.gray('─'.repeat(52));
11
11
  export class WelcomeScreen {
12
- /**
13
- * Show welcome if no API key is configured
14
- */
15
12
  static show(hasApiKey) {
16
13
  if (hasApiKey)
17
14
  return;
18
- const w = 62;
19
- console.log(chalk.cyan('+') + chalk.white.bold(' Thatgfsj Code ') + chalk.cyan('-'.repeat(w - 16)) + '+');
20
- console.log(chalk.cyan('|') + chalk.yellow(' 快速开始指南').padEnd(w) + chalk.cyan('|'));
21
- console.log(chalk.cyan('|') + ' '.repeat(w) + chalk.cyan('|'));
22
- console.log(chalk.cyan('|') + chalk.gray(' 运行 gfcode init 配置你的 API Key').padEnd(w) + chalk.cyan('|'));
23
- console.log(chalk.cyan('|') + chalk.gray(' 支持: OpenAI / Claude / DeepSeek / Kimi / 智谱 / 中转站').padEnd(w) + chalk.cyan('|'));
24
- console.log(chalk.cyan('+') + '-'.repeat(w) + '+');
15
+ console.log();
16
+ console.log(chalk.cyan.bold(' Thatgfsj Code') + chalk.gray(' v0.5.0'));
17
+ console.log(chalk.gray(' AI Coding Assistant'));
18
+ console.log(line);
19
+ console.log();
20
+ console.log(chalk.yellow(' ⚠ No API key configured'));
21
+ console.log();
22
+ console.log(chalk.gray(' Run ') + chalk.cyan.bold('gfcode init') + chalk.gray(' to set up your provider.'));
23
+ console.log();
24
+ console.log(chalk.gray(' Supported providers:'));
25
+ console.log(chalk.gray(' OpenAI · Claude · DeepSeek · Kimi · GLM · Gemini · 中转站'));
26
+ console.log(line);
25
27
  console.log();
26
28
  }
27
- /**
28
- * Interactive setup wizard
29
- */
30
29
  static async interactiveSetup() {
31
30
  console.clear();
32
- console.log(chalk.cyan('\n 🤖 Thatgfsj Code 配置向导\n'));
31
+ console.log();
32
+ console.log(chalk.cyan.bold(' ⚡ Thatgfsj Code') + chalk.gray(' - Setup'));
33
+ console.log(line);
34
+ console.log();
33
35
  const rl = readline.createInterface({
34
36
  input: process.stdin,
35
37
  output: process.stdout,
36
38
  });
37
- const question = (prompt) => new Promise(resolve => rl.question(prompt, answer => resolve(answer.trim())));
39
+ const ask = (prompt) => new Promise(resolve => rl.question(prompt, answer => resolve(answer.trim())));
38
40
  try {
39
- // Step 1: Choose provider
40
- console.log(chalk.white(' 步骤 1/3: 选择 AI 提供商\n'));
41
+ // ── Step 1: Provider ────────────────────────────────
42
+ console.log(chalk.bold(' 1. Choose Provider'));
43
+ console.log();
41
44
  const providers = listProviders();
42
45
  providers.forEach((p, i) => {
43
- const num = (i + 1).toString().padStart(2);
44
- console.log(chalk.green(` ${num}. ${p.name}`));
46
+ const num = chalk.cyan((i + 1).toString().padStart(2));
47
+ console.log(` ${num} ${p.name}`);
45
48
  });
46
49
  console.log();
47
- const choice = await question(chalk.green(' 请选择 (1-' + providers.length + '): '));
50
+ const choice = await ask(chalk.cyan(' '));
48
51
  const selected = providers[parseInt(choice) - 1] || providers[0];
49
52
  const providerName = selected.key;
50
- // Step 2: Enter API Key (and base URL for custom providers)
51
- console.log(chalk.white('\n 步骤 2/3: 配置连接信息\n'));
53
+ // ── Step 2: API Key ─────────────────────────────────
54
+ console.log();
55
+ console.log(chalk.bold(' 2. API Key'));
56
+ console.log();
52
57
  let baseUrl;
53
58
  if (isCustomProvider(providerName)) {
54
- // Custom provider needs base URL
55
- console.log(chalk.yellow(' 自定义 Provider (中转站) 模式'));
56
- console.log(chalk.gray(' 支持 OpenAI 兼容 或 Anthropic 兼容 的中转站'));
59
+ console.log(chalk.yellow(' Custom Provider (Relay Station)'));
60
+ console.log(chalk.gray(' OpenAI-compatible or Anthropic-compatible'));
57
61
  console.log();
58
- baseUrl = await question(chalk.green(' 请输入中转站 Base URL: '));
62
+ baseUrl = await ask(chalk.cyan(' Base URL '));
59
63
  if (!baseUrl) {
60
- console.log(chalk.red(' 中转站必须提供 Base URL'));
64
+ console.log(chalk.red(' ❌ Base URL required'));
61
65
  rl.close();
62
66
  return;
63
67
  }
64
- // Remove trailing slash
65
68
  baseUrl = baseUrl.replace(/\/+$/, '');
66
- // Remove /v1 suffix if present (we'll add it in the provider)
67
- // Actually keep it as-is since the user knows their endpoint
68
69
  }
69
- const apiKey = await question(chalk.green(' 请输入 API Key: '));
70
- // Step 3: Choose model
71
- console.log(chalk.white('\n 步骤 3/3: 选择模型\n'));
70
+ const apiKey = await ask(chalk.cyan(' API Key '));
71
+ // ── Step 3: Model ───────────────────────────────────
72
+ console.log();
73
+ console.log(chalk.bold(' 3. Choose Model'));
74
+ console.log();
72
75
  if (isCustomProvider(providerName)) {
73
- // Custom provider: let user type model name
74
- console.log(chalk.gray(' 输入中转站支持的模型名称'));
75
- console.log(chalk.gray(' 例如: gpt-4o-mini, claude-sonnet-4-20250514, deepseek-chat'));
76
- const model = await question(chalk.green(' 模型名称: '));
76
+ console.log(chalk.gray(' Enter model name for your relay station'));
77
+ console.log(chalk.gray(' e.g. gpt-4o-mini, claude-sonnet-4-20250514'));
78
+ console.log();
79
+ const model = await ask(chalk.cyan(' Model '));
77
80
  if (!model) {
78
- console.log(chalk.red(' 必须指定模型'));
81
+ console.log(chalk.red(' Model name required'));
79
82
  rl.close();
80
83
  return;
81
84
  }
82
- // Save config
83
85
  this.saveConfig(providerName, model, apiKey, baseUrl);
84
86
  }
85
87
  else {
86
- // Standard provider: show model list
87
88
  const models = getModelsForProvider(providerName);
88
89
  models.forEach((m, i) => {
89
- const num = (i + 1).toString().padStart(2);
90
- console.log(chalk.green(` ${num}. ${m.name}`) + chalk.gray(` - ${m.desc}`));
90
+ const num = chalk.cyan((i + 1).toString().padStart(2));
91
+ console.log(` ${num} ${chalk.white(m.name)} ${chalk.gray(m.desc)}`);
91
92
  });
92
93
  console.log();
93
- const modelChoice = await question(chalk.green(' 请选择模型 (1-' + models.length + '): '));
94
+ const modelChoice = await ask(chalk.cyan(' '));
94
95
  const selectedModel = models[parseInt(modelChoice) - 1] || models[0];
95
96
  this.saveConfig(providerName, selectedModel.id, apiKey, baseUrl);
96
97
  }
97
- console.log(chalk.green('\n ✅ 配置已保存!'));
98
- console.log(chalk.gray(` 配置文件: ${join(homedir(), '.thatgfsj', 'config.json')}`));
99
- console.log(chalk.cyan('\n 运行 gfcode 开始使用\n'));
98
+ // ── Done ────────────────────────────────────────────
99
+ console.log();
100
+ console.log(line);
101
+ console.log(chalk.green.bold(' ✅ Configuration saved!'));
102
+ console.log();
103
+ console.log(chalk.gray(' Config: ') + chalk.white(join(homedir(), '.thatgfsj', 'config.json')));
104
+ console.log(chalk.gray(' Run ') + chalk.cyan.bold('gfcode') + chalk.gray(' to start.'));
105
+ console.log(line);
106
+ console.log();
100
107
  }
101
108
  finally {
102
109
  rl.close();
103
110
  }
104
111
  }
105
- /**
106
- * Save config to file
107
- */
108
112
  static saveConfig(provider, model, apiKey, baseUrl) {
109
113
  const configDir = join(homedir(), '.thatgfsj');
110
114
  const configPath = join(configDir, 'config.json');
@@ -118,14 +122,14 @@ export class WelcomeScreen {
118
122
  temperature: 0.7,
119
123
  maxTokens: 4096,
120
124
  };
121
- if (baseUrl) {
125
+ if (baseUrl)
122
126
  config.baseUrl = baseUrl;
123
- }
124
127
  writeFileSync(configPath, JSON.stringify(config, null, 2));
125
- console.log(chalk.gray(` 提供商: ${provider}`));
126
- console.log(chalk.gray(` 模型: ${model}`));
128
+ console.log();
129
+ console.log(chalk.gray(' Provider: ') + chalk.white(provider));
130
+ console.log(chalk.gray(' Model: ') + chalk.white(model));
127
131
  if (baseUrl) {
128
- console.log(chalk.gray(` Base URL: ${baseUrl}`));
132
+ console.log(chalk.gray(' URL: ') + chalk.white(baseUrl));
129
133
  }
130
134
  }
131
135
  }
@@ -1 +1 @@
1
- {"version":3,"file":"welcome.js","sourceRoot":"","sources":["../../src/tui/welcome.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,QAAQ,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAC1D,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAa,oBAAoB,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG1G,MAAM,OAAO,aAAa;IACxB;;OAEG;IACH,MAAM,CAAC,IAAI,CAAC,SAAkB;QAC5B,IAAI,SAAS;YAAE,OAAO;QAEtB,MAAM,CAAC,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;QAC1G,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QACnF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC/D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QACtG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,mDAAmD,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3H,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;QACnD,OAAO,CAAC,GAAG,EAAE,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,KAAK,CAAC,gBAAgB;QAC3B,OAAO,CAAC,KAAK,EAAE,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC,CAAC;QAEvD,MAAM,EAAE,GAAG,QAAQ,CAAC,eAAe,CAAC;YAClC,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,CAAC,MAAc,EAAmB,EAAE,CACnD,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAEhF,IAAI,CAAC;YACH,0BAA0B;YAC1B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC,CAAC;YAClD,MAAM,SAAS,GAAG,aAAa,EAAE,CAAC;YAClC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;gBACzB,MAAM,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;gBAC3C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YAClD,CAAC,CAAC,CAAC;YACH,OAAO,CAAC,GAAG,EAAE,CAAC;YAEd,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,GAAG,SAAS,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC;YACnF,MAAM,QAAQ,GAAG,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC;YACjE,MAAM,YAAY,GAAG,QAAQ,CAAC,GAAG,CAAC;YAElC,4DAA4D;YAC5D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC,CAAC;YAEjD,IAAI,OAA2B,CAAC;YAEhC,IAAI,gBAAgB,CAAC,YAAY,CAAC,EAAE,CAAC;gBACnC,iCAAiC;gBACjC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,yBAAyB,CAAC,CAAC,CAAC;gBACrD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC,CAAC;gBAC9D,OAAO,CAAC,GAAG,EAAE,CAAC;gBACd,OAAO,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC,CAAC;gBAC7D,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC,CAAC;oBAC/C,EAAE,CAAC,KAAK,EAAE,CAAC;oBACX,OAAO;gBACT,CAAC;gBACD,wBAAwB;gBACxB,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;gBACtC,8DAA8D;gBAC9D,6DAA6D;YAC/D,CAAC;YAED,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC;YAE9D,uBAAuB;YACvB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,CAAC;YAE/C,IAAI,gBAAgB,CAAC,YAAY,CAAC,EAAE,CAAC;gBACnC,4CAA4C;gBAC5C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC;gBAC1C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,4DAA4D,CAAC,CAAC,CAAC;gBACtF,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;gBACtD,IAAI,CAAC,KAAK,EAAE,CAAC;oBACX,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC;oBACrC,EAAE,CAAC,KAAK,EAAE,CAAC;oBACX,OAAO;gBACT,CAAC;gBAED,cAAc;gBACd,IAAI,CAAC,UAAU,CAAC,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;YACxD,CAAC;iBAAM,CAAC;gBACN,qCAAqC;gBACrC,MAAM,MAAM,GAAG,oBAAoB,CAAC,YAAY,CAAC,CAAC;gBAClD,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;oBACtB,MAAM,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;oBAC3C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;gBAC/E,CAAC,CAAC,CAAC;gBACH,OAAO,CAAC,GAAG,EAAE,CAAC;gBAEd,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,aAAa,GAAG,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC;gBACvF,MAAM,aAAa,GAAG,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC;gBAErE,IAAI,CAAC,UAAU,CAAC,YAAY,EAAE,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;YACnE,CAAC;YAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC;YACzC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC;YAClF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC,CAAC;QAElD,CAAC;gBAAS,CAAC;YACT,EAAE,CAAC,KAAK,EAAE,CAAC;QACb,CAAC;IACH,CAAC;IAED;;OAEG;IACK,MAAM,CAAC,UAAU,CAAC,QAAsB,EAAE,KAAa,EAAE,MAAc,EAAE,OAAgB;QAC/F,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC;QAC/C,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;QAElD,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC3B,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5C,CAAC;QAED,MAAM,MAAM,GAAwB;YAClC,QAAQ;YACR,KAAK;YACL,MAAM;YACN,WAAW,EAAE,GAAG;YAChB,SAAS,EAAE,IAAI;SAChB,CAAC;QAEF,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC;QAC3B,CAAC;QAED,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAE3D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,QAAQ,EAAE,CAAC,CAAC,CAAC;QAC9C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,KAAK,EAAE,CAAC,CAAC,CAAC;QAC1C,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,eAAe,OAAO,EAAE,CAAC,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;CACF"}
1
+ {"version":3,"file":"welcome.js","sourceRoot":"","sources":["../../src/tui/welcome.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thatgfsj-code",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Thatgfsj Code - AI Coding Assistant",
5
5
  "main": "dist/cmd/index.js",
6
6
  "type": "module",
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,18 +33,12 @@ 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
43
  const session = new SessionManager();
54
44
  const tools = new ToolRegistry();
@@ -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
  }
package/src/cmd/index.ts CHANGED
@@ -22,7 +22,6 @@ import chalk from 'chalk';
22
22
  import { App } from '../app/index.js';
23
23
  import { REPL } from '../tui/repl.js';
24
24
  import { WelcomeScreen } from '../tui/welcome.js';
25
- import { ProjectContext } from '../utils/project.js';
26
25
 
27
26
  // ==================== Error Handling ====================
28
27
 
@@ -41,7 +40,7 @@ process.on('unhandledRejection', (reason) => {
41
40
  program
42
41
  .name('gfcode')
43
42
  .description('🤖 Thatgfsj Code - AI Coding Assistant')
44
- .version('0.4.0')
43
+ .version('0.5.0')
45
44
  .argument('[prompt]', 'Task to execute (omit to start interactive mode)')
46
45
  .option('-m, --model <model>', 'Specify model')
47
46
  .option('-i, --interactive', 'Force interactive mode')
@@ -70,13 +69,59 @@ program
70
69
  const repl = new REPL(app);
71
70
  await repl.start();
72
71
  } else {
73
- // Single prompt mode
74
- console.log(chalk.cyan('\n🤖 Thatgfsj Code\n'));
75
- console.log(chalk.gray(ProjectContext.getSummary()));
76
- console.log(chalk.gray('─'.repeat(40)));
77
- console.log(chalk.cyan('\n> ') + prompt + '\n');
72
+ // Single prompt mode - use same UI as REPL
73
+ const { REPLOutput } = await import('../tui/output.js');
74
+ const out = new REPLOutput();
78
75
 
79
- await app.runPrompt(prompt);
76
+ out.printBanner();
77
+ out.printUserInput(prompt);
78
+ out.startThinking();
79
+
80
+ app.session.addMessage('user', prompt);
81
+ let fullResponse = '';
82
+ let hasStarted = false;
83
+
84
+ try {
85
+ const stream = app.streamResponse();
86
+ for await (const chunk of stream) {
87
+ out.stopThinking();
88
+
89
+ if (chunk.includes('@@TOOL@@')) {
90
+ const parts = chunk.split('\n');
91
+ for (const part of parts) {
92
+ if (part.startsWith('@@TOOL@@')) {
93
+ try {
94
+ const data = JSON.parse(part.slice(8));
95
+ if (data.action === 'call') {
96
+ if (hasStarted) { out.endAssistant(); hasStarted = false; }
97
+ out.printToolCall(data.name, data.args || '');
98
+ out.startExecuting(data.name);
99
+ } else if (data.action === 'result') {
100
+ out.stopThinking();
101
+ out.printToolResult(data.output || data.error || '', !!data.error);
102
+ out.printToolEnd();
103
+ }
104
+ } catch {}
105
+ } else if (part) {
106
+ if (!hasStarted) { out.beginAssistant(); hasStarted = true; }
107
+ out.writeChunk(part + '\n');
108
+ }
109
+ }
110
+ } else {
111
+ if (!hasStarted) { out.beginAssistant(); hasStarted = true; }
112
+ out.writeChunk(chunk);
113
+ }
114
+
115
+ fullResponse += chunk;
116
+ }
117
+ if (hasStarted) out.endAssistant();
118
+ } catch (error: any) {
119
+ if (hasStarted) out.endAssistant();
120
+ out.stopThinkingFail(error.message);
121
+ out.error(error.message);
122
+ }
123
+
124
+ app.session.addMessage('assistant', fullResponse);
80
125
  }
81
126
  } catch (error: any) {
82
127
  console.error(chalk.red(`\n❌ ${error.message}`));
@@ -14,9 +14,9 @@
14
14
  * - Tool results use role "tool_result" (not "tool")
15
15
  */
16
16
 
17
- import type { ChatMessage, ChatResponse, ChatOptions } from '../types.js';
17
+ import type { ChatMessage, ChatResponse, ChatOptions, ToolCall } from '../types.js';
18
18
  import type { Tool } from '../tools/types.js';
19
- import type { LLMProvider, ProviderConfig } from './provider.js';
19
+ import type { LLMProvider, StreamChunk, ProviderConfig } from './provider.js';
20
20
 
21
21
  export class AnthropicProvider implements LLMProvider {
22
22
  readonly name = 'anthropic';
@@ -40,8 +40,8 @@ export class AnthropicProvider implements LLMProvider {
40
40
  }));
41
41
  }
42
42
 
43
- async chat(messages: ChatMessage[], options?: ChatOptions): Promise<ChatResponse> {
44
- const body = this.buildRequest(messages, false, options);
43
+ async chat(messages: ChatMessage[], options?: ChatOptions, tools?: Tool[]): Promise<ChatResponse> {
44
+ const body = this.buildRequest(messages, false, options, tools);
45
45
  const response = await this.doRequest(body);
46
46
 
47
47
  if (!response.ok) {
@@ -78,8 +78,8 @@ export class AnthropicProvider implements LLMProvider {
78
78
  };
79
79
  }
80
80
 
81
- async *chatStream(messages: ChatMessage[], options?: ChatOptions): AsyncGenerator<string, ChatResponse> {
82
- const body = this.buildRequest(messages, true, options);
81
+ async *chatStream(messages: ChatMessage[], options?: ChatOptions, tools?: Tool[]): AsyncGenerator<StreamChunk, ChatResponse> {
82
+ const body = this.buildRequest(messages, true, options, tools);
83
83
  const response = await this.doRequest(body);
84
84
 
85
85
  if (!response.ok || !response.body) {
@@ -91,6 +91,10 @@ export class AnthropicProvider implements LLMProvider {
91
91
  const decoder = new TextDecoder();
92
92
  let fullContent = '';
93
93
  let buffer = '';
94
+ // Track tool use blocks
95
+ const toolUseBlocks: Map<number, { id: string; name: string; input: string }> = new Map();
96
+ let currentBlockIndex = -1;
97
+ let currentBlockType = '';
94
98
 
95
99
  try {
96
100
  while (true) {
@@ -108,11 +112,31 @@ export class AnthropicProvider implements LLMProvider {
108
112
  try {
109
113
  const data = JSON.parse(trimmed.slice(6));
110
114
 
111
- // content_block_delta: incremental text
112
- if (data.type === 'content_block_delta' && data.delta?.type === 'text_delta') {
113
- const text = data.delta.text;
114
- fullContent += text;
115
- yield text;
115
+ // content_block_start: track block types
116
+ if (data.type === 'content_block_start') {
117
+ currentBlockIndex = data.index ?? 0;
118
+ currentBlockType = data.content_block?.type || '';
119
+ if (currentBlockType === 'tool_use') {
120
+ toolUseBlocks.set(currentBlockIndex, {
121
+ id: data.content_block.id || '',
122
+ name: data.content_block.name || '',
123
+ input: '',
124
+ });
125
+ }
126
+ }
127
+
128
+ // content_block_delta: incremental text or tool input
129
+ if (data.type === 'content_block_delta') {
130
+ if (data.delta?.type === 'text_delta') {
131
+ const text = data.delta.text;
132
+ fullContent += text;
133
+ yield { type: 'text', content: text };
134
+ } else if (data.delta?.type === 'input_json_delta') {
135
+ const buf = toolUseBlocks.get(currentBlockIndex);
136
+ if (buf) {
137
+ buf.input += data.delta.partial_json || '';
138
+ }
139
+ }
116
140
  }
117
141
  } catch {
118
142
  // Skip invalid JSON
@@ -123,13 +147,29 @@ export class AnthropicProvider implements LLMProvider {
123
147
  reader.releaseLock();
124
148
  }
125
149
 
126
- return { content: fullContent, role: 'assistant' };
150
+ // Convert tool use blocks to ToolCall[]
151
+ const toolCalls: ToolCall[] = [];
152
+ for (const [, buf] of toolUseBlocks) {
153
+ if (buf.id && buf.name) {
154
+ toolCalls.push({
155
+ id: buf.id,
156
+ type: 'function',
157
+ function: { name: buf.name, arguments: buf.input },
158
+ });
159
+ }
160
+ }
161
+
162
+ if (toolCalls.length > 0) {
163
+ yield { type: 'tool_calls', toolCalls };
164
+ }
165
+
166
+ return { content: fullContent, role: 'assistant', tool_calls: toolCalls.length > 0 ? toolCalls : undefined };
127
167
  }
128
168
 
129
169
  /**
130
170
  * Build request body for Anthropic API
131
171
  */
132
- protected buildRequest(messages: ChatMessage[], stream: boolean, options?: ChatOptions) {
172
+ protected buildRequest(messages: ChatMessage[], stream: boolean, options?: ChatOptions, tools?: Tool[]) {
133
173
  // Anthropic uses separate system message
134
174
  const systemMsg = messages.find(m => m.role === 'system');
135
175
  const nonSystemMsgs = messages.filter(m => m.role !== 'system');
@@ -169,7 +209,7 @@ export class AnthropicProvider implements LLMProvider {
169
209
  };
170
210
  });
171
211
 
172
- return {
212
+ const body: any = {
173
213
  model: this.config.model,
174
214
  max_tokens: options?.maxTokens ?? this.config.maxTokens ?? 4096,
175
215
  temperature: options?.temperature ?? this.config.temperature,
@@ -177,6 +217,12 @@ export class AnthropicProvider implements LLMProvider {
177
217
  messages: anthropicMessages,
178
218
  stream,
179
219
  };
220
+
221
+ if (tools && tools.length > 0) {
222
+ body.tools = this.buildTools(tools);
223
+ }
224
+
225
+ return body;
180
226
  }
181
227
 
182
228
  /**