thatgfsj-code 0.3.0 → 0.4.1

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 (60) hide show
  1. package/dist/cmd/index.js +56 -8
  2. package/dist/cmd/index.js.map +1 -1
  3. package/dist/config/index.d.ts +5 -1
  4. package/dist/config/index.d.ts.map +1 -1
  5. package/dist/config/index.js +22 -4
  6. package/dist/config/index.js.map +1 -1
  7. package/dist/config/providers.d.ts +5 -10
  8. package/dist/config/providers.d.ts.map +1 -1
  9. package/dist/config/providers.js +152 -78
  10. package/dist/config/providers.js.map +1 -1
  11. package/dist/config/types.d.ts +1 -1
  12. package/dist/config/types.d.ts.map +1 -1
  13. package/dist/llm/anthropic.d.ts +24 -6
  14. package/dist/llm/anthropic.d.ts.map +1 -1
  15. package/dist/llm/anthropic.js +136 -46
  16. package/dist/llm/anthropic.js.map +1 -1
  17. package/dist/llm/gemini.d.ts +4 -4
  18. package/dist/llm/gemini.d.ts.map +1 -1
  19. package/dist/llm/gemini.js +67 -30
  20. package/dist/llm/gemini.js.map +1 -1
  21. package/dist/llm/index.d.ts +10 -23
  22. package/dist/llm/index.d.ts.map +1 -1
  23. package/dist/llm/index.js +73 -78
  24. package/dist/llm/index.js.map +1 -1
  25. package/dist/llm/openai.d.ts +19 -6
  26. package/dist/llm/openai.d.ts.map +1 -1
  27. package/dist/llm/openai.js +86 -35
  28. package/dist/llm/openai.js.map +1 -1
  29. package/dist/llm/provider.d.ts +11 -16
  30. package/dist/llm/provider.d.ts.map +1 -1
  31. package/dist/tui/input.d.ts +0 -1
  32. package/dist/tui/input.d.ts.map +1 -1
  33. package/dist/tui/input.js +2 -5
  34. package/dist/tui/input.js.map +1 -1
  35. package/dist/tui/output.d.ts +27 -12
  36. package/dist/tui/output.d.ts.map +1 -1
  37. package/dist/tui/output.js +176 -40
  38. package/dist/tui/output.js.map +1 -1
  39. package/dist/tui/repl.d.ts +0 -10
  40. package/dist/tui/repl.d.ts.map +1 -1
  41. package/dist/tui/repl.js +76 -42
  42. package/dist/tui/repl.js.map +1 -1
  43. package/dist/tui/welcome.d.ts +2 -8
  44. package/dist/tui/welcome.d.ts.map +1 -1
  45. package/dist/tui/welcome.js +101 -51
  46. package/dist/tui/welcome.js.map +1 -1
  47. package/package.json +1 -1
  48. package/src/cmd/index.ts +49 -8
  49. package/src/config/index.ts +21 -4
  50. package/src/config/providers.ts +153 -79
  51. package/src/config/types.ts +11 -5
  52. package/src/llm/anthropic.ts +148 -51
  53. package/src/llm/gemini.ts +81 -39
  54. package/src/llm/index.ts +75 -83
  55. package/src/llm/openai.ts +96 -41
  56. package/src/llm/provider.ts +12 -16
  57. package/src/tui/input.ts +2 -5
  58. package/src/tui/output.ts +193 -40
  59. package/src/tui/repl.ts +82 -43
  60. package/src/tui/welcome.ts +110 -53
@@ -1,6 +1,5 @@
1
1
  /**
2
- * Welcome / Setup Wizard
3
- * Migrated and simplified from old src/repl/welcome.ts
2
+ * Welcome / Setup Wizard - Clean UI
4
3
  */
5
4
 
6
5
  import chalk from 'chalk';
@@ -8,94 +7,152 @@ import readline from 'readline';
8
7
  import { existsSync, mkdirSync, writeFileSync } from 'fs';
9
8
  import { join } from 'path';
10
9
  import { homedir } from 'os';
11
- import { PROVIDERS, getModelsForProvider, listProviders } from '../config/providers.js';
10
+ import { PROVIDERS, getModelsForProvider, listProviders, isCustomProvider } from '../config/providers.js';
12
11
  import type { ProviderName } from '../config/types.js';
13
12
 
13
+ const line = chalk.gray('─'.repeat(52));
14
+
14
15
  export class WelcomeScreen {
15
- /**
16
- * Show welcome if no API key is configured
17
- */
16
+
18
17
  static show(hasApiKey: boolean): void {
19
18
  if (hasApiKey) return;
20
19
 
21
- const w = 62;
22
- console.log(chalk.cyan('+') + chalk.white.bold(' Thatgfsj Code ') + chalk.cyan('-'.repeat(w - 16)) + '+');
23
- console.log(chalk.cyan('|') + chalk.yellow(' 快速开始指南').padEnd(w) + chalk.cyan('|'));
24
- console.log(chalk.cyan('|') + ' '.repeat(w) + chalk.cyan('|'));
25
- console.log(chalk.cyan('|') + chalk.gray(' 运行 gfcode init 配置你的 API Key').padEnd(w) + chalk.cyan('|'));
26
- console.log(chalk.cyan('|') + chalk.gray(' 或设置环境变量: OPENAI_API_KEY, SILICONFLOW_API_KEY 等').padEnd(w) + chalk.cyan('|'));
27
- console.log(chalk.cyan('+') + '-'.repeat(w) + '+');
20
+ console.log();
21
+ console.log(chalk.cyan.bold(' Thatgfsj Code') + chalk.gray(' v0.4.1'));
22
+ console.log(chalk.gray(' AI Coding Assistant'));
23
+ console.log(line);
24
+ console.log();
25
+ console.log(chalk.yellow(' ⚠ No API key configured'));
26
+ console.log();
27
+ console.log(chalk.gray(' Run ') + chalk.cyan.bold('gfcode init') + chalk.gray(' to set up your provider.'));
28
+ console.log();
29
+ console.log(chalk.gray(' Supported providers:'));
30
+ console.log(chalk.gray(' OpenAI · Claude · DeepSeek · Kimi · GLM · Gemini · 中转站'));
31
+ console.log(line);
28
32
  console.log();
29
33
  }
30
34
 
31
- /**
32
- * Interactive setup wizard
33
- */
34
35
  static async interactiveSetup(): Promise<void> {
35
36
  console.clear();
36
- console.log(chalk.cyan('\n 🤖 Thatgfsj Code 配置向导\n'));
37
+ console.log();
38
+ console.log(chalk.cyan.bold(' ⚡ Thatgfsj Code') + chalk.gray(' - Setup'));
39
+ console.log(line);
40
+ console.log();
37
41
 
38
42
  const rl = readline.createInterface({
39
43
  input: process.stdin,
40
44
  output: process.stdout,
41
45
  });
42
46
 
43
- const question = (prompt: string): Promise<string> =>
47
+ const ask = (prompt: string): Promise<string> =>
44
48
  new Promise(resolve => rl.question(prompt, answer => resolve(answer.trim())));
45
49
 
46
50
  try {
47
- // Step 1: Choose provider
48
- console.log(chalk.white(' 步骤 1/3: 选择 AI 提供商\n'));
51
+ // ── Step 1: Provider ────────────────────────────────
52
+ console.log(chalk.bold(' 1. Choose Provider'));
53
+ console.log();
49
54
  const providers = listProviders();
50
55
  providers.forEach((p, i) => {
51
- console.log(chalk.green(` ${i + 1}. ${p.name}`));
56
+ const num = chalk.cyan((i + 1).toString().padStart(2));
57
+ console.log(` ${num} ${p.name}`);
52
58
  });
53
59
  console.log();
54
60
 
55
- const choice = await question(chalk.green(' 请选择 (1-' + providers.length + '): '));
61
+ const choice = await ask(chalk.cyan(' '));
56
62
  const selected = providers[parseInt(choice) - 1] || providers[0];
57
63
  const providerName = selected.key;
58
64
 
59
- // Step 2: Enter API Key
60
- console.log(chalk.white('\n 步骤 2/3: 输入 API Key\n'));
61
- console.log(chalk.gray(` 获取地址: ${PROVIDERS[providerName].baseUrl.replace('/v1', '').replace('/v1beta', '')}`));
62
- const apiKey = await question(chalk.green(' 请输入 API Key: '));
63
-
64
- // Step 3: Choose model
65
- console.log(chalk.white('\n 步骤 3/3: 选择模型\n'));
66
- const models = getModelsForProvider(providerName);
67
- models.forEach((m, i) => {
68
- console.log(chalk.green(` ${i + 1}. ${m.name}`) + chalk.gray(` - ${m.desc}`));
69
- });
65
+ // ── Step 2: API Key ─────────────────────────────────
66
+ console.log();
67
+ console.log(chalk.bold(' 2. API Key'));
70
68
  console.log();
71
69
 
72
- const modelChoice = await question(chalk.green(' 请选择模型 (1-' + models.length + '): '));
73
- const selectedModel = models[parseInt(modelChoice) - 1] || models[0];
70
+ let baseUrl: string | undefined;
71
+
72
+ if (isCustomProvider(providerName)) {
73
+ console.log(chalk.yellow(' Custom Provider (Relay Station)'));
74
+ console.log(chalk.gray(' OpenAI-compatible or Anthropic-compatible'));
75
+ console.log();
76
+ baseUrl = await ask(chalk.cyan(' Base URL ❯ '));
77
+ if (!baseUrl) {
78
+ console.log(chalk.red(' ❌ Base URL required'));
79
+ rl.close();
80
+ return;
81
+ }
82
+ baseUrl = baseUrl.replace(/\/+$/, '');
83
+ }
74
84
 
75
- // Save config
76
- const configDir = join(homedir(), '.thatgfsj');
77
- const configPath = join(configDir, 'config.json');
85
+ const apiKey = await ask(chalk.cyan(' API Key ❯ '));
78
86
 
79
- if (!existsSync(configDir)) {
80
- mkdirSync(configDir, { recursive: true });
81
- }
87
+ // ── Step 3: Model ───────────────────────────────────
88
+ console.log();
89
+ console.log(chalk.bold(' 3. Choose Model'));
90
+ console.log();
82
91
 
83
- writeFileSync(configPath, JSON.stringify({
84
- provider: providerName,
85
- model: selectedModel.id,
86
- apiKey,
87
- temperature: 0.7,
88
- maxTokens: 4096,
89
- }, null, 2));
92
+ if (isCustomProvider(providerName)) {
93
+ console.log(chalk.gray(' Enter model name for your relay station'));
94
+ console.log(chalk.gray(' e.g. gpt-4o-mini, claude-sonnet-4-20250514'));
95
+ console.log();
96
+ const model = await ask(chalk.cyan(' Model ❯ '));
97
+ if (!model) {
98
+ console.log(chalk.red(' ❌ Model name required'));
99
+ rl.close();
100
+ return;
101
+ }
102
+ this.saveConfig(providerName, model, apiKey, baseUrl);
103
+ } else {
104
+ const models = getModelsForProvider(providerName);
105
+ models.forEach((m, i) => {
106
+ const num = chalk.cyan((i + 1).toString().padStart(2));
107
+ console.log(` ${num} ${chalk.white(m.name)} ${chalk.gray(m.desc)}`);
108
+ });
109
+ console.log();
110
+
111
+ const modelChoice = await ask(chalk.cyan(' ❯ '));
112
+ const selectedModel = models[parseInt(modelChoice) - 1] || models[0];
113
+ this.saveConfig(providerName, selectedModel.id, apiKey, baseUrl);
114
+ }
90
115
 
91
- console.log(chalk.green('\n ✅ 配置已保存!'));
92
- console.log(chalk.gray(` 提供商: ${providerName}`));
93
- console.log(chalk.gray(` 模型: ${selectedModel.name}`));
94
- console.log(chalk.gray(` 配置文件: ${configPath}`));
95
- console.log(chalk.cyan('\n 运行 gfcode 开始使用\n'));
116
+ // ── Done ────────────────────────────────────────────
117
+ console.log();
118
+ console.log(line);
119
+ console.log(chalk.green.bold(' Configuration saved!'));
120
+ console.log();
121
+ console.log(chalk.gray(' Config: ') + chalk.white(join(homedir(), '.thatgfsj', 'config.json')));
122
+ console.log(chalk.gray(' Run ') + chalk.cyan.bold('gfcode') + chalk.gray(' to start.'));
123
+ console.log(line);
124
+ console.log();
96
125
 
97
126
  } finally {
98
127
  rl.close();
99
128
  }
100
129
  }
130
+
131
+ private static saveConfig(provider: ProviderName, model: string, apiKey: string, baseUrl?: string): void {
132
+ const configDir = join(homedir(), '.thatgfsj');
133
+ const configPath = join(configDir, 'config.json');
134
+
135
+ if (!existsSync(configDir)) {
136
+ mkdirSync(configDir, { recursive: true });
137
+ }
138
+
139
+ const config: Record<string, any> = {
140
+ provider,
141
+ model,
142
+ apiKey,
143
+ temperature: 0.7,
144
+ maxTokens: 4096,
145
+ };
146
+
147
+ if (baseUrl) config.baseUrl = baseUrl;
148
+
149
+ writeFileSync(configPath, JSON.stringify(config, null, 2));
150
+
151
+ console.log();
152
+ console.log(chalk.gray(' Provider: ') + chalk.white(provider));
153
+ console.log(chalk.gray(' Model: ') + chalk.white(model));
154
+ if (baseUrl) {
155
+ console.log(chalk.gray(' URL: ') + chalk.white(baseUrl));
156
+ }
157
+ }
101
158
  }