closer-code 1.0.0 → 1.0.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 (99) hide show
  1. package/.closer-code.example.json +32 -0
  2. package/DUAL_OPTIMIZATION_COMPLETE.md +293 -0
  3. package/README.md +167 -557
  4. package/README_OPENAI.md +163 -0
  5. package/THINKING_THROTTLING_OPTIMIZATION.md +244 -0
  6. package/THROTTLING_1_5S_OPTIMIZATION.md +401 -0
  7. package/TOOLS_IMPROVEMENTS_SUMMARY.md +273 -0
  8. package/cloco.md +5 -1
  9. package/config.example.json +15 -94
  10. package/config.mcp.example.json +81 -0
  11. package/dist/bash-runner.js +5 -126
  12. package/dist/batch-cli.js +286 -20658
  13. package/dist/closer-cli.js +329 -21135
  14. package/dist/index.js +308 -31036
  15. package/docs/ANTHROPIC_TOOL_ERROR_HANDLING.md +220 -0
  16. package/docs/BUILD_COMMANDS.md +79 -0
  17. package/docs/CTRL_Z_SUPPORT.md +189 -0
  18. package/docs/DEEPSEEK_R1_INTEGRATION.md +427 -0
  19. package/docs/FIX_OPENAI_TOOL_ERROR_HANDLING.md +375 -0
  20. package/docs/FIX_OPENAI_TOOL_RESULT.md +198 -0
  21. package/docs/INPUT_ENHANCEMENTS.md +192 -0
  22. package/docs/MCP_IMPLEMENTATION_SUMMARY.md +428 -0
  23. package/docs/MCP_INTEGRATION.md +418 -0
  24. package/docs/MCP_QUICKSTART.md +299 -0
  25. package/docs/MCP_README.md +166 -0
  26. package/docs/MINIFY_BUILD.md +180 -0
  27. package/docs/MULTILINE_INPUT_FEATURE.md +119 -0
  28. package/docs/OPENAI_CLIENT.md +258 -0
  29. package/docs/PROJECT_LOCAL_CONFIG.md +471 -0
  30. package/docs/PROJECT_LOCAL_CONFIG_SUMMARY.md +407 -0
  31. package/docs/REFACTOR_CONVERSATION.md +306 -0
  32. package/docs/REGION_EDIT_DESIGN.md +475 -0
  33. package/docs/SIGNAL_HANDLING.md +171 -0
  34. package/docs/STREAM_UPDATE_THROTTLE.md +273 -0
  35. package/docs/TOOLS_REFACTOR_PLAN.md +520 -0
  36. package/ds_r1.md +249 -0
  37. package/examples/abort-fence-example.js +294 -0
  38. package/package.json +18 -4
  39. package/src/ai-client-legacy.js +6 -1
  40. package/src/ai-client-openai.js +672 -0
  41. package/src/ai-client.js +30 -13
  42. package/src/closer-cli.jsx +450 -162
  43. package/src/components/fullscreen-conversation.jsx +157 -0
  44. package/src/components/ink-text-input/index.jsx +324 -0
  45. package/src/components/multiline-text-input.jsx +614 -0
  46. package/src/components/progress-bar.jsx +135 -0
  47. package/src/components/tool-detail-view.jsx +82 -0
  48. package/src/components/tool-renderers/bash-renderer.jsx +197 -0
  49. package/src/components/tool-renderers/file-edit-renderer.jsx +247 -0
  50. package/src/components/tool-renderers/file-read-renderer.jsx +261 -0
  51. package/src/components/tool-renderers/file-write-renderer.jsx +222 -0
  52. package/src/components/tool-renderers/index.jsx +178 -0
  53. package/src/components/tool-renderers/list-renderer.jsx +274 -0
  54. package/src/components/tool-renderers/search-renderer.jsx +248 -0
  55. package/src/config.js +182 -20
  56. package/src/conversation/abort-fence.js +158 -0
  57. package/src/conversation/core.js +377 -0
  58. package/src/conversation/index.js +33 -0
  59. package/src/conversation/mcp-integration.js +96 -0
  60. package/src/conversation/plan-manager.js +295 -0
  61. package/src/conversation/stream-handler.js +154 -0
  62. package/src/conversation/tool-executor.js +264 -0
  63. package/src/conversation.js +23 -958
  64. package/src/hooks/use-throttled-state.js +158 -0
  65. package/src/input/enhanced-input.jsx +268 -0
  66. package/src/input/history.js +342 -0
  67. package/src/logger.js +20 -0
  68. package/src/mcp/client.js +275 -0
  69. package/src/mcp/tools-adapter.js +149 -0
  70. package/src/planner.js +18 -5
  71. package/src/prompt-builder.js +159 -0
  72. package/src/tools.js +457 -25
  73. package/src/utils/json-parser.js +231 -0
  74. package/src/utils/json-repair.js +146 -0
  75. package/src/utils/platform.js +259 -0
  76. package/test/test-ctrl-bf.js +121 -0
  77. package/test/test-deepseek-reasoning.js +118 -0
  78. package/test/test-history-navigation.js +80 -0
  79. package/test/test-input-fix.js +105 -0
  80. package/test/test-input-history.js +98 -0
  81. package/test/test-mcp.js +115 -0
  82. package/test/test-openai-client.js +152 -0
  83. package/test/test-openai-tool-result.js +199 -0
  84. package/test/test-project-config.js +106 -0
  85. package/test/test-shortcuts.js +79 -0
  86. package/test/test-stream-throttle.js +124 -0
  87. package/test/test-tool-error-handling.js +95 -0
  88. package/test/verify-input-fix.sh +35 -0
  89. package/test-abort-fence.js +263 -0
  90. package/test-abort-fix.js +54 -0
  91. package/test-abort-new-conversation.js +75 -0
  92. package/test-ctrl-z.js +54 -0
  93. package/test-file-read.js +105 -0
  94. package/test-tool-display.js +127 -0
  95. package/src/closer-cli.jsx.backup +0 -948
  96. package/test/workflows/longtalk/cloco.md +0 -19
  97. package/test/workflows/longtalk/emoji_500.txt +0 -63
  98. package/test/workflows/longtalk/emoji_list.txt +0 -20
  99. package/test-ctrl-c.jsx +0 -126
@@ -0,0 +1,149 @@
1
+ /**
2
+ * MCP 工具适配器
3
+ *
4
+ * 将 MCP 工具转换为 betaZodTool 格式,以便与现有工具系统集成
5
+ */
6
+
7
+ import { z } from 'zod';
8
+ import { betaZodTool } from '@anthropic-ai/sdk/helpers/beta/zod';
9
+ import { getMCPClientManager } from './client.js';
10
+ import { logger } from '../logger.js';
11
+
12
+ /**
13
+ * 将 MCP 工具的 JSON Schema 转换为 Zod Schema
14
+ * @param {Object} jsonSchema - MCP 工具的 inputSchema (JSON Schema)
15
+ * @returns {z.ZodTypeAny} Zod Schema
16
+ */
17
+ function jsonSchemaToZod(jsonSchema) {
18
+ // 简化版本:处理常见的 JSON Schema 类型
19
+ // 实际项目中可能需要更完整的转换
20
+
21
+ if (!jsonSchema || !jsonSchema.type) {
22
+ return z.object({});
23
+ }
24
+
25
+ const { type, properties, required } = jsonSchema;
26
+
27
+ if (type === 'object' && properties) {
28
+ const shape = {};
29
+
30
+ for (const [propName, propSchema] of Object.entries(properties)) {
31
+ let zodType;
32
+
33
+ switch (propSchema.type) {
34
+ case 'string':
35
+ zodType = z.string();
36
+ break;
37
+ case 'number':
38
+ case 'integer':
39
+ zodType = z.number();
40
+ break;
41
+ case 'boolean':
42
+ zodType = z.boolean();
43
+ break;
44
+ case 'array':
45
+ zodType = z.array(z.any());
46
+ break;
47
+ case 'object':
48
+ zodType = z.object({});
49
+ break;
50
+ default:
51
+ zodType = z.any();
52
+ }
53
+
54
+ // 处理可选字段
55
+ const isRequired = Array.isArray(required) && required.includes(propName);
56
+ shape[propName] = isRequired ? zodType : zodType.optional();
57
+ }
58
+
59
+ return z.object(shape);
60
+ }
61
+
62
+ // 其他类型作为 any 处理
63
+ return z.any();
64
+ }
65
+
66
+ /**
67
+ * 为 MCP 工具创建描述文本(包含服务器信息)
68
+ * @param {string} serverName - 服务器名称
69
+ * @param {Object} mcpTool - MCP 工具定义
70
+ * @returns {string} 增强的描述
71
+ */
72
+ function enhanceDescription(serverName, mcpTool) {
73
+ const baseDesc = mcpTool.description || mcpTool.name;
74
+ return `[MCP: ${serverName}] ${baseDesc}`;
75
+ }
76
+
77
+ /**
78
+ * 将 MCP 工具列表转换为 betaZodTool 列表
79
+ * @param {Array} mcpTools - MCP 工具列表
80
+ * @returns {Array} betaZodTool 列表
81
+ */
82
+ export function convertMCPToolsToBetaZod(mcpTools) {
83
+ const converted = [];
84
+
85
+ for (const mcpTool of mcpTools) {
86
+ try {
87
+ const { name, originalName, serverName, description, inputSchema } = mcpTool;
88
+
89
+ // 转换 JSON Schema 为 Zod Schema
90
+ const zodSchema = jsonSchemaToZod(inputSchema);
91
+
92
+ // 创建 betaZodTool
93
+ const betaTool = betaZodTool({
94
+ name: name,
95
+ description: enhanceDescription(serverName, { name: originalName, description }),
96
+ inputSchema: zodSchema,
97
+ run: async (input) => {
98
+ const manager = getMCPClientManager();
99
+
100
+ logger.debug(`执行 MCP 工具: ${serverName}.${originalName}`);
101
+
102
+ const result = await manager.callTool(serverName, originalName, input);
103
+
104
+ // 返回 JSON 字符串(与现有工具保持一致)
105
+ return JSON.stringify(result);
106
+ }
107
+ });
108
+
109
+ converted.push(betaTool);
110
+ } catch (error) {
111
+ logger.error(`转换 MCP 工具失败 (${mcpTool.name}): ${error.message}`);
112
+ }
113
+ }
114
+
115
+ return converted;
116
+ }
117
+
118
+ /**
119
+ * 获取所有 MCP 工具的 betaZodTool 格式
120
+ * @returns {Promise<Array>} betaZodTool 列表
121
+ */
122
+ export async function getAllMCPToolsAsBetaZod() {
123
+ const manager = getMCPClientManager();
124
+ const mcpTools = manager.getAllTools();
125
+
126
+ return convertMCPToolsToBetaZod(mcpTools);
127
+ }
128
+
129
+ /**
130
+ * 生成 MCP 工具的简短摘要
131
+ * @param {string} toolName - 工具名称
132
+ * @param {Object} input - 工具输入
133
+ * @param {Object} result - 工具结果
134
+ * @returns {string} 简短摘要
135
+ */
136
+ export function generateMCPToolSummary(toolName, input, result) {
137
+ // 解析工具名称(格式:serverName_toolName)
138
+ const parts = toolName.split('_');
139
+ const serverName = parts[0];
140
+ const shortToolName = parts.slice(1).join('_');
141
+
142
+ const success = result?.success;
143
+
144
+ if (success) {
145
+ return `✓ [${serverName}] ${shortToolName}`;
146
+ } else {
147
+ return `✗ [${serverName}] ${shortToolName}`;
148
+ }
149
+ }
package/src/planner.js CHANGED
@@ -80,11 +80,16 @@ export class TaskPlan {
80
80
  export class TaskPlanner {
81
81
  constructor(config) {
82
82
  this.config = config;
83
- this.aiClient = createAIClient(config);
83
+ this._aiClientPromise = createAIClient(config);
84
84
  this.toolExecutor = new ToolExecutor(config);
85
85
  this.memory = loadMemory();
86
86
  }
87
87
 
88
+ // 延迟初始化的 getter
89
+ async getAIClient() {
90
+ return await this._aiClientPromise;
91
+ }
92
+
88
93
  /**
89
94
  * 为用户请求创建任务计划
90
95
  */
@@ -128,7 +133,8 @@ Respond with only the JSON plan, no additional text.`
128
133
  ];
129
134
 
130
135
  try {
131
- const response = await this.aiClient.chat(messages, { systemPrompt });
136
+ const aiClient = await this.getAIClient();
137
+ const response = await aiClient.chat(messages, { systemPrompt });
132
138
  const text = response.content.find(c => c.type === 'text')?.text || '{}';
133
139
  const planData = JSON.parse(text);
134
140
 
@@ -309,7 +315,8 @@ Respond with a JSON object describing the patterns found.`;
309
315
  ];
310
316
 
311
317
  try {
312
- const response = await this.aiClient.chat(messages, { systemPrompt });
318
+ const aiClient = await this.getAIClient();
319
+ const response = await aiClient.chat(messages, { systemPrompt });
313
320
  const text = response.content.find(c => c.type === 'text')?.text || '{}';
314
321
  const patterns = JSON.parse(text);
315
322
 
@@ -335,10 +342,15 @@ Respond with a JSON object describing the patterns found.`;
335
342
  export class ProblemDiagnoser {
336
343
  constructor(config) {
337
344
  this.config = config;
338
- this.aiClient = createAIClient(config);
345
+ this._aiClientPromise = createAIClient(config);
339
346
  this.toolExecutor = new ToolExecutor(config);
340
347
  }
341
348
 
349
+ // 延迟初始化的 getter
350
+ async getAIClient() {
351
+ return await this._aiClientPromise;
352
+ }
353
+
342
354
  /**
343
355
  * 诊断错误
344
356
  */
@@ -365,7 +377,8 @@ ${contextInfo ? `\nContext:\n${contextInfo}` : ''}`
365
377
  }
366
378
  ];
367
379
 
368
- const response = await this.aiClient.chat(messages, { systemPrompt });
380
+ const aiClient = await this.getAIClient();
381
+ const response = await aiClient.chat(messages, { systemPrompt });
369
382
  return response.content.find(c => c.type === 'text')?.text;
370
383
  }
371
384
 
@@ -0,0 +1,159 @@
1
+ /**
2
+ * 系统提示词构建器
3
+ *
4
+ * 提供灵活的方式来构建和优化 AI 助手的系统提示词
5
+ */
6
+
7
+ import { loadMemory } from './config.js';
8
+
9
+ /**
10
+ * 读取全局 cloco.md 文件内容
11
+ */
12
+ async function readGlobalCloco() {
13
+ try {
14
+ const fs = await import('fs/promises');
15
+ const path = await import('path');
16
+ const os = await import('os');
17
+ const homeDir = os.homedir();
18
+ const globalClocoPath = path.join(homeDir, '.closer-code', 'cloco.md');
19
+ return await fs.readFile(globalClocoPath, 'utf-8');
20
+ } catch (error) {
21
+ // 全局配置不存在是正常情况,不报错
22
+ if (error.code !== 'ENOENT') {
23
+ console.error('读取全局 cloco.md 失败:', error.message);
24
+ }
25
+ return '';
26
+ }
27
+ }
28
+
29
+ /**
30
+ * 读取项目级 cloco.md 文件内容
31
+ */
32
+ async function readProjectCloco() {
33
+ try {
34
+ const fs = await import('fs/promises');
35
+ const path = await import('path');
36
+ const clocoPath = path.join(process.cwd(), 'cloco.md');
37
+ return await fs.readFile(clocoPath, 'utf-8');
38
+ } catch (error) {
39
+ // 项目配置不存在是正常情况,不报错
40
+ if (error.code !== 'ENOENT') {
41
+ console.error('读取项目 cloco.md 失败:', error.message);
42
+ }
43
+ return '';
44
+ }
45
+ }
46
+
47
+ /**
48
+ * 构建系统提示词(优化后的版本)
49
+ */
50
+ export async function getSystemPrompt(config, workflowTest = false) {
51
+ const memory = loadMemory();
52
+ const projectKey = config.behavior.workingDir || 'default';
53
+ const projectInfo = memory.projects?.[projectKey];
54
+
55
+ // 读取全局和项目级 cloco.md
56
+ const globalClocoContent = await readGlobalCloco();
57
+ const projectClocoContent = await readProjectCloco();
58
+
59
+ // 读取 WORKFLOW_SYSTEM_PROMPT(如果需要)
60
+ let workflowPrompt = '';
61
+ if (workflowTest) {
62
+ // 这里需要从 conversation.js 导入 WORKFLOW_SYSTEM_PROMPT
63
+ // 暂时留空,后面会处理
64
+ }
65
+
66
+ // 构建完整的系统提示词
67
+ const prompt = `You are Closer, an AI programming assistant designed to help developers with coding tasks, debugging, and project management.
68
+
69
+ ## Tool Usage (CRITICAL - Read Carefully)
70
+
71
+ **PRIORITY ORDER: Use specialized tools FIRST, bash LAST**
72
+
73
+ ### File Operations - ALWAYS use tools first:
74
+ 1. **Read file**: Use \`readFile\` tool (NOT \`cat\`)
75
+ 2. **Read specific lines**: Use \`readFileLines\` tool (NOT \`sed -e '2,3p'\`)
76
+ - Example: \`readFileLines({ filePath: "app.js", startLine: 10, endLine: 20 })\`
77
+ 3. **Read from end**: Use \`readFileTail\` tool (NOT \`tail -n 100\`)
78
+ - Example: \`readFileTail({ filePath: "log.txt", lines: 100 })\`
79
+ 4. **Write file**: Use \`writeFile\` tool (NOT \`echo > file\`)
80
+ 5. **Edit file**: Use \`editFile\` or \`regionConstrainedEdit\` tool (NOT \`sed -i\`)
81
+
82
+ ### When to use bash:
83
+ - Running tests (\`npm test\`, \`pytest\`)
84
+ - Git operations (\`git status\`, \`git commit\`)
85
+ - Build commands (\`npm run build\`)
86
+ - List directory (\`ls -la\`)
87
+ - Install dependencies (\`npm install\`)
88
+
89
+ ### Why use tools?
90
+ - More efficient (less token usage)
91
+ - Better error handling
92
+ - Structured output
93
+ - Automatic file size optimization
94
+
95
+ **Key principle**: Use tools proactively - show, don't just talk about it.
96
+
97
+ ## Error Handling (IMPORTANT)
98
+
99
+ When a tool returns an error:
100
+ 1. **Identify** the error type (ENOENT, EACCES, etc.)
101
+ 2. **Fix** the issue (create directory, fix permissions, etc.)
102
+ 3. **Retry** the operation
103
+
104
+ **Retry strategy**: 2-3 attempts maximum. If still failing, explain to the user.
105
+
106
+ Common fixes:
107
+ - Missing directory → \`mkdir -p path/to/dir\`
108
+ - Wrong content → Read file first, then edit
109
+
110
+ ## Task Execution Guide
111
+ When asked to analyze or review code:
112
+ - Start by searching for relevant files
113
+ - Read the key files to understand the codebase
114
+ - Focus on files that are most relevant to the task
115
+ - Provide specific findings with file names and line numbers
116
+
117
+ **NOTE**: Only perform comprehensive analysis when explicitly requested. For specific questions, focus on the relevant parts.
118
+
119
+ ## Current Context
120
+ Working Directory: ${config.behavior.workingDir}
121
+ Available Tools: ${config.tools.enabled.join(', ')}
122
+ ${projectInfo ? `
123
+ ## Project Patterns
124
+ This is a familiar project. Remember these patterns:
125
+ ${JSON.stringify(projectInfo.patterns, null, 2)}
126
+ ` : ''}
127
+
128
+ ## Behavior Configuration
129
+ - Auto Plan: ${config.behavior.autoPlan ? 'Enabled' : 'Disabled'}
130
+ - Auto Execute: ${config.behavior.autoExecute ? 'Enabled (low-risk operations only)' : 'Disabled'}
131
+ - Confirm Destructive: ${config.behavior.confirmDestructive ? 'Enabled' : 'Disabled'}
132
+
133
+ ${globalClocoContent ? `
134
+ ## 📋 Global Behavior Guidelines (CRITICAL)
135
+ **The following global guidelines from ~/.closer-code/cloco.md are EXTREMELY IMPORTANT and MUST be followed:**
136
+
137
+ ${globalClocoContent}
138
+
139
+ **These global guidelines take precedence over general instructions. Follow them carefully**
140
+ ` : ''}
141
+
142
+ ${projectClocoContent ? `
143
+ ## 📋 Project Behavior Guidelines (CRITICAL)
144
+ **The following project-specific guidelines from ./cloco.md are EXTREMELY IMPORTANT and MUST be followed:**
145
+
146
+ ${projectClocoContent}
147
+
148
+ **These project guidelines take precedence over general instructions. Follow them carefully**
149
+ ` : ''}
150
+
151
+ ${!globalClocoContent && !projectClocoContent ? `
152
+ ## 📋 Behavior Guidelines
153
+ No custom behavior guidelines found. You can add them by:
154
+ - Creating ~/.closer-code/cloco.md for global guidelines
155
+ - Creating ./cloco.md for project-specific guidelines
156
+ ` : ''}${workflowPrompt}`;
157
+
158
+ return prompt;
159
+ }