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.
- package/.env.example +83 -0
- package/API_GUIDE.md +1411 -0
- package/AUTO_MKDIR_IMPROVEMENT.md +354 -0
- package/CLAUDE.md +55 -0
- package/CTRL_C_EXPERIMENT.md +90 -0
- package/PROJECT_CLEANUP_SUMMARY.md +121 -0
- package/README.md +686 -0
- package/cloco.md +51 -0
- package/config.example.json +116 -0
- package/dist/bash-runner.js +128 -0
- package/dist/batch-cli.js +20736 -0
- package/dist/closer-cli.js +21190 -0
- package/dist/index.js +31228 -0
- package/docs/EXPORT_COMMAND.md +152 -0
- package/docs/FILE_NAMING_IMPROVEMENT.md +168 -0
- package/docs/GLOBAL_CONFIG.md +128 -0
- package/docs/LONG_MESSAGE_DISPLAY_FIX.md +202 -0
- package/docs/PROJECT_HISTORY_ISOLATION.md +315 -0
- package/docs/QUICK_START_HISTORY.md +207 -0
- package/docs/TASK_PROGRESS_FEATURE.md +190 -0
- package/docs/THINKING_CONTENT_RESEARCH.md +267 -0
- package/docs/THINKING_FEATURE.md +187 -0
- package/docs/THINKING_IMPROVEMENT_COMPARISON.md +193 -0
- package/docs/THINKING_OPTIMIZATION_SUMMARY.md +242 -0
- package/docs/UI_IMPROVEMENTS_2025-01-18.md +256 -0
- package/docs/WHY_THINKING_SHORT.md +201 -0
- package/package.json +49 -0
- package/scenarios/README.md +234 -0
- package/scenarios/run-all-scenarios.js +342 -0
- package/scenarios/scenario1-batch-converter.js +247 -0
- package/scenarios/scenario2-code-analyzer.js +375 -0
- package/scenarios/scenario3-doc-generator.js +371 -0
- package/scenarios/scenario4-log-analyzer.js +496 -0
- package/scenarios/scenario5-tdd-helper.js +681 -0
- package/src/ai-client-legacy.js +171 -0
- package/src/ai-client.js +221 -0
- package/src/bash-runner.js +148 -0
- package/src/batch-cli.js +327 -0
- package/src/cli.jsx +166 -0
- package/src/closer-cli.jsx +1103 -0
- package/src/closer-cli.jsx.backup +948 -0
- package/src/commands/batch.js +62 -0
- package/src/commands/chat.js +10 -0
- package/src/commands/config.js +154 -0
- package/src/commands/help.js +76 -0
- package/src/commands/history.js +192 -0
- package/src/commands/setup.js +17 -0
- package/src/commands/upgrade.js +101 -0
- package/src/commands/workflow-tests.js +125 -0
- package/src/config.js +343 -0
- package/src/conversation.js +962 -0
- package/src/git-helper.js +349 -0
- package/src/index.js +88 -0
- package/src/logger.js +347 -0
- package/src/plan.js +193 -0
- package/src/planner.js +397 -0
- package/src/search.js +195 -0
- package/src/setup.js +147 -0
- package/src/shortcuts.js +269 -0
- package/src/snippets.js +430 -0
- package/src/test-modules.js +118 -0
- package/src/tools.js +398 -0
- package/src/utils/cli.js +124 -0
- package/src/utils/validator.js +184 -0
- package/src/utils/version.js +33 -0
- package/src/utils/workflow-test.js +271 -0
- package/src/utils/workflow.js +268 -0
- package/test/demo-file-naming.js +92 -0
- package/test/demo-thinking.js +124 -0
- package/test/final-verification-report.md +303 -0
- package/test/research-thinking.js +130 -0
- package/test/test-auto-mkdir.js +123 -0
- package/test/test-e2e-empty-dir.md +108 -0
- package/test/test-export-logic.js +119 -0
- package/test/test-global-cloco.js +126 -0
- package/test/test-history-isolation.js +291 -0
- package/test/test-improved-thinking.js +43 -0
- package/test/test-long-message.js +65 -0
- package/test/test-plan-functionality.js +95 -0
- package/test/test-real-scenario.js +216 -0
- package/test/test-thinking-display.js +65 -0
- package/test/ui-verification-test.js +203 -0
- package/test/verify-history-isolation.sh +71 -0
- package/test/verify-thinking.js +339 -0
- package/test/workflows/empty-dir-creation.md +51 -0
- package/test/workflows/inventor/ascii-teacup.js +199 -0
- package/test/workflows/inventor/ascii-teacup.mjs +199 -0
- package/test/workflows/inventor/ascii_apple.hs +84 -0
- package/test/workflows/inventor/ascii_apple.py +91 -0
- package/test/workflows/inventor/cloco.md +3 -0
- package/test/workflows/longtalk/cloco.md +19 -0
- package/test/workflows/longtalk/emoji_500.txt +63 -0
- package/test/workflows/longtalk/emoji_list.txt +20 -0
- package/test/workflows/programmer/adder.md +33 -0
- package/test/workflows/programmer/expect.md +2 -0
- package/test/workflows/programmer/prompt.md +3 -0
- package/test/workflows/test-empty-dir-creation.js +113 -0
- package/test-ctrl-c.jsx +126 -0
- package/test-manual-file-creation.js +151 -0
- package/winfix.md +3 -0
package/src/setup.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Closer Code 设置脚本
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { createReadStream, createWriteStream, existsSync, mkdirSync } from 'fs';
|
|
7
|
+
import { homedir } from 'os';
|
|
8
|
+
import { join, dirname } from 'path';
|
|
9
|
+
import { createInterface } from 'readline';
|
|
10
|
+
import { execSync } from 'child_process';
|
|
11
|
+
|
|
12
|
+
const CONFIG_DIR = join(homedir(), '.closer-code');
|
|
13
|
+
const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
|
|
14
|
+
|
|
15
|
+
const rl = createInterface({
|
|
16
|
+
input: process.stdin,
|
|
17
|
+
output: process.stdout
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
function question(prompt) {
|
|
21
|
+
return new Promise(resolve => {
|
|
22
|
+
rl.question(prompt, resolve);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function setup() {
|
|
27
|
+
console.log('🚀 Closer Code 初始化向导\n');
|
|
28
|
+
|
|
29
|
+
// 创建配置目录
|
|
30
|
+
if (!existsSync(CONFIG_DIR)) {
|
|
31
|
+
console.log(`创建配置目录: ${CONFIG_DIR}`);
|
|
32
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 检查是否已有配置
|
|
36
|
+
if (existsSync(CONFIG_FILE)) {
|
|
37
|
+
console.log(`配置文件已存在: ${CONFIG_FILE}`);
|
|
38
|
+
const overwrite = await question('是否要覆盖? (y/N): ');
|
|
39
|
+
if (overwrite.toLowerCase() !== 'y') {
|
|
40
|
+
console.log('保持现有配置');
|
|
41
|
+
rl.close();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
console.log('\n选择 AI 提供商:');
|
|
47
|
+
console.log('1. Anthropic Claude (推荐)');
|
|
48
|
+
console.log('2. OpenAI (GPT-4, GPT-3.5)');
|
|
49
|
+
console.log('3. Ollama (本地运行)\n');
|
|
50
|
+
|
|
51
|
+
const providerChoice = await question('请选择 (1-3): ');
|
|
52
|
+
let provider, apiKey, model;
|
|
53
|
+
|
|
54
|
+
switch (providerChoice) {
|
|
55
|
+
case '1':
|
|
56
|
+
provider = 'anthropic';
|
|
57
|
+
apiKey = await question('请输入 Anthropic API Key: ');
|
|
58
|
+
model = 'claude-sonnet-4-5-20250929';
|
|
59
|
+
break;
|
|
60
|
+
case '2':
|
|
61
|
+
provider = 'openai';
|
|
62
|
+
apiKey = await question('请输入 OpenAI API Key: ');
|
|
63
|
+
model = 'gpt-4o';
|
|
64
|
+
break;
|
|
65
|
+
case '3':
|
|
66
|
+
provider = 'ollama';
|
|
67
|
+
console.log('使用 Ollama - 确保服务运行在 http://localhost:11434');
|
|
68
|
+
apiKey = '';
|
|
69
|
+
model = 'llama3.1';
|
|
70
|
+
break;
|
|
71
|
+
default:
|
|
72
|
+
console.log('无效选择,使用默认配置');
|
|
73
|
+
provider = 'anthropic';
|
|
74
|
+
apiKey = '';
|
|
75
|
+
model = 'claude-sonnet-4-5-20250929';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const workingDir = await question(`工作目录 (默认: ${process.cwd()}): `) || process.cwd();
|
|
79
|
+
|
|
80
|
+
const config = {
|
|
81
|
+
ai: {
|
|
82
|
+
provider,
|
|
83
|
+
anthropic: {
|
|
84
|
+
apiKey: provider === 'anthropic' ? apiKey : '',
|
|
85
|
+
baseURL: 'https://api.anthropic.com',
|
|
86
|
+
model: 'claude-sonnet-4-5-20250929',
|
|
87
|
+
maxTokens: 8192
|
|
88
|
+
},
|
|
89
|
+
openai: {
|
|
90
|
+
apiKey: provider === 'openai' ? apiKey : '',
|
|
91
|
+
baseURL: 'https://api.openai.com/v1',
|
|
92
|
+
model: 'gpt-4o',
|
|
93
|
+
maxTokens: 4096
|
|
94
|
+
},
|
|
95
|
+
ollama: {
|
|
96
|
+
baseURL: 'http://localhost:11434',
|
|
97
|
+
model: 'llama3.1',
|
|
98
|
+
maxTokens: 4096
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
behavior: {
|
|
102
|
+
autoPlan: true,
|
|
103
|
+
autoExecute: false,
|
|
104
|
+
confirmDestructive: true,
|
|
105
|
+
maxRetries: 3,
|
|
106
|
+
timeout: 30000,
|
|
107
|
+
workingDir
|
|
108
|
+
},
|
|
109
|
+
tools: {
|
|
110
|
+
enabled: [
|
|
111
|
+
'bash',
|
|
112
|
+
'readFile',
|
|
113
|
+
'writeFile',
|
|
114
|
+
'editFile',
|
|
115
|
+
'searchFiles',
|
|
116
|
+
'searchCode',
|
|
117
|
+
'listFiles',
|
|
118
|
+
'analyzeError',
|
|
119
|
+
'runTests',
|
|
120
|
+
'planTask'
|
|
121
|
+
]
|
|
122
|
+
},
|
|
123
|
+
ui: {
|
|
124
|
+
theme: 'default',
|
|
125
|
+
showLineNumbers: true,
|
|
126
|
+
maxOutputLines: 100,
|
|
127
|
+
autoScroll: true
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// 写入配置
|
|
132
|
+
const fs = await import('fs/promises');
|
|
133
|
+
await fs.writeFile(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
134
|
+
|
|
135
|
+
console.log(`\n✅ 配置已保存到: ${CONFIG_FILE}`);
|
|
136
|
+
console.log('\n你现在可以运行: npm start');
|
|
137
|
+
console.log('\n提示: 如果不想在配置文件中存储 API Key,可以设置环境变量:');
|
|
138
|
+
if (provider === 'anthropic') {
|
|
139
|
+
console.log(' export ANTHROPIC_API_KEY=your-key');
|
|
140
|
+
} else if (provider === 'openai') {
|
|
141
|
+
console.log(' export OPENAI_API_KEY=your-key');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
rl.close();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
setup().catch(console.error);
|
package/src/shortcuts.js
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 快捷操作和别名管理
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from 'fs/promises';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { homedir } from 'os';
|
|
8
|
+
|
|
9
|
+
const SHORTCUTS_FILE = path.join(homedir(), '.closer-code/shortcuts.json');
|
|
10
|
+
|
|
11
|
+
// 默认快捷操作
|
|
12
|
+
const DEFAULT_SHORTCUTS = {
|
|
13
|
+
// 文件操作
|
|
14
|
+
'ls': { action: 'listFiles', description: '列出当前目录文件' },
|
|
15
|
+
'pwd': { action: 'printWorkingDir', description: '打印当前工作目录' },
|
|
16
|
+
'cat': { action: 'readFile', description: '读取文件内容' },
|
|
17
|
+
|
|
18
|
+
// Git 操作
|
|
19
|
+
'gs': { action: 'gitStatus', description: 'Git 状态' },
|
|
20
|
+
'ga': { action: 'gitAdd', description: 'Git 添加文件' },
|
|
21
|
+
'gc': { action: 'gitCommit', description: 'Git 提交' },
|
|
22
|
+
'gp': { action: 'gitPush', description: 'Git 推送' },
|
|
23
|
+
'gl': { action: 'gitLog', description: 'Git 日志' },
|
|
24
|
+
|
|
25
|
+
// 项目操作
|
|
26
|
+
'build': { action: 'runBuild', description: '运行构建' },
|
|
27
|
+
'test': { action: 'runTests', description: '运行测试' },
|
|
28
|
+
'clean': { action: 'cleanBuild', description: '清理构建文件' },
|
|
29
|
+
'install': { action: 'installDeps', description: '安装依赖' },
|
|
30
|
+
|
|
31
|
+
// 搜索操作
|
|
32
|
+
'find': { action: 'searchCode', description: '搜索代码' },
|
|
33
|
+
'grep': { action: 'searchFiles', description: '搜索文件' },
|
|
34
|
+
|
|
35
|
+
// AI 操作
|
|
36
|
+
'explain': { action: 'explainCode', description: '解释代码' },
|
|
37
|
+
'fix': { action: 'fixError', description: '修复错误' },
|
|
38
|
+
'refactor': { action: 'refactorCode', description: '重构代码' },
|
|
39
|
+
'review': { action: 'reviewCode', description: '代码审查' },
|
|
40
|
+
|
|
41
|
+
// 系统操作
|
|
42
|
+
'env': { action: 'showEnv', description: '显示环境信息' },
|
|
43
|
+
'help': { action: 'showHelp', description: '显示帮助' },
|
|
44
|
+
'config': { action: 'showConfig', description: '显示配置' }
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 快捷操作管理器
|
|
49
|
+
*/
|
|
50
|
+
export class ShortcutManager {
|
|
51
|
+
constructor() {
|
|
52
|
+
this.shortcuts = {};
|
|
53
|
+
this.aliases = {};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 初始化快捷操作
|
|
58
|
+
*/
|
|
59
|
+
async initialize() {
|
|
60
|
+
await this.loadShortcuts();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 加载快捷操作
|
|
65
|
+
*/
|
|
66
|
+
async loadShortcuts() {
|
|
67
|
+
try {
|
|
68
|
+
const data = await fs.readFile(SHORTCUTS_FILE, 'utf-8');
|
|
69
|
+
const custom = JSON.parse(data);
|
|
70
|
+
|
|
71
|
+
this.shortcuts = { ...DEFAULT_SHORTCUTS, ...custom.shortcuts };
|
|
72
|
+
this.aliases = custom.aliases || {};
|
|
73
|
+
} catch (error) {
|
|
74
|
+
// 文件不存在,使用默认
|
|
75
|
+
this.shortcuts = { ...DEFAULT_SHORTCUTS };
|
|
76
|
+
this.aliases = {};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* 保存快捷操作
|
|
82
|
+
*/
|
|
83
|
+
async saveShortcuts() {
|
|
84
|
+
const data = {
|
|
85
|
+
shortcuts: this.shortcuts,
|
|
86
|
+
aliases: this.aliases
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const dir = path.dirname(SHORTCUTS_FILE);
|
|
90
|
+
await fs.mkdir(dir, { recursive: true });
|
|
91
|
+
await fs.writeFile(SHORTCUTS_FILE, JSON.stringify(data, null, 2));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 解析快捷命令
|
|
96
|
+
*/
|
|
97
|
+
parse(input) {
|
|
98
|
+
// 移除开头的斜杠(如果有)
|
|
99
|
+
const cleaned = input.replace(/^\/+/, '').trim();
|
|
100
|
+
|
|
101
|
+
// 分割命令和参数
|
|
102
|
+
const parts = cleaned.split(/\s+/);
|
|
103
|
+
const command = parts[0];
|
|
104
|
+
const args = parts.slice(1).join(' ');
|
|
105
|
+
|
|
106
|
+
// 检查别名
|
|
107
|
+
const actualCommand = this.aliases[command] || command;
|
|
108
|
+
|
|
109
|
+
// 查找快捷操作
|
|
110
|
+
const shortcut = this.shortcuts[actualCommand];
|
|
111
|
+
|
|
112
|
+
if (shortcut) {
|
|
113
|
+
return {
|
|
114
|
+
type: 'shortcut',
|
|
115
|
+
command: actualCommand,
|
|
116
|
+
action: shortcut.action,
|
|
117
|
+
args,
|
|
118
|
+
description: shortcut.description
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* 添加别名
|
|
127
|
+
*/
|
|
128
|
+
async addAlias(alias, command) {
|
|
129
|
+
this.aliases[alias] = command;
|
|
130
|
+
await this.saveShortcuts();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* 移除别名
|
|
135
|
+
*/
|
|
136
|
+
async removeAlias(alias) {
|
|
137
|
+
delete this.aliases[alias];
|
|
138
|
+
await this.saveShortcuts();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* 添加快捷操作
|
|
143
|
+
*/
|
|
144
|
+
async addShortcut(name, action, description) {
|
|
145
|
+
this.shortcuts[name] = { action, description };
|
|
146
|
+
await this.saveShortcuts();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* 移除快捷操作
|
|
151
|
+
*/
|
|
152
|
+
async removeShortcut(name) {
|
|
153
|
+
delete this.shortcuts[name];
|
|
154
|
+
await this.saveShortcuts();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* 列出所有快捷操作
|
|
159
|
+
*/
|
|
160
|
+
list() {
|
|
161
|
+
return {
|
|
162
|
+
shortcuts: this.shortcuts,
|
|
163
|
+
aliases: this.aliases
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* 执行快捷操作
|
|
169
|
+
*/
|
|
170
|
+
async execute(shortcut, toolExecutor) {
|
|
171
|
+
const { action, args } = shortcut;
|
|
172
|
+
|
|
173
|
+
switch (action) {
|
|
174
|
+
case 'listFiles':
|
|
175
|
+
return await toolExecutor.listFiles({
|
|
176
|
+
dirPath: args || '.',
|
|
177
|
+
recursive: false
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
case 'printWorkingDir':
|
|
181
|
+
return { success: true, data: { cwd: process.cwd() } };
|
|
182
|
+
|
|
183
|
+
case 'readFile':
|
|
184
|
+
if (!args) {
|
|
185
|
+
return { success: false, error: '需要指定文件路径' };
|
|
186
|
+
}
|
|
187
|
+
return await toolExecutor.readFile({ filePath: args });
|
|
188
|
+
|
|
189
|
+
case 'gitStatus':
|
|
190
|
+
return await toolExecutor.bash({ command: 'git status' });
|
|
191
|
+
|
|
192
|
+
case 'gitAdd':
|
|
193
|
+
return await toolExecutor.bash({ command: `git add ${args || '.'}` });
|
|
194
|
+
|
|
195
|
+
case 'gitCommit':
|
|
196
|
+
if (!args) {
|
|
197
|
+
return { success: false, error: '需要指定提交信息' };
|
|
198
|
+
}
|
|
199
|
+
return await toolExecutor.bash({ command: `git commit -m "${args}"` });
|
|
200
|
+
|
|
201
|
+
case 'gitPush':
|
|
202
|
+
return await toolExecutor.bash({ command: 'git push' });
|
|
203
|
+
|
|
204
|
+
case 'gitLog':
|
|
205
|
+
return await toolExecutor.bash({ command: 'git log --oneline -10' });
|
|
206
|
+
|
|
207
|
+
case 'runBuild':
|
|
208
|
+
return await toolExecutor.bash({ command: 'npm run build' });
|
|
209
|
+
|
|
210
|
+
case 'runTests':
|
|
211
|
+
return await toolExecutor.runTests({});
|
|
212
|
+
|
|
213
|
+
case 'cleanBuild':
|
|
214
|
+
return await toolExecutor.bash({ command: 'rm -rf dist node_modules/.cache' });
|
|
215
|
+
|
|
216
|
+
case 'installDeps':
|
|
217
|
+
return await toolExecutor.bash({ command: 'npm install' });
|
|
218
|
+
|
|
219
|
+
case 'searchCode':
|
|
220
|
+
if (!args) {
|
|
221
|
+
return { success: false, error: '需要指定搜索模式' };
|
|
222
|
+
}
|
|
223
|
+
return await toolExecutor.searchCode({ pattern: args });
|
|
224
|
+
|
|
225
|
+
case 'searchFiles':
|
|
226
|
+
if (!args) {
|
|
227
|
+
return { success: false, error: '需要指定文件模式' };
|
|
228
|
+
}
|
|
229
|
+
return await toolExecutor.searchFiles({ pattern: args });
|
|
230
|
+
|
|
231
|
+
case 'showEnv':
|
|
232
|
+
return {
|
|
233
|
+
success: true,
|
|
234
|
+
data: {
|
|
235
|
+
node: process.version,
|
|
236
|
+
platform: process.platform,
|
|
237
|
+
arch: process.arch,
|
|
238
|
+
cwd: process.cwd()
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
case 'showHelp':
|
|
243
|
+
const help = this.list();
|
|
244
|
+
return {
|
|
245
|
+
success: true,
|
|
246
|
+
data: {
|
|
247
|
+
message: '可用快捷操作:',
|
|
248
|
+
shortcuts: help.shortcuts,
|
|
249
|
+
aliases: help.aliases
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
default:
|
|
254
|
+
return {
|
|
255
|
+
success: false,
|
|
256
|
+
error: `未知操作: ${action}`
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* 创建快捷操作管理器
|
|
264
|
+
*/
|
|
265
|
+
export async function createShortcutManager() {
|
|
266
|
+
const manager = new ShortcutManager();
|
|
267
|
+
await manager.initialize();
|
|
268
|
+
return manager;
|
|
269
|
+
}
|