workplace-pua-cli 0.4.1 → 0.7.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 (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +512 -243
  3. package/dist/commands/chat-new-imports.js +2 -0
  4. package/dist/commands/config.js +14 -6
  5. package/dist/commands/email.js +301 -0
  6. package/dist/commands/interview.js +660 -0
  7. package/dist/commands/jargon.js +153 -0
  8. package/dist/commands/meeting-room.js +384 -0
  9. package/dist/commands/meeting.js +323 -0
  10. package/dist/commands/weekly.js +302 -0
  11. package/dist/index.js +29 -7
  12. package/dist/prompts/hr.js +126 -0
  13. package/dist/prompts/index.js +82 -1
  14. package/dist/prompts/intern.js +126 -0
  15. package/dist/prompts/interview-prompts.js +286 -0
  16. package/dist/prompts/meeting-prompts.js +229 -0
  17. package/dist/prompts/pm.js +123 -0
  18. package/dist/prompts/techlead.js +126 -0
  19. package/dist/utils/box.js +141 -0
  20. package/dist/utils/meeting-utils.js +194 -0
  21. package/dist/utils/resume-parser.js +122 -0
  22. package/dist/utils/stream.js +97 -13
  23. package/dist/utils/theme.js +177 -0
  24. package/package.json +73 -52
  25. package/.env.example +0 -4
  26. package/.eslintrc.json +0 -21
  27. package/.prettierrc.json +0 -9
  28. package/CHANGELOG.md +0 -113
  29. package/docs/OPTIMIZATION.md +0 -772
  30. package/docs/TECHNICAL_PRINCIPLES.md +0 -663
  31. package/sample/1.png +0 -0
  32. package/sample/2.png +0 -0
  33. package/screenshots/chat-dialogue.png +0 -0
  34. package/screenshots/chat-mode.png +0 -0
  35. package/src/__tests__/config/settings.test.ts +0 -48
  36. package/src/__tests__/prompts/boss.test.ts +0 -35
  37. package/src/commands/chat.ts +0 -328
  38. package/src/commands/config.ts +0 -283
  39. package/src/commands/prompt.ts +0 -154
  40. package/src/config/providers.ts +0 -109
  41. package/src/config/session-storage.ts +0 -94
  42. package/src/config/settings.ts +0 -194
  43. package/src/config/storage.ts +0 -150
  44. package/src/history/session.ts +0 -141
  45. package/src/index.ts +0 -164
  46. package/src/llm/base.ts +0 -55
  47. package/src/llm/factory.ts +0 -24
  48. package/src/llm/openai.ts +0 -113
  49. package/src/llm/zhipu.ts +0 -101
  50. package/src/prompts/boss.ts +0 -43
  51. package/src/prompts/employee.ts +0 -43
  52. package/src/prompts/index.ts +0 -3
  53. package/src/utils/formatter.ts +0 -104
  54. package/src/utils/logger.ts +0 -31
  55. package/src/utils/stream.ts +0 -76
  56. package/tsconfig.json +0 -20
  57. package/vitest.config.ts +0 -18
@@ -0,0 +1,153 @@
1
+ "use strict";
2
+ /**
3
+ * 职场黑话生成器
4
+ * 功能:生成各种类型的职场黑话,或将普通文本转换为黑话版本
5
+ */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.translateToJargon = translateToJargon;
11
+ exports.generateJargonSentence = generateJargonSentence;
12
+ exports.createJargonCommand = createJargonCommand;
13
+ const commander_1 = require("commander");
14
+ const chalk_1 = __importDefault(require("chalk"));
15
+ /**
16
+ * 职场黑话词典
17
+ */
18
+ const JARGON_DICT = {
19
+ meeting: [
20
+ '对齐', '拉通', '沉淀', '赋能', '闭环', '抓手', '打法',
21
+ '组合拳', '矩阵', '协同', '联动', '共振', '裂变',
22
+ '渗透', '击穿', '落地', '复盘', '迭代', '敏捷',
23
+ '颗粒度', '链路', '痛点', '痒点', '抓手', '底座',
24
+ '中台', '前端', '后端', '全链路', '端到端', '生命周期'
25
+ ],
26
+ report: [
27
+ '复盘', '迭代', '裂变', '矩阵', '组合拳', '深挖',
28
+ '赋能', '加持', '维度', '视角', '赛道', '锚点', '支点', '杠杆', '撬动', '辐射', '覆盖',
29
+ '渗透', '下沉', '上行', '输出', '交付', '闭环',
30
+ '认知', '心智', '感知', '体感', '触点', '路径', '打法', '模型', '范式', '体系', '方法论'
31
+ ],
32
+ email: [
33
+ '望', '谢谢', '辛苦了', '辛苦了', '劳烦', '敬请', '谢谢',
34
+ '妥否', '收到', '请回复', '为盼', '顺颂', '商祺',
35
+ '尽快', '方便', '麻烦', '协助', '支持', '配合',
36
+ '推进', '落实', '完成', '跟进', '反馈', '确认',
37
+ '抄送', '呈报', '汇报', '同步', '对齐', '拉通'
38
+ ],
39
+ chat: [
40
+ '颗粒度', '护城河', '降本增效', '天花板', '瓶颈',
41
+ '赋能', '迭代', '敏捷', '瀑布', 'Scrum', 'Daily',
42
+ 'OKR', 'KPI', 'GMV', 'DAU', 'MAU',
43
+ '转化', '留存', '促活', '召回', '裂变', '传播',
44
+ '痛点', '爽点', '场景', '案例', '方法论', '最佳实践'
45
+ ]
46
+ };
47
+ /**
48
+ * 获取指定类型的黑话列表
49
+ */
50
+ function getJargonByType(type) {
51
+ if (type === 'all') {
52
+ return [...JARGON_DICT.meeting, ...JARGON_DICT.report, ...JARGON_DICT.email, ...JARGON_DICT.chat];
53
+ }
54
+ return JARGON_DICT[type] || JARGON_DICT.meeting;
55
+ }
56
+ /**
57
+ * 根据强度过滤黑话
58
+ */
59
+ function filterByIntensity(jargon, intensity) {
60
+ if (intensity === 'light') {
61
+ return jargon.slice(0, 5);
62
+ }
63
+ else if (intensity === 'medium') {
64
+ return jargon.slice(0, 10);
65
+ }
66
+ else {
67
+ return jargon;
68
+ }
69
+ }
70
+ /**
71
+ * 将普通文本转换为黑话版本
72
+ */
73
+ function translateToJargon(text, intensity = 'medium') {
74
+ let result = text;
75
+ const replacementCount = intensity === 'light' ? 3 : intensity === 'medium' ? 6 : 10;
76
+ // 随机选择并替换 3-10 个词
77
+ const rules = ['做', '想想', '讨论', '连接'];
78
+ const shuffledRules = rules.sort(() => Math.random() - 0.5).slice(0, replacementCount);
79
+ for (const rule of shuffledRules) {
80
+ const regex = new RegExp(`(${rule})`, 'g');
81
+ result = result.replace(regex, '对$1');
82
+ }
83
+ // 添加随机黑话
84
+ const jargonList = JARGON_DICT.chat.sort(() => Math.random() - 0.5);
85
+ result = `${result},${jargonList[Math.floor(Math.random() * jargonList.length)]}到位`;
86
+ return result;
87
+ }
88
+ /**
89
+ * 生成黑话示例句子
90
+ */
91
+ function generateJargonSentence(type, intensity) {
92
+ const templates = {
93
+ meeting: ['我们来{action}一下{topic}的{aspect}', '需要{action}一下{topic}'],
94
+ report: ['本周{action}了{number}个{topic}', '完成了{number}个{topic}的{action}'],
95
+ email: ['辛苦{action}一下{topic}', '麻烦{action}一下{topic}'],
96
+ chat: ['需要在{topic}方面{action}', '关于{topic}的{aspect}']
97
+ };
98
+ const selectedTemplates = templates[type] || templates.meeting;
99
+ const template = selectedTemplates[Math.floor(Math.random() * selectedTemplates.length)];
100
+ return template
101
+ .replace(/{action}/g, () => ['对齐', '拉通', '赋能', '沉淀'][Math.floor(Math.random() * 4)])
102
+ .replace(/{number}/g, () => String(Math.floor(Math.random() * 10) + 1))
103
+ .replace(/{topic}/g, () => ['业务', '产品', '项目', '需求'][Math.floor(Math.random() * 4)])
104
+ .replace(/{aspect}/g, () => ['颗粒度', '护城河', '闭环', '抓手'][Math.floor(Math.random() * 4)]);
105
+ }
106
+ /**
107
+ * 黑话生成器命令
108
+ */
109
+ function createJargonCommand() {
110
+ const command = new commander_1.Command('jargon')
111
+ .description('职场黑话生成器 - 生成各种类型的职场黑话')
112
+ .option('-t, --type <type>', '黑话类型: meeting(会议), report(报告), email(邮件), chat(聊天), all(全部)', 'meeting')
113
+ .option('-i, --intensity <level>', '强度: light(轻度), medium(中度), heavy(重度)', 'medium')
114
+ .argument('[text...]', '要翻译的普通文本(输入文本则进入翻译模式)');
115
+ command.action(async (textArgs, options) => {
116
+ const type = options.type || 'meeting';
117
+ const intensity = options.intensity || 'medium';
118
+ // 当用户输入文本时,进入翻译模式
119
+ if (textArgs.length > 0) {
120
+ // 翻译模式
121
+ if (textArgs.length === 0) {
122
+ console.log('❌ 请提供要翻译的文本');
123
+ console.log('\n示例:');
124
+ console.log(' pua jargon "帮我做个功能"');
125
+ console.log(' pua jargon "我们一起讨论这个问题"');
126
+ return;
127
+ }
128
+ const input = textArgs.join(' ');
129
+ const translated = translateToJargon(input, intensity);
130
+ console.log();
131
+ console.log('📝 原文:', input);
132
+ console.log('🎯 黑话:', translated);
133
+ console.log();
134
+ }
135
+ else {
136
+ // 生成模式
137
+ const jargonList = getJargonByType(type);
138
+ const filteredJargon = filterByIntensity(jargonList, intensity);
139
+ console.log();
140
+ console.log(`🎯 职场黑话生成器 [${type.toUpperCase()} - ${intensity.toUpperCase()}]`);
141
+ console.log(chalk_1.default.gray('─'.repeat(50)));
142
+ console.log();
143
+ filteredJargon.forEach((word, index) => {
144
+ console.log(` ${index + 1}. ${chalk_1.default.cyan(word)}`);
145
+ });
146
+ console.log();
147
+ console.log('💡 示例句子:');
148
+ console.log(chalk_1.default.gray(generateJargonSentence(type, intensity)));
149
+ console.log();
150
+ }
151
+ });
152
+ return command;
153
+ }
@@ -0,0 +1,384 @@
1
+ "use strict";
2
+ /**
3
+ * 会议室命令 - 多角色同时参会
4
+ * 用户发言后多个角色随机搭话回复
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ var __importDefault = (this && this.__importDefault) || function (mod) {
40
+ return (mod && mod.__esModule) ? mod : { "default": mod };
41
+ };
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.createMeetingRoomCommand = createMeetingRoomCommand;
44
+ const readline_1 = __importDefault(require("readline"));
45
+ const commander_1 = require("commander");
46
+ const chalk_1 = __importDefault(require("chalk"));
47
+ const ora_1 = __importDefault(require("ora"));
48
+ const factory_1 = require("../llm/factory");
49
+ const settings_1 = require("../config/settings");
50
+ const index_1 = require("../prompts/index");
51
+ const meeting_prompts_1 = require("../prompts/meeting-prompts");
52
+ const meeting_utils_1 = require("../utils/meeting-utils");
53
+ const logger_1 = require("../utils/logger");
54
+ const CHALK_COLORS = {
55
+ red: chalk_1.default.red,
56
+ yellow: chalk_1.default.yellow,
57
+ cyan: chalk_1.default.cyan,
58
+ magenta: chalk_1.default.magenta,
59
+ blue: chalk_1.default.blue,
60
+ green: chalk_1.default.green,
61
+ };
62
+ function getChalkColor(role) {
63
+ return CHALK_COLORS[index_1.ROLE_COLORS[role]] || chalk_1.default.white;
64
+ }
65
+ /**
66
+ * 渲染角色消息框
67
+ */
68
+ function renderRoleMessage(role, content) {
69
+ const emoji = index_1.ROLE_EMOJIS[role];
70
+ const name = meeting_prompts_1.CHARACTER_NAMES[role];
71
+ const title = meeting_prompts_1.CHARACTER_TITLES[role];
72
+ const colorFn = getChalkColor(role);
73
+ const header = `${emoji} ${name} (${title})`;
74
+ const width = 50;
75
+ const topLine = colorFn(`┌─ ${header} ${'─'.repeat(Math.max(0, width - header.length - 4))}┐`);
76
+ const bottomLine = colorFn(`└${'─'.repeat(width - 1)}┘`);
77
+ // Wrap content to fit box width
78
+ const maxContentWidth = width - 4;
79
+ const lines = wrapText(content, maxContentWidth);
80
+ console.log(topLine);
81
+ for (const line of lines) {
82
+ console.log(colorFn('│') + ` ${line.padEnd(maxContentWidth + 1)}` + colorFn('│'));
83
+ }
84
+ console.log(bottomLine);
85
+ console.log();
86
+ }
87
+ /**
88
+ * 简单文本换行
89
+ */
90
+ function wrapText(text, maxWidth) {
91
+ const lines = [];
92
+ let remaining = text;
93
+ while (remaining.length > maxWidth) {
94
+ let breakIdx = remaining.lastIndexOf(' ', maxWidth);
95
+ if (breakIdx <= 0)
96
+ breakIdx = maxWidth;
97
+ lines.push(remaining.slice(0, breakIdx));
98
+ remaining = remaining.slice(breakIdx).trimStart();
99
+ }
100
+ if (remaining.length > 0) {
101
+ lines.push(remaining);
102
+ }
103
+ return lines.length > 0 ? lines : [''];
104
+ }
105
+ /**
106
+ * 渲染会议事件
107
+ */
108
+ function renderEvent(text) {
109
+ console.log(chalk_1.default.gray(` ── ${text} ──`));
110
+ console.log();
111
+ }
112
+ /**
113
+ * 渲染评分卡
114
+ */
115
+ function renderScoreCard(messages, participants) {
116
+ const card = (0, meeting_utils_1.generateScoreCard)(messages.map(m => ({ role: m.role, content: m.content })), participants);
117
+ const stars = '★'.repeat(card.rating) + '☆'.repeat(5 - card.rating);
118
+ console.log();
119
+ console.log(chalk_1.default.yellow.bold('╔════════════════════════════════════╗'));
120
+ console.log(chalk_1.default.yellow.bold('║') + ' 📊 ' + chalk_1.default.white.bold('会议评分卡') + ' ' + chalk_1.default.yellow.bold('║'));
121
+ console.log(chalk_1.default.yellow.bold('╠════════════════════════════════════╣'));
122
+ console.log(chalk_1.default.yellow.bold('║') + ` 总消息数: ${String(card.totalMessages).padEnd(20)}` + chalk_1.default.yellow.bold('║'));
123
+ console.log(chalk_1.default.yellow.bold('║') + ` 画饼次数: ${chalk_1.default.red(String(card.cakePaintCount) + '次').padEnd(29)}` + chalk_1.default.yellow.bold('║'));
124
+ console.log(chalk_1.default.yellow.bold('║') + ` 黑话密度: ${chalk_1.default.cyan(card.jargonDensity + '%').padEnd(29)}` + chalk_1.default.yellow.bold('║'));
125
+ console.log(chalk_1.default.yellow.bold('║') + ` 有效决策: ${chalk_1.default.green(card.effectiveDecisions + '个').padEnd(29)}` + chalk_1.default.yellow.bold('║'));
126
+ console.log(chalk_1.default.yellow.bold('║') + ` 打断次数: ${String(card.interruptCount + '次').padEnd(20)}` + chalk_1.default.yellow.bold('║'));
127
+ console.log(chalk_1.default.yellow.bold('║') + ` 最活跃: ${card.topContributor.padEnd(20)}` + chalk_1.default.yellow.bold('║'));
128
+ console.log(chalk_1.default.yellow.bold('║') + ' ' + chalk_1.default.yellow.bold('║'));
129
+ console.log(chalk_1.default.yellow.bold('║') + ` ${stars} ` + chalk_1.default.yellow.bold('║'));
130
+ console.log(chalk_1.default.yellow.bold('║') + ' ' + chalk_1.default.yellow.bold('║'));
131
+ console.log(chalk_1.default.yellow.bold('║') + ` "${chalk_1.default.gray(card.summary)}"` + chalk_1.default.yellow.bold(''));
132
+ console.log(chalk_1.default.yellow.bold('╚════════════════════════════════════╝'));
133
+ if (card.goldQuote) {
134
+ console.log();
135
+ console.log(chalk_1.default.yellow('💎 金句: ') + chalk_1.default.gray(card.goldQuote));
136
+ }
137
+ console.log();
138
+ }
139
+ /**
140
+ * 渲染会议纪要
141
+ */
142
+ function renderMinutes(state) {
143
+ const meetingName = meeting_prompts_1.MEETING_TYPE_NAMES[state.meetingType];
144
+ const participantNames = state.participants.map(r => `${meeting_prompts_1.CHARACTER_NAMES[r]}(${meeting_prompts_1.CHARACTER_TITLES[r]})`).join('、');
145
+ console.log();
146
+ console.log(chalk_1.default.cyan.bold('══════════════════════════════════════'));
147
+ console.log(chalk_1.default.cyan.bold(' 📝 会议纪要'));
148
+ console.log(chalk_1.default.cyan.bold('══════════════════════════════════════'));
149
+ console.log();
150
+ console.log(chalk_1.default.white(`会议类型: ${meetingName}`));
151
+ console.log(chalk_1.default.white(`参会人员: ${participantNames}`));
152
+ console.log(chalk_1.default.white(`消息总数: ${state.messages.length}`));
153
+ console.log();
154
+ console.log(chalk_1.default.gray('── 对话记录 ──'));
155
+ console.log();
156
+ for (const msg of state.messages) {
157
+ if (msg.role === 'user') {
158
+ console.log(chalk_1.default.green(` [你]: ${msg.content}`));
159
+ }
160
+ else {
161
+ const colorFn = getChalkColor(msg.role);
162
+ console.log(colorFn(` [${msg.name}]: ${msg.content}`));
163
+ }
164
+ }
165
+ console.log();
166
+ console.log(chalk_1.default.cyan.bold('══════════════════════════════════════'));
167
+ console.log();
168
+ }
169
+ /**
170
+ * 清理 AI 回复 - 去除泄漏的上下文格式
171
+ */
172
+ function cleanMeetingResponse(raw, currentRole) {
173
+ let cleaned = raw.trim();
174
+ // 去除开头的 [角色名]: 或 角色名: 格式
175
+ const allNames = Object.values(meeting_prompts_1.CHARACTER_NAMES);
176
+ for (const name of allNames) {
177
+ cleaned = cleaned.replace(new RegExp(`^\\[${name}\\][::]\\s*`, 'g'), '');
178
+ cleaned = cleaned.replace(new RegExp(`^${name}[::]\\s*`, 'g'), '');
179
+ }
180
+ // 去除回复中夹带的其他角色发言
181
+ for (const name of allNames) {
182
+ if (name === meeting_prompts_1.CHARACTER_NAMES[currentRole])
183
+ continue;
184
+ cleaned = cleaned.replace(new RegExp(`\\s*\\[${name}\\][::][^\\n]*`, 'g'), '');
185
+ }
186
+ // 去除多余的引号包裹
187
+ cleaned = cleaned.replace(/^["「](.+)["」]$/, '$1');
188
+ cleaned = cleaned.trim();
189
+ if (cleaned.length < 2 || cleaned === '...' || cleaned === '……') {
190
+ return '...';
191
+ }
192
+ return cleaned;
193
+ }
194
+ function createMeetingRoomCommand() {
195
+ const command = new commander_1.Command('meeting-room')
196
+ .description('会议室 - 多角色同时参会的职场会议模拟')
197
+ .option('-p, --provider <zhipu|openai>', 'AI 服务提供商')
198
+ .option('-m, --model <model>', '模型名称');
199
+ command.action(async (options) => {
200
+ try {
201
+ // Dynamic import for @inquirer/prompts (ESM)
202
+ const { checkbox, select } = await Promise.resolve().then(() => __importStar(require('@inquirer/prompts')));
203
+ // Step 1: Select participants
204
+ console.log();
205
+ console.log(chalk_1.default.cyan.bold('🏢 职场会议室'));
206
+ console.log(chalk_1.default.gray('选择参会角色,开始一场充满PUA的会议'));
207
+ console.log();
208
+ const participants = await checkbox({
209
+ message: '选择参会角色(空格选择,至少2人)',
210
+ choices: ['boss', 'employee', 'pm', 'hr', 'techlead', 'intern'].map(role => ({
211
+ name: `${index_1.ROLE_EMOJIS[role]} ${meeting_prompts_1.CHARACTER_NAMES[role]} (${meeting_prompts_1.CHARACTER_TITLES[role]}) - ${meeting_prompts_1.CHARACTER_TAGS[role]}`,
212
+ value: role,
213
+ })),
214
+ });
215
+ if (participants.length < 2) {
216
+ logger_1.logger.error('至少需要选择 2 个参会角色');
217
+ return;
218
+ }
219
+ // Step 2: Select meeting type
220
+ const meetingType = await select({
221
+ message: '选择会议类型',
222
+ choices: Object.entries(meeting_prompts_1.MEETING_TYPE_NAMES).map(([value, name]) => ({
223
+ name,
224
+ value,
225
+ })),
226
+ });
227
+ // Step 3: Select chaos level
228
+ const chaosLevel = await select({
229
+ message: '选择混乱程度',
230
+ choices: [
231
+ { name: '🧊 有序 - 礼貌发言,点到为止', value: 1 },
232
+ { name: '🔥 标准 - 正常发挥,有些冲突', value: 2 },
233
+ { name: '💥 混乱 - 抢话打断,不留情面', value: 3 },
234
+ ],
235
+ });
236
+ // Load config
237
+ const config = (0, settings_1.loadConfig)(options);
238
+ const llm = (0, factory_1.createLLM)(config.provider, {
239
+ apiKey: config.apiKey,
240
+ model: config.model,
241
+ baseUrl: (0, settings_1.getProviderBaseUrl)(config.provider),
242
+ });
243
+ // Initialize state
244
+ const state = {
245
+ participants,
246
+ meetingType,
247
+ chaosLevel,
248
+ messages: [],
249
+ lastRespondents: [],
250
+ };
251
+ // Print meeting header
252
+ const meetingName = meeting_prompts_1.MEETING_TYPE_NAMES[meetingType];
253
+ const chaosLabels = { 1: '有序', 2: '标准', 3: '混乱' };
254
+ const participantNames = participants.map(r => `${index_1.ROLE_EMOJIS[r]} ${meeting_prompts_1.CHARACTER_NAMES[r]}`).join(' ');
255
+ console.log();
256
+ console.log(chalk_1.default.cyan.bold('╔════════════════════════════════════════════════════╗'));
257
+ console.log(chalk_1.default.cyan.bold('║') + chalk_1.default.white.bold(` 🏢 ${meetingName} 开始了! `) + chalk_1.default.cyan.bold('║'));
258
+ console.log(chalk_1.default.cyan.bold('╠════════════════════════════════════════════════════╣'));
259
+ console.log(chalk_1.default.cyan.bold('║') + ` 参会: ${participantNames}`);
260
+ console.log(chalk_1.default.cyan.bold('║') + ` 混乱: ${chaosLabels[chaosLevel]} | Provider: ${config.provider}`);
261
+ console.log(chalk_1.default.cyan.bold('╚════════════════════════════════════════════════════╝'));
262
+ console.log();
263
+ console.log(chalk_1.default.gray('输入消息开始讨论。支持: /exit 退出 | /minutes 会议纪要 | /score 评分卡'));
264
+ console.log();
265
+ // Start interactive loop
266
+ const rl = readline_1.default.createInterface({
267
+ input: process.stdin,
268
+ output: process.stdout,
269
+ prompt: chalk_1.default.green('你 ❯ '),
270
+ });
271
+ rl.prompt();
272
+ rl.on('line', async (input) => {
273
+ const trimmed = input.trim();
274
+ if (!trimmed) {
275
+ rl.prompt();
276
+ return;
277
+ }
278
+ // Handle commands
279
+ if (trimmed === '/exit' || trimmed === '/quit') {
280
+ renderScoreCard(state.messages, state.participants);
281
+ rl.close();
282
+ return;
283
+ }
284
+ if (trimmed === '/minutes') {
285
+ renderMinutes(state);
286
+ rl.prompt();
287
+ return;
288
+ }
289
+ if (trimmed === '/score') {
290
+ renderScoreCard(state.messages, state.participants);
291
+ rl.prompt();
292
+ return;
293
+ }
294
+ if (trimmed === '/help') {
295
+ console.log();
296
+ console.log(chalk_1.default.bold('会议室命令:'));
297
+ console.log(chalk_1.default.gray('─').repeat(40));
298
+ console.log(' /score 查看会议评分卡');
299
+ console.log(' /minutes 查看会议纪要');
300
+ console.log(' /exit 结束会议');
301
+ console.log();
302
+ rl.prompt();
303
+ return;
304
+ }
305
+ // Add user message
306
+ state.messages.push({ role: 'user', name: '你', content: trimmed });
307
+ // Maybe trigger a random event
308
+ const event = (0, meeting_utils_1.generateMeetingEvent)(participants, 0.1);
309
+ if (event) {
310
+ renderEvent(event.text);
311
+ }
312
+ // Select respondents
313
+ const respondents = (0, meeting_utils_1.selectRespondents)(participants, trimmed, chaosLevel, state.lastRespondents);
314
+ // Build context for LLM calls
315
+ const historyMessages = state.messages.slice(-8).map(m => ({
316
+ role: 'user',
317
+ content: m.role === 'user' ? m.content : `(${m.name}说:"${m.content}")`,
318
+ }));
319
+ const spinner = (0, ora_1.default)({ text: '角色们在讨论...', spinner: 'dots' }).start();
320
+ const respondentResults = [];
321
+ for (const role of respondents) {
322
+ const systemPrompt = (0, meeting_prompts_1.getMeetingPrompt)(role, meetingType, chaosLevel, participants);
323
+ // Include previous respondents' messages in context (narrative format)
324
+ const prevSpeech = respondentResults.map(r => `${r.name}说:"${r.content}"`).join('\n');
325
+ const contextWithPrev = [
326
+ ...historyMessages,
327
+ ...(prevSpeech ? [{ role: 'user', content: `(会议中其他人的发言:\n${prevSpeech})` }] : []),
328
+ ];
329
+ const messages = [
330
+ { role: 'system', content: systemPrompt },
331
+ ...contextWithPrev,
332
+ ];
333
+ try {
334
+ const rawReply = await llm.chat(messages);
335
+ const reply = cleanMeetingResponse(rawReply, role);
336
+ respondentResults.push({
337
+ role,
338
+ name: meeting_prompts_1.CHARACTER_NAMES[role],
339
+ content: reply,
340
+ });
341
+ }
342
+ catch (err) {
343
+ // Skip failed roles
344
+ logger_1.logger.warning(`${meeting_prompts_1.CHARACTER_NAMES[role]} 回复失败`);
345
+ }
346
+ }
347
+ spinner.stop();
348
+ if (respondentResults.length === 0) {
349
+ console.log(chalk_1.default.red(' 所有角色都没有回复,请检查 API 配置'));
350
+ }
351
+ else {
352
+ // Render each response
353
+ for (const result of respondentResults) {
354
+ renderRoleMessage(result.role, result.content);
355
+ const mood = (0, meeting_utils_1.detectMood)(result.content);
356
+ state.messages.push({
357
+ role: result.role,
358
+ name: result.name,
359
+ content: result.content,
360
+ mood,
361
+ });
362
+ }
363
+ state.lastRespondents = respondentResults.map(r => r.role);
364
+ }
365
+ rl.prompt();
366
+ });
367
+ rl.on('close', () => {
368
+ console.log();
369
+ logger_1.logger.info('会议结束,再见!');
370
+ process.exit(0);
371
+ });
372
+ }
373
+ catch (error) {
374
+ if (error.message?.includes('cancelled') || error.message?.includes('User force closed')) {
375
+ console.log();
376
+ logger_1.logger.info('已取消');
377
+ return;
378
+ }
379
+ logger_1.logger.error(error instanceof Error ? error.message : String(error));
380
+ process.exit(1);
381
+ }
382
+ });
383
+ return command;
384
+ }