closer-code 1.0.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 (100) hide show
  1. package/.env.example +83 -0
  2. package/API_GUIDE.md +1411 -0
  3. package/AUTO_MKDIR_IMPROVEMENT.md +354 -0
  4. package/CLAUDE.md +55 -0
  5. package/CTRL_C_EXPERIMENT.md +90 -0
  6. package/PROJECT_CLEANUP_SUMMARY.md +121 -0
  7. package/README.md +686 -0
  8. package/cloco.md +51 -0
  9. package/config.example.json +116 -0
  10. package/dist/bash-runner.js +128 -0
  11. package/dist/batch-cli.js +20736 -0
  12. package/dist/closer-cli.js +21190 -0
  13. package/dist/index.js +31228 -0
  14. package/docs/EXPORT_COMMAND.md +152 -0
  15. package/docs/FILE_NAMING_IMPROVEMENT.md +168 -0
  16. package/docs/GLOBAL_CONFIG.md +128 -0
  17. package/docs/LONG_MESSAGE_DISPLAY_FIX.md +202 -0
  18. package/docs/PROJECT_HISTORY_ISOLATION.md +315 -0
  19. package/docs/QUICK_START_HISTORY.md +207 -0
  20. package/docs/TASK_PROGRESS_FEATURE.md +190 -0
  21. package/docs/THINKING_CONTENT_RESEARCH.md +267 -0
  22. package/docs/THINKING_FEATURE.md +187 -0
  23. package/docs/THINKING_IMPROVEMENT_COMPARISON.md +193 -0
  24. package/docs/THINKING_OPTIMIZATION_SUMMARY.md +242 -0
  25. package/docs/UI_IMPROVEMENTS_2025-01-18.md +256 -0
  26. package/docs/WHY_THINKING_SHORT.md +201 -0
  27. package/package.json +49 -0
  28. package/scenarios/README.md +234 -0
  29. package/scenarios/run-all-scenarios.js +342 -0
  30. package/scenarios/scenario1-batch-converter.js +247 -0
  31. package/scenarios/scenario2-code-analyzer.js +375 -0
  32. package/scenarios/scenario3-doc-generator.js +371 -0
  33. package/scenarios/scenario4-log-analyzer.js +496 -0
  34. package/scenarios/scenario5-tdd-helper.js +681 -0
  35. package/src/ai-client-legacy.js +171 -0
  36. package/src/ai-client.js +221 -0
  37. package/src/bash-runner.js +148 -0
  38. package/src/batch-cli.js +327 -0
  39. package/src/cli.jsx +166 -0
  40. package/src/closer-cli.jsx +1103 -0
  41. package/src/closer-cli.jsx.backup +948 -0
  42. package/src/commands/batch.js +62 -0
  43. package/src/commands/chat.js +10 -0
  44. package/src/commands/config.js +154 -0
  45. package/src/commands/help.js +76 -0
  46. package/src/commands/history.js +192 -0
  47. package/src/commands/setup.js +17 -0
  48. package/src/commands/upgrade.js +101 -0
  49. package/src/commands/workflow-tests.js +125 -0
  50. package/src/config.js +343 -0
  51. package/src/conversation.js +962 -0
  52. package/src/git-helper.js +349 -0
  53. package/src/index.js +88 -0
  54. package/src/logger.js +347 -0
  55. package/src/plan.js +193 -0
  56. package/src/planner.js +397 -0
  57. package/src/search.js +195 -0
  58. package/src/setup.js +147 -0
  59. package/src/shortcuts.js +269 -0
  60. package/src/snippets.js +430 -0
  61. package/src/test-modules.js +118 -0
  62. package/src/tools.js +398 -0
  63. package/src/utils/cli.js +124 -0
  64. package/src/utils/validator.js +184 -0
  65. package/src/utils/version.js +33 -0
  66. package/src/utils/workflow-test.js +271 -0
  67. package/src/utils/workflow.js +268 -0
  68. package/test/demo-file-naming.js +92 -0
  69. package/test/demo-thinking.js +124 -0
  70. package/test/final-verification-report.md +303 -0
  71. package/test/research-thinking.js +130 -0
  72. package/test/test-auto-mkdir.js +123 -0
  73. package/test/test-e2e-empty-dir.md +108 -0
  74. package/test/test-export-logic.js +119 -0
  75. package/test/test-global-cloco.js +126 -0
  76. package/test/test-history-isolation.js +291 -0
  77. package/test/test-improved-thinking.js +43 -0
  78. package/test/test-long-message.js +65 -0
  79. package/test/test-plan-functionality.js +95 -0
  80. package/test/test-real-scenario.js +216 -0
  81. package/test/test-thinking-display.js +65 -0
  82. package/test/ui-verification-test.js +203 -0
  83. package/test/verify-history-isolation.sh +71 -0
  84. package/test/verify-thinking.js +339 -0
  85. package/test/workflows/empty-dir-creation.md +51 -0
  86. package/test/workflows/inventor/ascii-teacup.js +199 -0
  87. package/test/workflows/inventor/ascii-teacup.mjs +199 -0
  88. package/test/workflows/inventor/ascii_apple.hs +84 -0
  89. package/test/workflows/inventor/ascii_apple.py +91 -0
  90. package/test/workflows/inventor/cloco.md +3 -0
  91. package/test/workflows/longtalk/cloco.md +19 -0
  92. package/test/workflows/longtalk/emoji_500.txt +63 -0
  93. package/test/workflows/longtalk/emoji_list.txt +20 -0
  94. package/test/workflows/programmer/adder.md +33 -0
  95. package/test/workflows/programmer/expect.md +2 -0
  96. package/test/workflows/programmer/prompt.md +3 -0
  97. package/test/workflows/test-empty-dir-creation.js +113 -0
  98. package/test-ctrl-c.jsx +126 -0
  99. package/test-manual-file-creation.js +151 -0
  100. package/winfix.md +3 -0
@@ -0,0 +1,33 @@
1
+ /**
2
+ * 版本信息工具
3
+ */
4
+
5
+ import { readFileSync } from 'fs';
6
+ import { fileURLToPath } from 'url';
7
+ import { dirname, join } from 'path';
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = dirname(__filename);
11
+
12
+ /**
13
+ * 获取包版本号
14
+ * @returns {string} 版本号
15
+ */
16
+ export function getVersion() {
17
+ try {
18
+ const packagePath = join(__dirname, '../../package.json');
19
+ const packageJson = JSON.parse(readFileSync(packagePath, 'utf-8'));
20
+ return packageJson.version || '1.0.0';
21
+ } catch (error) {
22
+ return '1.0.0';
23
+ }
24
+ }
25
+
26
+ /**
27
+ * 显示版本信息
28
+ */
29
+ export function showVersion() {
30
+ const version = getVersion();
31
+ console.log(`cloco v${version}`);
32
+ console.log('AI 编程助理 - 通过对话完成编码、调试和任务规划');
33
+ }
@@ -0,0 +1,271 @@
1
+ /**
2
+ * Workflow 测试工具
3
+ * 用于执行两轮对话的 workflow 测试
4
+ */
5
+
6
+ import fs from 'fs/promises';
7
+ import path from 'path';
8
+ import { createConversation, WORKFLOW_PROMPT_PREFIX } from '../conversation.js';
9
+ import { loadConfig } from '../config.js';
10
+
11
+ /**
12
+ * 列出所有可用的 workflow 测试
13
+ */
14
+ export async function listWorkflows() {
15
+ const workflowsDir = path.resolve('test/workflows');
16
+
17
+ try {
18
+ const entries = await fs.readdir(workflowsDir, { withFileTypes: true });
19
+ const workflows = [];
20
+
21
+ for (const entry of entries) {
22
+ if (entry.isDirectory()) {
23
+ const testDir = path.join(workflowsDir, entry.name);
24
+ const promptPath = path.join(testDir, 'prompt.md');
25
+ const expectPath = path.join(testDir, 'expect.md');
26
+
27
+ // 检查必需的文件是否存在
28
+ try {
29
+ await fs.access(promptPath);
30
+ await fs.access(expectPath);
31
+
32
+ // 读取描述
33
+ let description = '';
34
+ try {
35
+ const descPath = path.join(testDir, 'description.md');
36
+ description = await fs.readFile(descPath, 'utf-8');
37
+ } catch {
38
+ // description.md 可选
39
+ }
40
+
41
+ workflows.push({
42
+ name: entry.name,
43
+ path: testDir,
44
+ description: description.trim()
45
+ });
46
+ } catch {
47
+ // 缺少必需文件,跳过
48
+ }
49
+ }
50
+ }
51
+
52
+ return workflows;
53
+ } catch (error) {
54
+ if (error.code === 'ENOENT') {
55
+ return [];
56
+ }
57
+ throw error;
58
+ }
59
+ }
60
+
61
+ /**
62
+ * 运行单个 workflow 测试
63
+ * @param {string} testName - 测试名称
64
+ * @param {Object} options - 选项
65
+ * @returns {Object} 测试结果
66
+ */
67
+ export async function runWorkflowTest(testName, options = {}) {
68
+ const workflowsDir = path.resolve('test/workflows');
69
+ const testDir = path.join(workflowsDir, testName);
70
+ const promptPath = path.join(testDir, 'prompt.md');
71
+ const expectPath = path.join(testDir, 'expect.md');
72
+
73
+ // 验证测试目录存在
74
+ try {
75
+ await fs.access(testDir);
76
+ await fs.access(promptPath);
77
+ await fs.access(expectPath);
78
+ } catch (error) {
79
+ throw new Error(`Workflow test "${testName}" not found or incomplete`);
80
+ }
81
+
82
+ // 读取文件
83
+ const prompt = await fs.readFile(promptPath, 'utf-8');
84
+ const expect = await fs.readFile(expectPath, 'utf-8');
85
+
86
+ // 加载配置
87
+ const config = await loadConfig();
88
+
89
+ // 设置工作目录为测试目录
90
+ config.behavior.workingDir = testDir;
91
+
92
+ // 创建对话会话(workflow 测试模式)
93
+ const conversation = await createConversation(config, true);
94
+
95
+ const startTime = Date.now();
96
+ const iterations = [];
97
+
98
+ try {
99
+ // 第1轮:执行任务
100
+ if (options.verbose) {
101
+ console.log(`\n${'='.repeat(60)}`);
102
+ console.log(`第1轮: 执行任务`);
103
+ console.log(`${'='.repeat(60)}`);
104
+ }
105
+
106
+ const response1 = await conversation.sendMessage(
107
+ WORKFLOW_PROMPT_PREFIX + '\n\n' + prompt,
108
+ (event) => {
109
+ if (options.verbose) {
110
+ handleProgress(event, options);
111
+ }
112
+ }
113
+ );
114
+
115
+ iterations.push({
116
+ round: 1,
117
+ content: response1.content,
118
+ toolCalls: response1.toolCalls || []
119
+ });
120
+
121
+ if (options.verbose) {
122
+ console.log(`\n${'─'.repeat(60)}`);
123
+ console.log(`响应:`, response1.content);
124
+ console.log(`${'─'.repeat(60)}`);
125
+ }
126
+
127
+ // 第2轮:验证结果
128
+ if (options.verbose) {
129
+ console.log(`\n${'='.repeat(60)}`);
130
+ console.log(`第2轮: 验证结果`);
131
+ console.log(`${'='.repeat(60)}`);
132
+ }
133
+
134
+ const response2 = await conversation.sendMessage(
135
+ WORKFLOW_PROMPT_PREFIX + '\n\n' + expect,
136
+ (event) => {
137
+ if (options.verbose) {
138
+ handleProgress(event, options);
139
+ }
140
+ }
141
+ );
142
+
143
+ iterations.push({
144
+ round: 2,
145
+ content: response2.content,
146
+ toolCalls: response2.toolCalls || []
147
+ });
148
+
149
+ if (options.verbose) {
150
+ console.log(`\n${'─'.repeat(60)}`);
151
+ console.log(`响应:`, response2.content);
152
+ console.log(`${'─'.repeat(60)}`);
153
+ }
154
+
155
+ // 检查验收结果
156
+ const passed = response2.content?.includes('WORKFLOW TEST AS EXPECTED');
157
+ const endTime = Date.now();
158
+ const duration = endTime - startTime;
159
+
160
+ console.log(`\n[DEBUG] Response 2 content:`, response2.content?.substring(0, 200));
161
+ console.log(`[DEBUG] Passed:`, passed);
162
+
163
+ return {
164
+ name: testName,
165
+ passed,
166
+ duration,
167
+ iterations,
168
+ error: null
169
+ };
170
+
171
+ } catch (error) {
172
+ const endTime = Date.now();
173
+ const duration = endTime - startTime;
174
+
175
+ console.error(`[DEBUG] Error:`, error.message);
176
+ console.error(`[DEBUG] Stack:`, error.stack);
177
+
178
+ return {
179
+ name: testName,
180
+ passed: false,
181
+ duration,
182
+ iterations,
183
+ error: error.message
184
+ };
185
+ }
186
+ }
187
+
188
+ /**
189
+ * 处理进度事件
190
+ */
191
+ function handleProgress(event, options) {
192
+ switch (event.type) {
193
+ case 'token':
194
+ process.stdout.write(event.content);
195
+ break;
196
+ case 'tool_start':
197
+ console.log(`\n[工具调用] ${event.tool}`, JSON.stringify(event.input, null, 2));
198
+ break;
199
+ case 'tool_complete':
200
+ console.log(`[工具完成] ${event.tool}`);
201
+ if (options.debug) {
202
+ console.log(JSON.stringify(event.result, null, 2));
203
+ }
204
+ break;
205
+ case 'error':
206
+ console.error(`\n[错误]`, event.error);
207
+ break;
208
+ }
209
+ }
210
+
211
+ /**
212
+ * 格式化测试结果输出
213
+ */
214
+ export function formatTestResult(result, options = {}) {
215
+ const { name, passed, duration, error } = result;
216
+
217
+ if (options.json) {
218
+ return JSON.stringify(result, null, 2);
219
+ }
220
+
221
+ const statusIcon = passed ? '✅' : '❌';
222
+ const statusText = passed ? '通过' : '失败';
223
+ const durationSec = (duration / 1000).toFixed(2);
224
+
225
+ let output = `\n${statusIcon} ${name} - ${statusText} (${durationSec}s)`;
226
+
227
+ if (error) {
228
+ output += `\n 错误: ${error}`;
229
+ }
230
+
231
+ if (options.verbose && result.iterations) {
232
+ result.iterations.forEach((iter, idx) => {
233
+ output += `\n\n 第${idx + 1}轮对话:`;
234
+ output += `\n ${'─'.repeat(50)}`;
235
+ output += `\n ${iter.content.split('\n').join('\n ')}`;
236
+
237
+ if (iter.toolCalls && iter.toolCalls.length > 0) {
238
+ output += `\n 工具调用: ${iter.toolCalls.join(', ')}`;
239
+ }
240
+ });
241
+ }
242
+
243
+ return output;
244
+ }
245
+
246
+ /**
247
+ * 格式化多个测试结果
248
+ */
249
+ export function formatTestResults(results, options = {}) {
250
+ if (options.json) {
251
+ return JSON.stringify(results, null, 2);
252
+ }
253
+
254
+ const passed = results.filter(r => r.passed).length;
255
+ const failed = results.filter(r => !r.passed).length;
256
+ const total = results.length;
257
+
258
+ let output = `\n${'='.repeat(60)}`;
259
+ output += `\nWorkflow 测试结果汇总`;
260
+ output += `\n${'='.repeat(60)}`;
261
+ output += `\n总计: ${total}, 通过: ${passed}, 失败: ${failed}`;
262
+ output += `\n${'='.repeat(60)}\n`;
263
+
264
+ results.forEach(result => {
265
+ output += formatTestResult(result, options);
266
+ });
267
+
268
+ output += `\n${'='.repeat(60)}\n`;
269
+
270
+ return output;
271
+ }
@@ -0,0 +1,268 @@
1
+ /**
2
+ * Workflow 自动化执行引擎
3
+ * 支持多轮对话迭代和验收标准验证
4
+ */
5
+
6
+ import fs from 'fs/promises';
7
+ import path from 'path';
8
+ import { createConversation } from '../conversation.js';
9
+ import { validateExpect } from './validator.js';
10
+ import { showSuccess, showError, showInfo, showWarning } from './cli.js';
11
+
12
+ /**
13
+ * 解析 workflow 路径
14
+ */
15
+ function resolveWorkflowPath(workflowPath) {
16
+ // 如果是绝对路径,直接使用
17
+ if (path.isAbsolute(workflowPath)) {
18
+ return workflowPath;
19
+ }
20
+
21
+ // 尝试相对于当前工作目录
22
+ const relativeToCwd = path.resolve(process.cwd(), workflowPath);
23
+
24
+ // 尝试相对于项目根目录的 workflow 目录
25
+ const relativeToProject = path.resolve(process.cwd(), 'workflow', workflowPath);
26
+
27
+ // 检查哪个路径存在
28
+ return relativeToCwd; // 优先使用用户提供的路径
29
+ }
30
+
31
+ /**
32
+ * 加载 workflow 案例
33
+ */
34
+ async function loadWorkflowCase(workflowDir) {
35
+ const promptPath = path.join(workflowDir, 'prompt.md');
36
+ const expectPath = path.join(workflowDir, 'expect.md');
37
+
38
+ let prompt, expect;
39
+
40
+ try {
41
+ prompt = await fs.readFile(promptPath, 'utf-8');
42
+ } catch (error) {
43
+ throw new Error(`无法读取 prompt.md: ${error.message}`);
44
+ }
45
+
46
+ try {
47
+ expect = await fs.readFile(expectPath, 'utf-8');
48
+ } catch (error) {
49
+ throw new Error(`无法读取 expect.md: ${error.message}`);
50
+ }
51
+
52
+ return { prompt: prompt.trim(), expect: expect.trim(), workflowDir };
53
+ }
54
+
55
+ /**
56
+ * 处理进度事件
57
+ */
58
+ function handleProgress(event, options) {
59
+ if (!options.verbose && !options.debug) {
60
+ return;
61
+ }
62
+
63
+ switch (event.type) {
64
+ case 'token':
65
+ // 流式输出 token(仅在 debug 模式)
66
+ if (options.debug) {
67
+ process.stdout.write(event.token);
68
+ }
69
+ break;
70
+ case 'tool_start':
71
+ showInfo(`⚡ 执行工具: ${event.tool}`);
72
+ break;
73
+ case 'tool_complete':
74
+ if (event.error) {
75
+ showError(`工具 ${event.tool} 失败: ${event.error}`);
76
+ } else {
77
+ showSuccess(`工具 ${event.tool} 完成`);
78
+ }
79
+ break;
80
+ case 'error':
81
+ showError(`错误: ${event.error}`);
82
+ break;
83
+ }
84
+ }
85
+
86
+ /**
87
+ * 运行 workflow
88
+ */
89
+ export async function runWorkflow(workflowPath, args, options) {
90
+ const workflowDir = resolveWorkflowPath(workflowPath);
91
+
92
+ showInfo(`🎯 开始执行 workflow: ${workflowPath}`);
93
+
94
+ // 1. 加载案例
95
+ let caseData;
96
+ try {
97
+ caseData = await loadWorkflowCase(workflowDir);
98
+ console.log(`\n📝 需求:\n${caseData.prompt}\n`);
99
+ console.log(`✅ 验收标准:\n${caseData.expect}\n`);
100
+ } catch (error) {
101
+ showError(error.message);
102
+ return {
103
+ success: false,
104
+ error: error.message
105
+ };
106
+ }
107
+
108
+ // 2. 初始化配置
109
+ const maxIterations = parseInt(options.maxIterations) || 5;
110
+ console.log(`🔄 最大迭代次数: ${maxIterations}\n`);
111
+
112
+ // 3. 创建对话会话
113
+ let conversation;
114
+ try {
115
+ conversation = await createConversation();
116
+ } catch (error) {
117
+ showError(`创建对话失败: ${error.message}`);
118
+ return {
119
+ success: false,
120
+ error: `Failed to create conversation: ${error.message}`
121
+ };
122
+ }
123
+
124
+ // 4. 迭代执行
125
+ let lastValidationResult = null;
126
+
127
+ for (let iteration = 1; iteration <= maxIterations; iteration++) {
128
+ console.log(`\n${'='.repeat(50)}`);
129
+ console.log(`第 ${iteration}/${maxIterations} 轮`);
130
+ console.log('='.repeat(50));
131
+
132
+ // 构造用户消息
133
+ let userMessage;
134
+ if (iteration === 1) {
135
+ // 第一轮:发送原始需求
136
+ userMessage = caseData.prompt;
137
+ } else {
138
+ // 后续轮:告知 AI 继续改进
139
+ userMessage = `请继续改进。当前未满足验收标准:\n${caseData.expect}\n\n上次验证结果:${lastValidationResult?.reason || '请检查并改进'}`;
140
+ }
141
+
142
+ // 发送到 AI
143
+ let response;
144
+ try {
145
+ console.log(`\n📤 发送消息到 AI...`);
146
+
147
+ response = await conversation.sendMessage(userMessage, (event) => {
148
+ handleProgress(event, options);
149
+ });
150
+
151
+ // 显示 AI 响应摘要
152
+ const content = response.content || '';
153
+ const preview = content.length > 200 ? content.substring(0, 200) + '...' : content;
154
+ console.log(`\n🤖 AI 响应:\n${preview}\n`);
155
+
156
+ } catch (error) {
157
+ showError(`AI 交互失败: ${error.message}`);
158
+
159
+ return {
160
+ success: false,
161
+ iterations: iteration,
162
+ error: `AI interaction failed: ${error.message}`,
163
+ lastValidation: lastValidationResult
164
+ };
165
+ }
166
+
167
+ // 5. 验证结果
168
+ try {
169
+ console.log(`\n🔍 验证验收标准...`);
170
+
171
+ lastValidationResult = await validateExpect(
172
+ caseData.expect,
173
+ workflowDir,
174
+ options.verbose
175
+ );
176
+
177
+ if (lastValidationResult.passed) {
178
+ console.log(`\n${'='.repeat(50)}`);
179
+ showSuccess(`✅ 验收标准已满足!`);
180
+ console.log(`📊 总迭代次数: ${iteration}`);
181
+
182
+ if (lastValidationResult.details) {
183
+ console.log(`\n验证详情:`);
184
+ lastValidationResult.details.forEach(detail => {
185
+ console.log(` ✓ ${detail.check}`);
186
+ });
187
+ }
188
+
189
+ return {
190
+ success: true,
191
+ iterations: iteration,
192
+ validation: lastValidationResult,
193
+ response: response
194
+ };
195
+ } else {
196
+ showWarning(`验证未通过: ${lastValidationResult.reason}`);
197
+
198
+ if (lastValidationResult.details && options.verbose) {
199
+ console.log(`\n验证详情:`);
200
+ lastValidationResult.details.forEach(detail => {
201
+ console.log(` ${detail.passed ? '✓' : '✗'} ${detail.check}: ${detail.reason}`);
202
+ });
203
+ }
204
+ }
205
+
206
+ } catch (error) {
207
+ showError(`验证失败: ${error.message}`);
208
+
209
+ // 验证失败不应中断整个流程,记录后继续
210
+ lastValidationResult = {
211
+ passed: false,
212
+ reason: `Validation error: ${error.message}`
213
+ };
214
+ }
215
+ }
216
+
217
+ // 达到最大迭代次数
218
+ console.log(`\n${'='.repeat(50)}`);
219
+ showError(`❌ 达到最大迭代次数 (${maxIterations}),任务未完成`);
220
+
221
+ if (lastValidationResult) {
222
+ console.log(`\n最后验证结果: ${lastValidationResult.reason}`);
223
+ }
224
+
225
+ return {
226
+ success: false,
227
+ iterations: maxIterations,
228
+ reason: 'Max iterations reached',
229
+ lastValidation: lastValidationResult
230
+ };
231
+ }
232
+
233
+ /**
234
+ * 列出可用的 workflow 案例
235
+ */
236
+ export async function listWorkflows() {
237
+ const workflowDir = path.resolve(process.cwd(), 'workflow');
238
+
239
+ try {
240
+ const entries = await fs.readdir(workflowDir, { withFileTypes: true });
241
+ const workflows = [];
242
+
243
+ for (const entry of entries) {
244
+ if (entry.isDirectory()) {
245
+ const casePath = path.join(workflowDir, entry.name);
246
+ const promptPath = path.join(casePath, 'prompt.md');
247
+ const expectPath = path.join(casePath, 'expect.md');
248
+
249
+ try {
250
+ await fs.access(promptPath);
251
+ await fs.access(expectPath);
252
+
253
+ workflows.push({
254
+ name: entry.name,
255
+ path: casePath
256
+ });
257
+ } catch {
258
+ // 缺少必要文件,跳过
259
+ }
260
+ }
261
+ }
262
+
263
+ return workflows;
264
+ } catch (error) {
265
+ // workflow 目录不存在
266
+ return [];
267
+ }
268
+ }
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * 演示改进后的文件命名
4
+ *
5
+ * 文件名格式: {md5-hash}-{directory-name}.json
6
+ * 例如: 06aecb89a562f1a6038cca327538315e-project-beta.json
7
+ */
8
+
9
+ import {
10
+ loadHistory,
11
+ saveHistory,
12
+ listHistory,
13
+ clearHistory
14
+ } from '../src/config.js';
15
+ import path from 'path';
16
+ import crypto from 'crypto';
17
+
18
+ const colors = {
19
+ reset: '\x1b[0m',
20
+ green: '\x1b[32m',
21
+ blue: '\x1b[34m',
22
+ yellow: '\x1b[33m',
23
+ cyan: '\x1b[36m',
24
+ magenta: '\x1b[35m'
25
+ };
26
+
27
+ function log(message, color = 'reset') {
28
+ console.log(`${colors[color]}${message}${colors.reset}`);
29
+ }
30
+
31
+ console.log('\n' + '='.repeat(70));
32
+ log(' 改进后的文件命名演示', 'cyan');
33
+ console.log('='.repeat(70) + '\n');
34
+
35
+ // 测试项目路径
36
+ const testProjects = [
37
+ path.join(process.cwd(), 'test-projects', 'my-awesome-app'),
38
+ path.join(process.cwd(), 'test-projects', 'api-server'),
39
+ path.join(process.cwd(), 'test-projects', 'frontend-v2'),
40
+ path.join(process.cwd(), 'test-projects', 'data.processor'),
41
+ ];
42
+
43
+ log('📝 创建测试项目历史...\n', 'yellow');
44
+
45
+ // 为每个项目创建一些历史
46
+ testProjects.forEach((projectPath, i) => {
47
+ const history = [
48
+ { role: 'user', content: `项目 ${i + 1} 的第一条消息` },
49
+ { role: 'assistant', content: `收到!正在处理项目 ${i + 1}` }
50
+ ];
51
+ saveHistory(history, projectPath);
52
+ log(`✓ 已创建: ${path.basename(projectPath)}`, 'green');
53
+ });
54
+
55
+ log('\n📂 历史文件命名示例:\n', 'cyan');
56
+
57
+ // 列出所有项目
58
+ const projects = listHistory();
59
+
60
+ projects.forEach((p, i) => {
61
+ const dirName = path.basename(p.projectPath);
62
+ log(`${i + 1}. 项目: ${dirName}`, 'blue');
63
+ log(` 完整路径: ${p.projectPath}`, 'reset');
64
+ log(` 消息数: ${p.messageCount}`, 'green');
65
+ log(` 最后更新: ${new Date(p.lastUpdated).toLocaleString('zh-CN')}`, 'reset');
66
+
67
+ // 显示对应的文件名
68
+ const hash = crypto.createHash('md5').update(p.projectPath).digest('hex');
69
+ const cleanDirName = dirName.replace(/[^a-zA-Z0-9_-]/g, '_');
70
+ log(` 文件名: ${hash}-${cleanDirName}.json`, 'magenta');
71
+ log('', 'reset');
72
+ });
73
+
74
+ console.log('='.repeat(70));
75
+ log('✨ 文件命名优势:', 'cyan');
76
+ console.log('='.repeat(70));
77
+ log('1. 哈希值前缀 - 确保文件名唯一性', 'green');
78
+ log('2. 目录名后缀 - 便于人类识别和查阅', 'green');
79
+ log('3. 特殊字符处理 - 避免文件系统问题', 'green');
80
+ log('4. 示例对比:', 'yellow');
81
+ log(' ❌ 旧: 06aecb89a562f1a6038cca327538315e.json', 'red');
82
+ log(' ✅ 新: 06aecb89a562f1a6038cca327538315e-my-awesome-app.json', 'green');
83
+ console.log('='.repeat(70) + '\n');
84
+
85
+ // 清理测试数据
86
+ log('🧹 清理测试数据...\n', 'yellow');
87
+ testProjects.forEach(projectPath => {
88
+ clearHistory(projectPath);
89
+ });
90
+
91
+ log('✓ 清理完成', 'green');
92
+ log('\n演示结束!\n', 'cyan');