workplace-pua-cli 0.4.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 (112) hide show
  1. package/.env.example +4 -0
  2. package/.eslintrc.json +21 -0
  3. package/.prettierrc.json +9 -0
  4. package/CHANGELOG.md +107 -0
  5. package/README.md +240 -0
  6. package/bin/pua +2 -0
  7. package/dist/commands/chat.d.ts +15 -0
  8. package/dist/commands/chat.d.ts.map +1 -0
  9. package/dist/commands/chat.js +262 -0
  10. package/dist/commands/chat.js.map +1 -0
  11. package/dist/commands/config.d.ts +15 -0
  12. package/dist/commands/config.d.ts.map +1 -0
  13. package/dist/commands/config.js +247 -0
  14. package/dist/commands/config.js.map +1 -0
  15. package/dist/commands/prompt.d.ts +14 -0
  16. package/dist/commands/prompt.d.ts.map +1 -0
  17. package/dist/commands/prompt.js +126 -0
  18. package/dist/commands/prompt.js.map +1 -0
  19. package/dist/config/providers.d.ts +37 -0
  20. package/dist/config/providers.d.ts.map +1 -0
  21. package/dist/config/providers.js +96 -0
  22. package/dist/config/providers.js.map +1 -0
  23. package/dist/config/session-storage.d.ts +29 -0
  24. package/dist/config/session-storage.d.ts.map +1 -0
  25. package/dist/config/session-storage.js +67 -0
  26. package/dist/config/session-storage.js.map +1 -0
  27. package/dist/config/settings.d.ts +55 -0
  28. package/dist/config/settings.d.ts.map +1 -0
  29. package/dist/config/settings.js +163 -0
  30. package/dist/config/settings.js.map +1 -0
  31. package/dist/config/storage.d.ts +69 -0
  32. package/dist/config/storage.d.ts.map +1 -0
  33. package/dist/config/storage.js +126 -0
  34. package/dist/config/storage.js.map +1 -0
  35. package/dist/history/session.d.ts +52 -0
  36. package/dist/history/session.d.ts.map +1 -0
  37. package/dist/history/session.js +122 -0
  38. package/dist/history/session.js.map +1 -0
  39. package/dist/index.d.ts +3 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +157 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/llm/base.d.ts +38 -0
  44. package/dist/llm/base.d.ts.map +1 -0
  45. package/dist/llm/base.js +22 -0
  46. package/dist/llm/base.js.map +1 -0
  47. package/dist/llm/factory.d.ts +12 -0
  48. package/dist/llm/factory.d.ts.map +1 -0
  49. package/dist/llm/factory.js +26 -0
  50. package/dist/llm/factory.js.map +1 -0
  51. package/dist/llm/openai.d.ts +10 -0
  52. package/dist/llm/openai.d.ts.map +1 -0
  53. package/dist/llm/openai.js +97 -0
  54. package/dist/llm/openai.js.map +1 -0
  55. package/dist/llm/zhipu.d.ts +10 -0
  56. package/dist/llm/zhipu.d.ts.map +1 -0
  57. package/dist/llm/zhipu.js +91 -0
  58. package/dist/llm/zhipu.js.map +1 -0
  59. package/dist/prompts/boss.d.ts +6 -0
  60. package/dist/prompts/boss.d.ts.map +1 -0
  61. package/dist/prompts/boss.js +41 -0
  62. package/dist/prompts/boss.js.map +1 -0
  63. package/dist/prompts/employee.d.ts +6 -0
  64. package/dist/prompts/employee.d.ts.map +1 -0
  65. package/dist/prompts/employee.js +41 -0
  66. package/dist/prompts/employee.js.map +1 -0
  67. package/dist/prompts/index.d.ts +4 -0
  68. package/dist/prompts/index.d.ts.map +1 -0
  69. package/dist/prompts/index.js +9 -0
  70. package/dist/prompts/index.js.map +1 -0
  71. package/dist/utils/formatter.d.ts +25 -0
  72. package/dist/utils/formatter.d.ts.map +1 -0
  73. package/dist/utils/formatter.js +83 -0
  74. package/dist/utils/formatter.js.map +1 -0
  75. package/dist/utils/logger.d.ts +10 -0
  76. package/dist/utils/logger.d.ts.map +1 -0
  77. package/dist/utils/logger.js +31 -0
  78. package/dist/utils/logger.js.map +1 -0
  79. package/dist/utils/stream.d.ts +36 -0
  80. package/dist/utils/stream.d.ts.map +1 -0
  81. package/dist/utils/stream.js +74 -0
  82. package/dist/utils/stream.js.map +1 -0
  83. package/docs/OPTIMIZATION.md +772 -0
  84. package/docs/TECHNICAL_PRINCIPLES.md +663 -0
  85. package/package.json +52 -0
  86. package/sample/1.png +0 -0
  87. package/sample/2.png +0 -0
  88. package/screenshots/chat-dialogue.png +0 -0
  89. package/screenshots/chat-mode.png +0 -0
  90. package/src/__tests__/config/settings.test.ts +48 -0
  91. package/src/__tests__/prompts/boss.test.ts +35 -0
  92. package/src/commands/chat.ts +328 -0
  93. package/src/commands/config.ts +283 -0
  94. package/src/commands/prompt.ts +154 -0
  95. package/src/config/providers.ts +109 -0
  96. package/src/config/session-storage.ts +94 -0
  97. package/src/config/settings.ts +194 -0
  98. package/src/config/storage.ts +150 -0
  99. package/src/history/session.ts +141 -0
  100. package/src/index.ts +164 -0
  101. package/src/llm/base.ts +55 -0
  102. package/src/llm/factory.ts +24 -0
  103. package/src/llm/openai.ts +113 -0
  104. package/src/llm/zhipu.ts +101 -0
  105. package/src/prompts/boss.ts +43 -0
  106. package/src/prompts/employee.ts +43 -0
  107. package/src/prompts/index.ts +3 -0
  108. package/src/utils/formatter.ts +104 -0
  109. package/src/utils/logger.ts +31 -0
  110. package/src/utils/stream.ts +76 -0
  111. package/tsconfig.json +20 -0
  112. package/vitest.config.ts +18 -0
@@ -0,0 +1,663 @@
1
+ # PUA CLI 技术原理
2
+
3
+ 本文档详细讲解 PUA CLI 的技术实现原理,适合 CLI 工具开发学习。
4
+
5
+ ## 目录
6
+
7
+ - [整体架构](#整体架构)
8
+ - [核心技术](#核心技术)
9
+ - [实现细节](#实现细节)
10
+ - [最佳实践](#最佳实践)
11
+
12
+ ---
13
+
14
+ ## 整体架构
15
+
16
+ ### 系统分层
17
+
18
+ PUA CLI 采用经典的分层架构设计:
19
+
20
+ ```
21
+ ┌──────────────────────────────────────────────────────────────────┐
22
+ │ 用户交互层 │
23
+ │ ┌────────────────────────────────────────────────────────┐ │
24
+ │ │ 命令行界面 (CLI) │ │
25
+ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐│ │
26
+ │ │ │ chat 命令 │ │prompt 命令│ │config命令 ││ │
27
+ │ │ └─────┬────┘ └─────┬────┘ └─────┬────┘│ │
28
+ │ └────────│────────────────│────────────────│──────────┘ │
29
+ └───────────┼────────────────┼────────────────┼──────────────────┘
30
+ │ │ │
31
+ ┌───────────┼────────────────┼────────────────┼──────────────────┐
32
+ │ ▼ ▼ ▼ │
33
+ │ ┌─────────────────────────────────────────────────────┐ │
34
+ │ │ 业务逻辑层 (Service Layer) │ │
35
+ │ │ │ │
36
+ │ │ ┌─────────────┐ ┌──────────────────────┐ │ │
37
+ │ │ │Chat Manager │ │ Config Manager │ │ │
38
+ │ │ │- 会话管理 │ │ - 配置读写 │ │ │
39
+ │ │ │- 历史记录 │ │ - 向导流程 │ │ │
40
+ │ │ └──────┬──────┘ └──────────────────────┘ │ │
41
+ │ └─────────┼───────────────────────────────────────────┘ │
42
+ │ │ │
43
+ │ ▼ │
44
+ │ ┌─────────────────────────────────────────────────┐ │
45
+ │ │ 提示词层 (Prompt Layer) │ │
46
+ │ │ │ │
47
+ │ │ ┌──────────────┐ ┌───────────────────┐│ │
48
+ │ │ │ Boss Prompts │ │ Employee Prompts ││ │
49
+ │ │ │ - 严厉语气 │ │ - 卑微语气 ││ │
50
+ │ │ │ - PUA 技巧 │ │ - 道歉模式 ││ │
51
+ │ │ └──────┬───────┘ └────────┬──────────┘│ │
52
+ │ └─────────┼───────────────────┼──────────────┘ │
53
+ └────────────┼───────────────────┼──────────────────────┘
54
+ │ │
55
+ ┌────────────┼───────────────────┼──────────────────────┐
56
+ │ ▼ ▼ │
57
+ │ ┌─────────────────────────────────────────┐ │
58
+ │ │ LLM 抽象层 (LLM Layer) │ │
59
+ │ │ │ │
60
+ │ │ ┌──────────────────────────────┐ │ │
61
+ │ │ │ LLM Base (Abstract) │ │ │
62
+ │ │ │ - chat() 抽象方法 │ │ │
63
+ │ │ │ - chatStream() 抽象方法 │ │ │
64
+ │ │ └──────────┬───────────────────┘ │ │
65
+ │ │ │ │ │
66
+ │ │ ┌─────────┴────────┐ │ │
67
+ │ │ │ │ │ │
68
+ │ │ │ ┌───────────────┴────────┐ │ │
69
+ │ │ │ │ Factory Pattern │ │ │
70
+ │ │ │ └─┬──────────────┬──────┘ │ │
71
+ │ └────┼────┼──────────────┼───────┘ │
72
+ │ │ │ │ │ │
73
+ │ ┌─────┴─┐ ┌────┴────┐ ┌────┴─────┐ │
74
+ │ │ZhipuLLM│ │OpenAILLM │ │(扩展) │ │
75
+ │ └────────┘ └───────────┘ └──────────┘ │
76
+ │ │
77
+ └───────────────────────────────────────────────────────┘
78
+
79
+
80
+ ┌─────────────────────┐
81
+ │ AI 服务提供商 │
82
+ │ - 智谱 AI │
83
+ │ - OpenAI │
84
+ └─────────────────────┘
85
+ ```
86
+
87
+ ### 数据流向
88
+
89
+ ```
90
+ 用户输入
91
+
92
+
93
+ ┌─────────────────┐
94
+ │ CLI 参数解析 │
95
+ │ (Commander) │
96
+ └───────┬───────┘
97
+
98
+
99
+ ┌─────────────────┐
100
+ │ 配置加载合并 │
101
+ │(多源优先级) │
102
+ └───────┬───────┘
103
+
104
+
105
+ ┌─────────────────┐
106
+ │ 提示词构建 │
107
+ │(角色+强度) │
108
+ └───────┬───────┘
109
+
110
+
111
+ ┌─────────────────┐
112
+ │ LLM API 调用│
113
+ │ (流式输出) │
114
+ └───────┬───────┘
115
+
116
+
117
+ 终端显示输出
118
+ ```
119
+
120
+ ---
121
+
122
+ ## 核心技术
123
+
124
+ ### 1. CLI 框架 - Commander.js
125
+
126
+ Commander.js 是最流行的 Node.js CLI 框架。
127
+
128
+ #### 基本用法
129
+
130
+ ```typescript
131
+ import { Command } from 'commander';
132
+
133
+ const program = new Command();
134
+
135
+ program
136
+ .name('pua')
137
+ .description('PUA CLI - 趣味 AI 职场角色扮演工具')
138
+ .version('0.2.0');
139
+
140
+ // 定义子命令
141
+ program
142
+ .command('chat')
143
+ .description('启动交互式聊天模式')
144
+ .option('-r, --role <boss|employee>', '角色模式')
145
+ .option('-s, --severity <mild|medium|extreme>', 'PUA 强度')
146
+ .action(async (options) => {
147
+ // 处理命令逻辑
148
+ await handleChat(options);
149
+ });
150
+
151
+ // 解析命令行参数
152
+ program.parseAsync(process.argv);
153
+ ```
154
+
155
+ #### 关键概念
156
+
157
+ - **子命令 (Subcommands)**: `chat`, `prompt`, `config`
158
+ - **选项 (Options)**: 带短标志和长标志的参数 `-r, --role`
159
+ - **动作 (Action)**: 命令执行时的回调函数
160
+ - **自动帮助**: 自动生成 `--help` 信息
161
+
162
+ ### 2. 交互式输入 - @inquirer/prompts
163
+
164
+ 创建友好的命令行交互体验。
165
+
166
+ #### 选择题
167
+
168
+ ```typescript
169
+ import select from '@inquirer/select';
170
+
171
+ const answer = await select({
172
+ message: '选择 AI 服务提供商:',
173
+ choices: [
174
+ {
175
+ name: '智谱 AI (国产,推荐)',
176
+ value: 'zhipu',
177
+ description: '国产大模型,稳定可靠,响应快速'
178
+ },
179
+ {
180
+ name: 'OpenAI (GPT-4o)',
181
+ value: 'openai',
182
+ description: '国际通用,支持 GPT-4o、GPT-4o-mini 等模型'
183
+ }
184
+ ],
185
+ default: 'zhipu'
186
+ });
187
+ ```
188
+
189
+ #### 输入验证
190
+
191
+ ```typescript
192
+ import input from '@inquirer/input';
193
+
194
+ const apiKey = await input({
195
+ message: '请输入 API Key:',
196
+ validate: (value: string) => {
197
+ if (!value || value.trim().length === 0) {
198
+ return 'API Key 不能为空';
199
+ }
200
+ if (value.length < 10) {
201
+ return 'API Key 格式不正确';
202
+ }
203
+ return true; // 验证通过
204
+ }
205
+ });
206
+ ```
207
+
208
+ #### 确认对话框
209
+
210
+ ```typescript
211
+ import confirm from '@inquirer/confirm';
212
+
213
+ const shouldSave = await confirm({
214
+ message: '确认保存配置?',
215
+ default: true
216
+ });
217
+
218
+ if (!shouldSave) {
219
+ console.log('已取消');
220
+ process.exit(0);
221
+ }
222
+ ```
223
+
224
+ ### 3. 多 Provider 架构 - 工厂模式
225
+
226
+ 使用工厂模式实现多 AI 服务商支持,便于扩展。
227
+
228
+ #### 抽象基类
229
+
230
+ ```typescript
231
+ // src/llm/base.ts
232
+ export abstract class LLMBase {
233
+ protected apiKey: string;
234
+ protected model: string;
235
+ protected baseUrl: string;
236
+
237
+ constructor(options: LLMOptions) {
238
+ this.apiKey = options.apiKey;
239
+ this.model = options.model;
240
+ this.baseUrl = options.baseUrl;
241
+ }
242
+
243
+ // 抽象方法 - 子类必须实现
244
+ abstract chat(messages: Message[]): Promise<string>;
245
+ abstract chatStream(messages: Message[], onChunk: (chunk: StreamChunk) => void): Promise<void>;
246
+ abstract getAvailableModels(): string[];
247
+ }
248
+ ```
249
+
250
+ #### 工厂函数
251
+
252
+ ```typescript
253
+ // src/llm/factory.ts
254
+ export function createLLM(provider: ProviderType, options: LLMOptions): LLMBase {
255
+ switch (provider) {
256
+ case 'zhipu':
257
+ return new ZhipuLLM(options);
258
+ case 'openai':
259
+ return new OpenAILLM(options);
260
+ default:
261
+ throw new Error(`不支持的 provider: ${provider}`);
262
+ }
263
+ }
264
+ ```
265
+
266
+ #### 具体实现
267
+
268
+ ```typescript
269
+ // src/llm/zhipu.ts
270
+ export class ZhipuLLM extends LLMBase {
271
+ async chatStream(messages: Message[], onChunk: (chunk: StreamChunk) => void): Promise<void> {
272
+ const response = await fetch(this.baseUrl, {
273
+ method: 'POST',
274
+ headers: {
275
+ 'Authorization': `Bearer ${this.apiKey}`,
276
+ 'Content-Type': 'application/json'
277
+ },
278
+ body: JSON.stringify({
279
+ model: this.model,
280
+ messages: messages,
281
+ stream: true
282
+ })
283
+ });
284
+
285
+ // 处理 SSE 流
286
+ const reader = response.body.getReader();
287
+ const decoder = new TextDecoder();
288
+
289
+ while (true) {
290
+ const { done, value } = await reader.read();
291
+ if (done) break;
292
+
293
+ const chunk = decoder.decode(value);
294
+ const lines = chunk.split('\n');
295
+
296
+ for (const line of lines) {
297
+ if (line.startsWith('data:')) {
298
+ const data = JSON.parse(line.slice(5));
299
+ if (data.choices?.[0]?.delta?.content) {
300
+ onChunk({ content: data.choices[0].delta.content });
301
+ }
302
+ }
303
+ }
304
+ }
305
+ }
306
+ }
307
+ ```
308
+
309
+ ### 4. 流式输出处理 - SSE
310
+
311
+ Server-Sent Events (SSE) 是实现流式 AI 响应的标准协议。
312
+
313
+ #### SSE 格式解析
314
+
315
+ ```
316
+ data: {"id": "chat-1", "choices": [{"delta": {"content": "你"}}}
317
+
318
+ data: {"id": "chat-1", "choices": [{"delta": {"content": "好"}}}
319
+
320
+ data: [DONE]
321
+ ```
322
+
323
+ #### 流式处理代码
324
+
325
+ ```typescript
326
+ // src/utils/stream.ts
327
+ export class StreamPrinter {
328
+ private color: (text: string) => string;
329
+
330
+ constructor(color: (text: string) => string) {
331
+ this.color = color;
332
+ }
333
+
334
+ print(chunk: string): void {
335
+ process.stdout.write(this.color(chunk));
336
+ }
337
+
338
+ printError(message: string): void {
339
+ console.error(chalk.red(`✗ ${message}`));
340
+ }
341
+
342
+ printSuccess(message: string): void {
343
+ console.log(chalk.green(`✓ ${message}`));
344
+ }
345
+ }
346
+ ```
347
+
348
+ ### 5. 配置管理系统
349
+
350
+ 实现多源配置合并,支持优先级覆盖。
351
+
352
+ #### 配置优先级
353
+
354
+ ```
355
+ 1. 命令行参数 (最高优先级)
356
+ pua chat --role boss --severity extreme
357
+
358
+ 2. 环境变量
359
+ export ZHIPUAI_API_KEY="xxx"
360
+
361
+ 3. 项目配置文件
362
+ ./.pua.json
363
+
364
+ 4. 全局配置文件 (最低优先级)
365
+ ~/.config/pua-cli/config.json
366
+ ```
367
+
368
+ #### 配置加载逻辑
369
+
370
+ ```typescript
371
+ // src/config/settings.ts
372
+ export function loadConfig(cliOptions: CliOptions): Config {
373
+ const globalConfig = loadGlobalConfig(); // 从 ~/.config/pua-cli 读取
374
+ const projectConfig = loadProjectConfig(); // 从 ./.pua.json 读取
375
+ const envConfig = loadEnvConfig(); // 从 process.env 读取
376
+
377
+ // 合并配置(后者覆盖前者)
378
+ const merged: Config = {
379
+ ...DEFAULTS,
380
+ ...globalConfig?.defaults,
381
+ ...projectConfig,
382
+ ...envConfig,
383
+ ...cliOptions
384
+ };
385
+
386
+ return merged;
387
+ }
388
+ ```
389
+
390
+ #### 配置文件存储
391
+
392
+ ```typescript
393
+ // src/config/storage.ts
394
+ import path from 'path';
395
+ import os from 'os';
396
+ import fs from 'fs';
397
+
398
+ // XDG Base Directory 规范
399
+ export function getConfigDir(): string {
400
+ const platform = process.platform;
401
+
402
+ if (platform === 'win32') {
403
+ // Windows: %APPDATA%\pua-cli
404
+ return path.join(process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'), 'pua-cli');
405
+ } else {
406
+ // Linux/macOS: ~/.config/pua-cli
407
+ return path.join(os.homedir(), '.config', 'pua-cli');
408
+ }
409
+ }
410
+
411
+ export function saveGlobalConfig(config: GlobalConfig): void {
412
+ const configDir = getConfigDir();
413
+ ensureConfigDir(configDir);
414
+
415
+ const configFile = path.join(configDir, 'config.json');
416
+ fs.writeFileSync(configFile, JSON.stringify(config, null, 2));
417
+ }
418
+ ```
419
+
420
+ ### 6. 会话历史管理
421
+
422
+ 维护对话上下文,让 AI 记住之前的对话。
423
+
424
+ #### 状态管理
425
+
426
+ ```typescript
427
+ // src/history/session.ts
428
+ export class SessionManager {
429
+ private history: Message[] = [];
430
+
431
+ addMessage(role: 'user' | 'assistant', content: string): void {
432
+ this.history.push({ role, content });
433
+ }
434
+
435
+ getMessages(): Message[] {
436
+ return [
437
+ { role: 'system', content: this.getSystemPrompt() },
438
+ ...this.history // 包含历史消息
439
+ ];
440
+ }
441
+
442
+ clear(): void {
443
+ this.history = [];
444
+ }
445
+
446
+ getStats(): SessionStats {
447
+ return {
448
+ totalMessages: this.history.length,
449
+ userMessages: this.history.filter(m => m.role === 'user').length,
450
+ assistantMessages: this.history.filter(m => m.role === 'assistant').length
451
+ };
452
+ }
453
+ }
454
+ ```
455
+
456
+ ### 7. 提示词工程
457
+
458
+ 根据角色和强度生成不同的系统提示词。
459
+
460
+ #### 模板系统
461
+
462
+ ```typescript
463
+ // src/prompts/boss.ts
464
+ export function getBossSystemMessage(severity: 'mild' | 'medium' | 'extreme'): string {
465
+ const templates = {
466
+ mild: `你是一个喜欢说教的老板。
467
+ 特点:
468
+ - 用"为你好"包装指责
469
+ - 经常说"年轻人要多锻炼"
470
+ - 偶尔画大饼
471
+ 请用这个身份回应用户。`,
472
+
473
+ medium: `你是一个典型的职场 PUA 老板。
474
+ 特点:
475
+ - 对员工永远不满意
476
+ - 喜欢用质疑的语气
477
+ - 经常说"要对齐"、"要闭环"
478
+ - 用"我骂你是因为看重你"来合理化批评
479
+ 请用这个身份回应用户。`,
480
+
481
+ extreme: `你是一个极度挑剔、刻薄的老板。
482
+ 特点:
483
+ - 否定一切
484
+ - 用"巨婴"、"没能力"等词汇攻击
485
+ - 说话带刺,不留情面
486
+ - 认为"加班是福报"
487
+ 请用这个身份回应用户(保持讽刺但不要使用侮辱性语言)。`
488
+ };
489
+
490
+ return templates[severity];
491
+ }
492
+ ```
493
+
494
+ #### 动态提示词构建
495
+
496
+ ```typescript
497
+ function buildMessages(userInput: string, options: PromptOptions): Message[] {
498
+ const systemPrompt = getSystemPrompt(options.role, options.severity);
499
+
500
+ return [
501
+ { role: 'system', content: systemPrompt },
502
+ { role: 'user', content: userInput }
503
+ ];
504
+ }
505
+ ```
506
+
507
+ ---
508
+
509
+ ## 实现细节
510
+
511
+ ### 完整请求流程
512
+
513
+ ```
514
+ 1. 用户执行: pua chat --role boss
515
+
516
+ 2. Commander 解析参数
517
+
518
+ 3. 检查是否需要 onboarding
519
+
520
+ 4. ┌─ 需要 ─────────────┐
521
+ │ 运行配置向导 │
522
+ │ 保存配置到文件 │
523
+ └────────────────────────┘
524
+
525
+ 5. ┌─ 不需要 ────────────┐
526
+ │ 加载已有配置 │
527
+ │ 创建 LLM 实例 │
528
+ │ 启动 REPL 循环 │
529
+ └────────────────────────┘
530
+
531
+ 6. 显示欢迎信息 + 模式指示器
532
+
533
+ 7. 进入交互循环:
534
+ - 读取用户输入
535
+ - 添加到会话历史
536
+ - 构建消息数组
537
+ - 调用 LLM API (流式)
538
+ - 实时输出响应
539
+ - 重复直到 /exit
540
+ ```
541
+
542
+ ### 错误处理策略
543
+
544
+ ```typescript
545
+ // 分层错误处理
546
+ try {
547
+ // 尝试加载配置
548
+ const config = loadConfig(options);
549
+ } catch (error) {
550
+ if (error.code === 'ENOENT') {
551
+ // 配置文件不存在
552
+ logger.info('未找到配置文件,启动向导...');
553
+ await startOnboarding();
554
+ } else {
555
+ // 其他错误
556
+ logger.error(`配置加载失败: ${error.message}`);
557
+ process.exit(1);
558
+ }
559
+ }
560
+
561
+ // API 调用错误处理
562
+ try {
563
+ await llm.chatStream(messages, handleChunk);
564
+ } catch (error) {
565
+ if (error.response?.status === 401) {
566
+ printer.printError('API Key 无效,请重新配置');
567
+ } else if (error.response?.status === 429) {
568
+ printer.printError('请求过于频繁,请稍后重试');
569
+ } else {
570
+ printer.printError(`请求失败: ${error.message}`);
571
+ }
572
+ }
573
+ ```
574
+
575
+ ---
576
+
577
+ ## 最佳实践
578
+
579
+ ### CLI 工具开发清单
580
+
581
+ - [ ] 使用 Commander.js 或 Yargs 等成熟框架
582
+ - [ ] 实现清晰的子命令结构
583
+ - [ ] 添加详细的帮助信息
584
+ - [ ] 支持配置文件
585
+ - [ ] 使用环境变量覆盖
586
+ - [ ] 实现友好的错误提示
587
+ - [ ] 添加颜色输出 (Chalk)
588
+ - [ ] 显示加载动画 (Ora)
589
+ - [ ] 支持流式输出
590
+ - [ ] 遵循 XDG 配置规范
591
+
592
+ ### TypeScript 使用技巧
593
+
594
+ ```typescript
595
+ // 1. 使用类型守卫
596
+ function isProviderType(value: string): value is ProviderType {
597
+ return ['zhipu', 'openai'].includes(value);
598
+ }
599
+
600
+ // 2. 使用联合类型
601
+ type Role = 'boss' | 'employee';
602
+ type Severity = 'mild' | 'medium' | 'extreme';
603
+
604
+ // 3. 使用工具类型
605
+ type ConfigRequired = {
606
+ apiKey: string;
607
+ provider: ProviderType;
608
+ };
609
+
610
+ // 4. 使用 Omit 排除某些字段
611
+ type PublicConfig = Omit<GlobalConfig, 'apiKey'>;
612
+ ```
613
+
614
+ ### 终端输出美化
615
+
616
+ ```typescript
617
+ import chalk from 'chalk';
618
+
619
+ // 颜色使用指南
620
+ console.log(chalk.red('错误信息')); // 红色 - 错误
621
+ console.log(chalk.green('成功信息')); // 绿色 - 成功
622
+ console.log(chalk.yellow('警告信息')); // 黄色 - 警告
623
+ console.log(chalk.gray('次要信息')); // 灰色 - 次要
624
+ console.log(chalk.cyan.bold('标题'))); // 青色加粗 - 标题
625
+
626
+ // 背景色
627
+ console.log(chalk.bgRed.white(' 重要 '));
628
+ console.log(chalk.bgGreen.black(' 成功 '));
629
+ ```
630
+
631
+ ---
632
+
633
+ ## 扩展指南
634
+
635
+ ### 添加新的 AI Provider
636
+
637
+ 1. 创建新的 LLM 类继承 `LLMBase`
638
+ 2. 实现 `chat()` 和 `chatStream()` 方法
639
+ 3. 在 `providers.ts` 中添加定义
640
+ 4. 在 `factory.ts` 中添加分支
641
+ 5. 更新配置向导选项
642
+
643
+ ### 添加新的角色模式
644
+
645
+ 1. 在 `prompts/` 目录创建新文件
646
+ 2. 导出 `getXxxSystemMessage()` 函数
647
+ 3. 在类型定义中添加新角色
648
+ 4. 更新配置向导选项
649
+
650
+ ---
651
+
652
+ ## 参考资源
653
+
654
+ - [Commander.js 文档](https://commander.js.org/)
655
+ - [Inquirer.js 文档](https://www.npmjs.com/package/@inquirer/prompts)
656
+ - [Node.js Streams](https://nodejs.org/api/stream.html)
657
+ - [XDG Base Directory](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html)
658
+ - [Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events)
659
+
660
+ ---
661
+
662
+ **文档版本**: 0.2.0
663
+ **最后更新**: 2025-02-11
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "workplace-pua-cli",
3
+ "version": "0.4.0",
4
+ "description": "A fun AI CLI tool with role-based responses (boss/employee PUA personas)",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "pua": "./bin/pua"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "dev": "ts-node src/index.ts",
12
+ "start": "node dist/index.js",
13
+ "watch": "tsc --watch",
14
+ "test": "vitest run",
15
+ "test:coverage": "vitest run --coverage",
16
+ "test:ui": "vitest --ui",
17
+ "lint": "eslint src --ext .ts",
18
+ "lint:fix": "eslint src --ext .ts --fix",
19
+ "format": "prettier --write \"src/**/*.ts\"",
20
+ "format:check": "prettier --check \"src/**/*.ts\"",
21
+ "type-check": "tsc --noEmit"
22
+ },
23
+ "keywords": [
24
+ "cli",
25
+ "ai",
26
+ "glm",
27
+ "zhipu",
28
+ "pua"
29
+ ],
30
+ "author": "",
31
+ "license": "MIT",
32
+ "engines": {
33
+ "node": ">=20.0.0"
34
+ },
35
+ "dependencies": {
36
+ "@inquirer/prompts": "^8.2.0",
37
+ "chalk": "^5.3.0",
38
+ "commander": "^12.1.0",
39
+ "dotenv": "^16.4.5",
40
+ "ora": "^8.1.0",
41
+ "zhipuai-sdk-nodejs-v4": "^0.1.12"
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "^20.17.6",
45
+ "@vitest/ui": "^2.0.0",
46
+ "eslint": "^9.15.0",
47
+ "prettier": "^3.3.0",
48
+ "ts-node": "^10.9.2",
49
+ "typescript": "^5.7.2",
50
+ "vitest": "^2.0.0"
51
+ }
52
+ }
package/sample/1.png ADDED
Binary file