claude-pangu 1.0.13 → 1.0.14

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.
@@ -1,314 +1,168 @@
1
1
  #!/usr/bin/env node
2
-
3
2
  /**
4
3
  * oh-my-claude CLI - TypeScript 版本
5
4
  * 基于中国传统文化的 Claude Code 智能编排插件
6
5
  */
7
-
8
6
  import { execSync } from 'child_process';
9
7
  import * as fs from 'fs';
10
8
  import * as path from 'path';
11
9
  import * as os from 'os';
12
10
  import { z } from 'zod';
13
11
  import AgentStateManagerImpl from '../lib/agent-state-manager';
14
- import { configManager, OhMyClaudeConfig } from '../lib/config-manager';
15
-
16
- // ==================== 类型定义 ====================
17
-
18
- interface PluginConfig {
19
- version: string;
20
- name?: string;
21
- description?: string;
22
- }
23
-
24
- interface TaskSnapshot {
25
- id: string;
26
- taskId?: string;
27
- timestamp: string;
28
- type: string;
29
- status: string;
30
- progress: number;
31
- agents: string[];
32
- context: Record<string, any>;
33
- checkpoint?: any;
34
- }
35
-
36
- interface AgentCollaboration {
37
- id: string;
38
- timestamp: string;
39
- agents: Array<{
40
- name: string;
41
- status: string;
42
- }>;
43
- task: {
44
- description: string;
45
- status: string;
46
- };
47
- status: string;
48
- messages: string[];
49
- context: Record<string, any>;
50
- }
51
-
52
- interface CompressedContext {
53
- id: string;
54
- timestamp: string;
55
- summary: string;
56
- keyPoints: string[];
57
- references: Record<string, any>;
58
- size: number;
59
- }
60
-
61
- interface ContextSnapshots {
62
- compressedContexts: Record<string, CompressedContext>;
63
- keyReferences: Record<string, any>;
64
- contextSnapshots: any[];
65
- }
66
-
67
- interface PluginState {
68
- version: string;
69
- installed: boolean;
70
- installTime: string | null;
71
- lastUpdate: string;
72
- agents: Array<{
73
- name: string;
74
- file: string;
75
- checksum?: string;
76
- }>;
77
- commands: Array<{
78
- name: string;
79
- file: string;
80
- checksum?: string;
81
- }>;
82
- hooks: Array<{
83
- file: string;
84
- checksum?: string;
85
- }>;
86
- skills: Array<{
87
- name: string;
88
- checksum?: string;
89
- }>;
90
- checksums: Record<string, string>;
91
- tasks: {
92
- activeTasks: TaskSnapshot[];
93
- completedTasks: TaskSnapshot[];
94
- failedTasks: TaskSnapshot[];
95
- taskHistory: TaskSnapshot[];
96
- };
97
- agentCollaboration: {
98
- activeSessions: AgentCollaboration[];
99
- agentStates: Record<string, any>;
100
- collaborationHistory: AgentCollaboration[];
101
- };
102
- context: ContextSnapshots;
103
- performance: {
104
- taskCompletionRate: number;
105
- averageTaskDuration: number;
106
- agentUtilization: Record<string, number>;
107
- errorRates: Record<string, number>;
108
- };
109
- }
110
-
111
- interface TaskRecoveryPlan {
112
- canRecover: boolean;
113
- reason: string;
114
- recoveryPlan?: {
115
- checkpointId: string;
116
- lastProgress: number;
117
- currentStep: number;
118
- nextAction: string;
119
- contextSnapshot: any;
120
- recoverySteps: string[];
121
- };
122
- }
123
-
124
- interface ComplexityAnalysis {
125
- score: number;
126
- level: string;
127
- factors: string[];
128
- maxScore: number;
129
- }
130
-
12
+ import { configManager } from '../lib/config-manager';
131
13
  // ==================== Zod 模式验证 ====================
132
-
133
14
  const TaskSnapshotSchema = z.object({
134
- id: z.string(),
135
- taskId: z.string().optional(),
136
- timestamp: z.string(),
137
- type: z.string(),
138
- status: z.string(),
139
- progress: z.number().min(0).max(100),
140
- agents: z.array(z.string()),
141
- context: z.record(z.any()),
142
- checkpoint: z.any().optional()
15
+ id: z.string(),
16
+ taskId: z.string().optional(),
17
+ timestamp: z.string(),
18
+ type: z.string(),
19
+ status: z.string(),
20
+ progress: z.number().min(0).max(100),
21
+ agents: z.array(z.string()),
22
+ context: z.record(z.any()),
23
+ checkpoint: z.any().optional()
143
24
  });
144
-
145
25
  const AgentCollaborationSchema = z.object({
146
- id: z.string(),
147
- timestamp: z.string(),
148
- agents: z.array(z.object({
149
- name: z.string(),
150
- status: z.string()
151
- })),
152
- task: z.object({
153
- description: z.string(),
154
- status: z.string()
155
- }),
156
- status: z.string(),
157
- messages: z.array(z.string()),
158
- context: z.record(z.any())
26
+ id: z.string(),
27
+ timestamp: z.string(),
28
+ agents: z.array(z.object({
29
+ name: z.string(),
30
+ status: z.string()
31
+ })),
32
+ task: z.object({
33
+ description: z.string(),
34
+ status: z.string()
35
+ }),
36
+ status: z.string(),
37
+ messages: z.array(z.string()),
38
+ context: z.record(z.any())
159
39
  });
160
-
161
40
  const PluginStateSchema = z.object({
162
- version: z.string(),
163
- installed: z.boolean(),
164
- installTime: z.string().nullable(),
165
- lastUpdate: z.string(),
166
- agents: z.array(z.object({
167
- name: z.string(),
168
- file: z.string(),
169
- checksum: z.string().optional()
170
- })),
171
- commands: z.array(z.object({
172
- name: z.string(),
173
- file: z.string(),
174
- checksum: z.string().optional()
175
- })),
176
- hooks: z.array(z.object({
177
- file: z.string(),
178
- checksum: z.string().optional()
179
- })),
180
- skills: z.array(z.object({
181
- name: z.string(),
182
- checksum: z.string().optional()
183
- })),
184
- checksums: z.record(z.string()),
185
- tasks: z.object({
186
- activeTasks: z.array(TaskSnapshotSchema),
187
- completedTasks: z.array(TaskSnapshotSchema),
188
- failedTasks: z.array(TaskSnapshotSchema),
189
- taskHistory: z.array(TaskSnapshotSchema)
190
- }),
191
- agentCollaboration: z.object({
192
- activeSessions: z.array(AgentCollaborationSchema),
193
- agentStates: z.record(z.any()),
194
- collaborationHistory: z.array(AgentCollaborationSchema)
195
- }),
196
- context: z.object({
197
- compressedContexts: z.record(z.any()),
198
- keyReferences: z.record(z.any()),
199
- contextSnapshots: z.array(z.any())
200
- }),
201
- performance: z.object({
202
- taskCompletionRate: z.number().min(0).max(100),
203
- averageTaskDuration: z.number().min(0),
204
- agentUtilization: z.record(z.number()),
205
- errorRates: z.record(z.number())
206
- })
41
+ version: z.string(),
42
+ installed: z.boolean(),
43
+ installTime: z.string().nullable(),
44
+ lastUpdate: z.string(),
45
+ agents: z.array(z.object({
46
+ name: z.string(),
47
+ file: z.string(),
48
+ checksum: z.string().optional()
49
+ })),
50
+ commands: z.array(z.object({
51
+ name: z.string(),
52
+ file: z.string(),
53
+ checksum: z.string().optional()
54
+ })),
55
+ hooks: z.array(z.object({
56
+ file: z.string(),
57
+ checksum: z.string().optional()
58
+ })),
59
+ skills: z.array(z.object({
60
+ name: z.string(),
61
+ checksum: z.string().optional()
62
+ })),
63
+ checksums: z.record(z.string()),
64
+ tasks: z.object({
65
+ activeTasks: z.array(TaskSnapshotSchema),
66
+ completedTasks: z.array(TaskSnapshotSchema),
67
+ failedTasks: z.array(TaskSnapshotSchema),
68
+ taskHistory: z.array(TaskSnapshotSchema)
69
+ }),
70
+ agentCollaboration: z.object({
71
+ activeSessions: z.array(AgentCollaborationSchema),
72
+ agentStates: z.record(z.any()),
73
+ collaborationHistory: z.array(AgentCollaborationSchema)
74
+ }),
75
+ context: z.object({
76
+ compressedContexts: z.record(z.any()),
77
+ keyReferences: z.record(z.any()),
78
+ contextSnapshots: z.array(z.any())
79
+ }),
80
+ performance: z.object({
81
+ taskCompletionRate: z.number().min(0).max(100),
82
+ averageTaskDuration: z.number().min(0),
83
+ agentUtilization: z.record(z.number()),
84
+ errorRates: z.record(z.number())
85
+ })
207
86
  });
208
-
209
87
  // 其他验证模式
210
88
  const TaskDescriptionSchema = z.string().max(10000); // 允许空字符串,但限制最大长度
211
89
  const FilePathSchema = z.string().min(1);
212
90
  const PluginDirSchema = z.string().min(1);
213
91
  const LockTimeoutSchema = z.number().min(1000).max(300000);
214
92
  const ComplexityAnalysisSchema = z.object({
215
- score: z.number().min(0).max(10),
216
- level: z.enum(['低', '中', '高']),
217
- factors: z.array(z.string()),
218
- maxScore: z.number()
93
+ score: z.number().min(0).max(10),
94
+ level: z.enum(['低', '中', '高']),
95
+ factors: z.array(z.string()),
96
+ maxScore: z.number()
219
97
  });
220
-
221
98
  const CopyResultSchema = z.object({
222
- dirs: z.number().min(0),
223
- files: z.number().min(0),
224
- errors: z.array(z.string())
99
+ dirs: z.number().min(0),
100
+ files: z.number().min(0),
101
+ errors: z.array(z.string())
225
102
  });
226
-
227
103
  const InstallResultSchema = z.object({
228
- count: z.number().min(0),
229
- errors: z.array(z.string()),
230
- cleaned: z.boolean().optional()
104
+ count: z.number().min(0),
105
+ errors: z.array(z.string()),
106
+ cleaned: z.boolean().optional()
231
107
  });
232
-
233
108
  const VerificationResultSchema = z.object({
234
- success: z.boolean(),
235
- errors: z.array(z.string())
109
+ success: z.boolean(),
110
+ errors: z.array(z.string())
236
111
  });
237
-
238
- interface VerificationResult {
239
- success: boolean;
240
- errors: string[];
241
- }
242
-
243
112
  // ==================== 常量配置 ====================
244
-
245
113
  const VERSION = '1.0.9';
246
114
  const PLUGIN_NAME = 'oh-my-claude';
247
-
248
115
  // 路径配置
249
116
  const LOG_DIR = path.join(os.homedir(), '.oh-my-claude', 'logs');
250
117
  const ERROR_LOG_PATH = path.join(LOG_DIR, 'error.log');
251
118
  const PLUGIN_STATE_DIR = path.join(os.homedir(), '.oh-my-claude', 'state');
252
119
  const PLUGIN_STATE_PATH = path.join(PLUGIN_STATE_DIR, 'plugin-state.json');
253
-
254
120
  // 时间配置(毫秒)
255
121
  const LOCK_TIMEOUT_MS = 30000;
256
122
  const LOCK_STALE_MS = 5 * 60 * 1000;
257
123
  const LOCK_RETRY_INTERVAL_MS = 1000;
258
-
259
124
  // 文件处理配置
260
125
  const LARGE_FILE_THRESHOLD_BYTES = 1024 * 1024;
261
126
  const COPY_BUFFER_SIZE = 64 * 1024;
262
-
263
127
  // UI 配置
264
128
  const DIVIDER_LENGTH = 40;
265
129
  const DIVIDER_LONG_LENGTH = 60;
266
-
267
130
  // GitHub 配置
268
131
  const GITHUB_REPO = 'ZDragon17/oh-my-claude';
269
132
  const GITHUB_ISSUES_URL = `https://github.com/${GITHUB_REPO}/issues`;
270
-
271
133
  // ==================== 核心功能 ====================
272
-
273
134
  /**
274
135
  * 脱敏处理堆栈跟踪中的敏感路径信息
275
136
  */
276
- function sanitizeStackTrace(stack: string): string {
277
- if (!stack) return '';
278
-
279
- const home = os.homedir();
280
- const username = os.userInfo().username;
281
-
282
- // 替换用户主目录路径
283
- let sanitized = stack
284
- .replace(new RegExp(home.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi'), '~')
285
- .replace(new RegExp(username.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi'), '<user>');
286
-
287
- // 替换 Windows 风格的用户路径
288
- sanitized = sanitized.replace(/[A-Za-z]:\\Users\\[^\\]+\\/gi, '<user-home>\\');
289
-
290
- // 替换可能的环境变量值(如 API keys、tokens 等)
291
- sanitized = sanitized.replace(/[a-f0-9]{32,}/gi, '<redacted-hex>');
292
- sanitized = sanitized.replace(/[A-Za-z0-9+/=]{40,}/g, '<redacted-token>');
293
-
294
- return sanitized;
137
+ function sanitizeStackTrace(stack) {
138
+ if (!stack)
139
+ return '';
140
+ const home = os.homedir();
141
+ const username = os.userInfo().username;
142
+ // 替换用户主目录路径
143
+ let sanitized = stack
144
+ .replace(new RegExp(home.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi'), '~')
145
+ .replace(new RegExp(username.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi'), '<user>');
146
+ // 替换 Windows 风格的用户路径
147
+ sanitized = sanitized.replace(/[A-Za-z]:\\Users\\[^\\]+\\/gi, '<user-home>\\');
148
+ // 替换可能的环境变量值(如 API keys、tokens 等)
149
+ sanitized = sanitized.replace(/[a-f0-9]{32,}/gi, '<redacted-hex>');
150
+ sanitized = sanitized.replace(/[A-Za-z0-9+/=]{40,}/g, '<redacted-token>');
151
+ return sanitized;
295
152
  }
296
-
297
153
  /**
298
154
  * 记录错误到日志文件(带脱敏处理)
299
155
  */
300
- function logErrorToFile(err: Error): boolean {
301
- try {
302
- // 确保日志目录存在
303
- if (!fs.existsSync(LOG_DIR)) {
304
- fs.mkdirSync(LOG_DIR, { recursive: true });
305
- }
306
-
307
- const timestamp = new Date().toISOString();
308
- const sanitizedStack = sanitizeStackTrace(err.stack || '');
309
- const sanitizedMessage = sanitizeStackTrace(err.message);
310
-
311
- const logEntry = `
156
+ function logErrorToFile(err) {
157
+ try {
158
+ // 确保日志目录存在
159
+ if (!fs.existsSync(LOG_DIR)) {
160
+ fs.mkdirSync(LOG_DIR, { recursive: true });
161
+ }
162
+ const timestamp = new Date().toISOString();
163
+ const sanitizedStack = sanitizeStackTrace(err.stack || '');
164
+ const sanitizedMessage = sanitizeStackTrace(err.message);
165
+ const logEntry = `
312
166
  [${timestamp}]
313
167
  Version: ${VERSION}
314
168
  Platform: ${os.platform()} ${os.release()}
@@ -317,1693 +171,1492 @@ Error: ${err.name}: ${sanitizedMessage}
317
171
  Stack: ${sanitizedStack}
318
172
  ---
319
173
  `;
320
- fs.appendFileSync(ERROR_LOG_PATH, logEntry, 'utf8');
321
- return true;
322
- } catch {
323
- return false;
324
- }
174
+ fs.appendFileSync(ERROR_LOG_PATH, logEntry, 'utf8');
175
+ return true;
176
+ }
177
+ catch {
178
+ return false;
179
+ }
325
180
  }
326
-
327
181
  // ==================== 全局错误处理 ====================
328
-
329
182
  // 全局错误处理
330
- process.on('uncaughtException', (err: Error) => {
331
- console.error('\x1b[31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m');
332
- console.error('\x1b[31m❌ 发生未捕获的错误\x1b[0m');
333
- console.error('\x1b[31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m');
334
- console.error(`\x1b[33m错误类型:\x1b[0m ${err.name}`);
335
- console.error(`\x1b[33m错误信息:\x1b[0m ${err.message}`);
336
- console.error(`\x1b[33m错误堆栈:\x1b[0m`);
337
- console.error(err.stack);
338
-
339
- // 记录到日志文件
340
- if (logErrorToFile(err)) {
341
- console.error(`\n\x1b[36m错误已记录到:\x1b[0m ${ERROR_LOG_PATH}`);
342
- }
343
-
344
- console.error('\n\x1b[33m这是一个程序缺陷,请报告到:\x1b[0m');
345
- console.error(`${GITHUB_ISSUES_URL}\n`);
346
- process.exit(1);
183
+ process.on('uncaughtException', (err) => {
184
+ console.error('\x1b[31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m');
185
+ console.error('\x1b[31m❌ 发生未捕获的错误\x1b[0m');
186
+ console.error('\x1b[31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m');
187
+ console.error(`\x1b[33m错误类型:\x1b[0m ${err.name}`);
188
+ console.error(`\x1b[33m错误信息:\x1b[0m ${err.message}`);
189
+ console.error(`\x1b[33m错误堆栈:\x1b[0m`);
190
+ console.error(err.stack);
191
+ // 记录到日志文件
192
+ if (logErrorToFile(err)) {
193
+ console.error(`\n\x1b[36m错误已记录到:\x1b[0m ${ERROR_LOG_PATH}`);
194
+ }
195
+ console.error('\n\x1b[33m这是一个程序缺陷,请报告到:\x1b[0m');
196
+ console.error(`${GITHUB_ISSUES_URL}\n`);
197
+ process.exit(1);
347
198
  });
348
-
349
- process.on('unhandledRejection', (reason: any, promise: Promise<any>) => {
350
- console.error('\x1b[31m❌ 发生未处理的 Promise 拒绝:\x1b[0m');
351
- console.error(reason);
352
- if (reason instanceof Error) {
353
- logErrorToFile(reason);
354
- }
355
- process.exit(1);
199
+ process.on('unhandledRejection', (reason, promise) => {
200
+ console.error('\x1b[31m❌ 发生未处理的 Promise 拒绝:\x1b[0m');
201
+ console.error(reason);
202
+ if (reason instanceof Error) {
203
+ logErrorToFile(reason);
204
+ }
205
+ process.exit(1);
356
206
  });
357
-
358
207
  // 颜色输出 - 使用共享模块
359
208
  const { colors, log, success, error, info, warn } = require('./logger');
360
-
361
209
  // 初始化 Agent 状态管理器
362
210
  const agentStateManager = new AgentStateManagerImpl();
363
-
364
211
  // 初始化配置管理器并启用热重载
365
212
  if (configManager.get('advanced').enableTracing) {
366
- configManager.enableHotReload();
213
+ configManager.enableHotReload();
367
214
  }
368
-
369
215
  // ==================== 用户友好的错误信息 ====================
370
-
371
216
  /**
372
217
  * 将系统错误码转换为用户友好的错误信息
373
218
  */
374
- function getUserFriendlyError(err: NodeJS.ErrnoException, filePath: string): string {
375
- const errorMessages: Record<string, string> = {
376
- 'ENOENT': `文件或目录不存在: ${filePath}\n 请检查路径是否正确`,
377
- 'EACCES': `权限不足: ${filePath}\n 请尝试以管理员身份运行,或检查文件权限`,
378
- 'EPERM': `操作被拒绝: ${filePath}\n 可能被其他程序占用,请关闭相关程序后重试`,
379
- 'ENOSPC': `磁盘空间不足\n 请清理磁盘空间后重试`,
380
- 'EBUSY': `资源正忙: ${filePath}\n 文件可能正在被其他程序使用`,
381
- 'EMFILE': `打开文件过多\n 请关闭一些应用程序后重试`,
382
- 'EEXIST': `文件已存在: ${filePath}\n 请先删除或重命名现有文件`,
383
- 'EISDIR': `目标是目录而非文件: ${filePath}`,
384
- 'ENOTDIR': `目标不是目录: ${filePath}`,
385
- 'ENOTEMPTY': `目录不为空: ${filePath}\n 请先清空目录内容`,
386
- };
387
-
388
- const friendlyMessage = errorMessages[err.code || ''];
389
- if (friendlyMessage) {
390
- return friendlyMessage;
391
- }
392
-
393
- // 默认错误信息
394
- return `${err.message}\n 如需帮助,请访问: ${GITHUB_ISSUES_URL}`;
219
+ function getUserFriendlyError(err, filePath) {
220
+ const errorMessages = {
221
+ 'ENOENT': `文件或目录不存在: ${filePath}\n 请检查路径是否正确`,
222
+ 'EACCES': `权限不足: ${filePath}\n 请尝试以管理员身份运行,或检查文件权限`,
223
+ 'EPERM': `操作被拒绝: ${filePath}\n 可能被其他程序占用,请关闭相关程序后重试`,
224
+ 'ENOSPC': `磁盘空间不足\n 请清理磁盘空间后重试`,
225
+ 'EBUSY': `资源正忙: ${filePath}\n 文件可能正在被其他程序使用`,
226
+ 'EMFILE': `打开文件过多\n 请关闭一些应用程序后重试`,
227
+ 'EEXIST': `文件已存在: ${filePath}\n 请先删除或重命名现有文件`,
228
+ 'EISDIR': `目标是目录而非文件: ${filePath}`,
229
+ 'ENOTDIR': `目标不是目录: ${filePath}`,
230
+ 'ENOTEMPTY': `目录不为空: ${filePath}\n 请先清空目录内容`,
231
+ };
232
+ const friendlyMessage = errorMessages[err.code || ''];
233
+ if (friendlyMessage) {
234
+ return friendlyMessage;
235
+ }
236
+ // 默认错误信息
237
+ return `${err.message}\n 如需帮助,请访问: ${GITHUB_ISSUES_URL}`;
395
238
  }
396
-
397
239
  // ==================== 安全的文件操作 ====================
398
-
399
240
  /**
400
241
  * 安全的文件读取
401
242
  */
402
- function safeReadFile(filePath: string): string | null {
403
- try {
404
- return fs.readFileSync(filePath, 'utf8');
405
- } catch (err) {
406
- if (err instanceof Error && 'code' in err && err.code === 'ENOENT') {
407
- return null;
408
- }
409
- throw new Error(getUserFriendlyError(err as NodeJS.ErrnoException, filePath));
410
- }
243
+ function safeReadFile(filePath) {
244
+ try {
245
+ return fs.readFileSync(filePath, 'utf8');
246
+ }
247
+ catch (err) {
248
+ if (err instanceof Error && 'code' in err && err.code === 'ENOENT') {
249
+ return null;
250
+ }
251
+ throw new Error(getUserFriendlyError(err, filePath));
252
+ }
411
253
  }
412
-
413
254
  /**
414
255
  * 安全的文件写入
415
256
  */
416
- function safeWriteFile(filePath: string, content: string): boolean {
417
- try {
418
- const dir = path.dirname(filePath);
419
- if (!fs.existsSync(dir)) {
420
- fs.mkdirSync(dir, { recursive: true });
421
- }
422
- fs.writeFileSync(filePath, content, 'utf8');
423
- return true;
424
- } catch (err) {
425
- throw new Error(getUserFriendlyError(err as NodeJS.ErrnoException, filePath));
426
- }
257
+ function safeWriteFile(filePath, content) {
258
+ try {
259
+ const dir = path.dirname(filePath);
260
+ if (!fs.existsSync(dir)) {
261
+ fs.mkdirSync(dir, { recursive: true });
262
+ }
263
+ fs.writeFileSync(filePath, content, 'utf8');
264
+ return true;
265
+ }
266
+ catch (err) {
267
+ throw new Error(getUserFriendlyError(err, filePath));
268
+ }
427
269
  }
428
-
429
270
  /**
430
271
  * 安全的目录删除
431
272
  */
432
- function safeRemoveDir(dirPath: string): boolean {
433
- try {
434
- if (fs.existsSync(dirPath)) {
435
- fs.rmSync(dirPath, { recursive: true, force: true });
436
- }
437
- return true;
438
- } catch (err) {
439
- throw new Error(getUserFriendlyError(err as NodeJS.ErrnoException, dirPath));
440
- }
273
+ function safeRemoveDir(dirPath) {
274
+ try {
275
+ if (fs.existsSync(dirPath)) {
276
+ fs.rmSync(dirPath, { recursive: true, force: true });
277
+ }
278
+ return true;
279
+ }
280
+ catch (err) {
281
+ throw new Error(getUserFriendlyError(err, dirPath));
282
+ }
441
283
  }
442
-
443
284
  /**
444
285
  * 安全的文件复制
445
286
  */
446
- function safeCopyFile(src: string, dest: string): boolean {
447
- try {
448
- const dir = path.dirname(dest);
449
- if (!fs.existsSync(dir)) {
450
- fs.mkdirSync(dir, { recursive: true });
451
- }
452
- fs.copyFileSync(src, dest);
453
- return true;
454
- } catch (err) {
455
- throw new Error(getUserFriendlyError(err as NodeJS.ErrnoException, `${src} -> ${dest}`));
456
- }
287
+ function safeCopyFile(src, dest) {
288
+ try {
289
+ const dir = path.dirname(dest);
290
+ if (!fs.existsSync(dir)) {
291
+ fs.mkdirSync(dir, { recursive: true });
292
+ }
293
+ fs.copyFileSync(src, dest);
294
+ return true;
295
+ }
296
+ catch (err) {
297
+ throw new Error(getUserFriendlyError(err, `${src} -> ${dest}`));
298
+ }
457
299
  }
458
-
459
300
  // ==================== 路径函数 ====================
460
-
461
301
  /**
462
302
  * 获取插件安装路径
463
303
  */
464
- function getPluginDir(): string {
465
- const home = os.homedir();
466
- return path.join(home, '.claude', 'plugins', PLUGIN_NAME);
304
+ function getPluginDir() {
305
+ const home = os.homedir();
306
+ return path.join(home, '.claude', 'plugins', PLUGIN_NAME);
467
307
  }
468
-
469
308
  /**
470
309
  * 获取 commands 安装路径
471
310
  */
472
- function getCommandsDir(): string {
473
- const home = os.homedir();
474
- return path.join(home, '.claude', 'commands');
311
+ function getCommandsDir() {
312
+ const home = os.homedir();
313
+ return path.join(home, '.claude', 'commands');
475
314
  }
476
-
477
315
  /**
478
316
  * 获取 skills 安装路径
479
317
  */
480
- function getSkillsDir(): string {
481
- const home = os.homedir();
482
- return path.join(home, '.claude', 'skills');
318
+ function getSkillsDir() {
319
+ const home = os.homedir();
320
+ return path.join(home, '.claude', 'skills');
483
321
  }
484
-
485
322
  /**
486
323
  * 获取当前包的路径
487
324
  */
488
- function getPackageDir(): string {
489
- return path.resolve(__dirname, '..');
325
+ function getPackageDir() {
326
+ return path.resolve(__dirname, '..');
490
327
  }
491
-
492
328
  /**
493
329
  * 检查 Claude Code 是否安装
494
330
  */
495
- function checkClaudeCode(): boolean {
496
- try {
497
- execSync('claude --version', { stdio: 'pipe' });
498
- return true;
499
- } catch {
500
- return false;
501
- }
331
+ function checkClaudeCode() {
332
+ try {
333
+ execSync('claude --version', { stdio: 'pipe' });
334
+ return true;
335
+ }
336
+ catch {
337
+ return false;
338
+ }
502
339
  }
503
-
504
340
  // ==================== 任务复杂度分析 ====================
505
-
506
341
  /**
507
342
  * 任务复杂度分析 (带 Zod 验证)
508
343
  * 导出以供测试使用
509
344
  */
510
- export function analyzeTaskComplexity(taskDescription: string): ComplexityAnalysis {
511
- // 验证输入
512
- const validatedDescription = TaskDescriptionSchema.parse(taskDescription);
513
-
514
- // 空字符串特殊处理
515
- if (!validatedDescription.trim()) {
516
- return {
517
- score: 0,
518
- level: '' as const,
519
- factors: ['空任务描述'],
520
- maxScore: 10
345
+ export function analyzeTaskComplexity(taskDescription) {
346
+ // 验证输入
347
+ const validatedDescription = TaskDescriptionSchema.parse(taskDescription);
348
+ // 空字符串特殊处理
349
+ if (!validatedDescription.trim()) {
350
+ return {
351
+ score: 0,
352
+ level: '低',
353
+ factors: ['空任务描述'],
354
+ maxScore: 10
355
+ };
356
+ }
357
+ let complexity = 0;
358
+ const factors = [];
359
+ // 1. 任务规模分析 (0-3分)
360
+ if (validatedDescription.match(/(完整|整个|全部|系统|平台)/i)) {
361
+ complexity += 3;
362
+ factors.push('大规模系统开发 (+3)');
363
+ }
364
+ else if (validatedDescription.match(/(模块|功能|页面)/i)) {
365
+ complexity += 2;
366
+ factors.push('中等规模功能开发 (+2)');
367
+ }
368
+ else {
369
+ complexity += 1;
370
+ factors.push('小型任务 (+1)');
371
+ }
372
+ // 2. 技术复杂度分析 (0-2分)
373
+ if (validatedDescription.match(/(架构|设计|重构|优化|性能|安全|测试)/i)) {
374
+ complexity += 2;
375
+ factors.push('涉及架构/设计层面 (+2)');
376
+ }
377
+ else if (validatedDescription.match(/(数据库|API|集成|第三方)/i)) {
378
+ complexity += 1.5;
379
+ factors.push('涉及技术集成 (+1.5)');
380
+ }
381
+ // 3. 依赖关系复杂度 (0-2分)
382
+ if (validatedDescription.match(/(电商|管理系统|多模块|微服务)/i)) {
383
+ complexity += 2;
384
+ factors.push('高度耦合的多模块系统 (+2)');
385
+ }
386
+ else if (validatedDescription.match(/(前后端|数据库|缓存)/i)) {
387
+ complexity += 1;
388
+ factors.push('涉及多层架构 (+1)');
389
+ }
390
+ // 4. 风险评估 (0-1.5分)
391
+ if (validatedDescription.match(/(生产|线上|重要|紧急)/i)) {
392
+ complexity += 1.5;
393
+ factors.push('高风险/高优先级任务 (+1.5)');
394
+ }
395
+ else if (validatedDescription.match(/(新功能|实验|测试)/i)) {
396
+ complexity += 0.5;
397
+ factors.push('中等风险任务 (+0.5)');
398
+ }
399
+ // 5. 时间压力 (0-1分)
400
+ if (validatedDescription.match(/(快速|紧急|deadline|尽快)/i)) {
401
+ complexity += 1;
402
+ factors.push('时间压力较大 (+1)');
403
+ }
404
+ // 限制最大分数为10
405
+ complexity = Math.min(complexity, 10);
406
+ // 确定复杂度等级
407
+ let level = '低';
408
+ if (complexity >= 7)
409
+ level = '高';
410
+ else if (complexity >= 4)
411
+ level = '中';
412
+ const result = {
413
+ score: complexity,
414
+ level,
415
+ factors,
416
+ maxScore: 10
521
417
  };
522
- }
523
-
524
- let complexity = 0;
525
- const factors: string[] = [];
526
-
527
- // 1. 任务规模分析 (0-3分)
528
- if (validatedDescription.match(/(完整|整个|全部|系统|平台)/i)) {
529
- complexity += 3;
530
- factors.push('大规模系统开发 (+3)');
531
- } else if (validatedDescription.match(/(模块|功能|页面)/i)) {
532
- complexity += 2;
533
- factors.push('中等规模功能开发 (+2)');
534
- } else {
535
- complexity += 1;
536
- factors.push('小型任务 (+1)');
537
- }
538
-
539
- // 2. 技术复杂度分析 (0-2分)
540
- if (validatedDescription.match(/(架构|设计|重构|优化|性能|安全|测试)/i)) {
541
- complexity += 2;
542
- factors.push('涉及架构/设计层面 (+2)');
543
- } else if (validatedDescription.match(/(数据库|API|集成|第三方)/i)) {
544
- complexity += 1.5;
545
- factors.push('涉及技术集成 (+1.5)');
546
- }
547
-
548
- // 3. 依赖关系复杂度 (0-2分)
549
- if (validatedDescription.match(/(电商|管理系统|多模块|微服务)/i)) {
550
- complexity += 2;
551
- factors.push('高度耦合的多模块系统 (+2)');
552
- } else if (validatedDescription.match(/(前后端|数据库|缓存)/i)) {
553
- complexity += 1;
554
- factors.push('涉及多层架构 (+1)');
555
- }
556
-
557
- // 4. 风险评估 (0-1.5分)
558
- if (validatedDescription.match(/(生产|线上|重要|紧急)/i)) {
559
- complexity += 1.5;
560
- factors.push('高风险/高优先级任务 (+1.5)');
561
- } else if (validatedDescription.match(/(新功能|实验|测试)/i)) {
562
- complexity += 0.5;
563
- factors.push('中等风险任务 (+0.5)');
564
- }
565
-
566
- // 5. 时间压力 (0-1分)
567
- if (validatedDescription.match(/(快速|紧急|deadline|尽快)/i)) {
568
- complexity += 1;
569
- factors.push('时间压力较大 (+1)');
570
- }
571
-
572
- // 限制最大分数为10
573
- complexity = Math.min(complexity, 10);
574
-
575
- // 确定复杂度等级
576
- let level: '低' | '中' | '高' = '低';
577
- if (complexity >= 7) level = '高';
578
- else if (complexity >= 4) level = '中';
579
-
580
- const result = {
581
- score: complexity,
582
- level,
583
- factors,
584
- maxScore: 10
585
- };
586
-
587
- // 验证输出
588
- return ComplexityAnalysisSchema.parse(result);
418
+ // 验证输出
419
+ return ComplexityAnalysisSchema.parse(result);
589
420
  }
590
-
591
421
  // ==================== 插件状态管理 ====================
592
-
593
422
  /**
594
423
  * 获取插件状态 (带 Zod 验证)
595
424
  */
596
- function getPluginState(): PluginState {
597
- try {
598
- if (fs.existsSync(PLUGIN_STATE_PATH)) {
599
- const stateData = fs.readFileSync(PLUGIN_STATE_PATH, 'utf8');
600
- const parsed = JSON.parse(stateData);
601
- // 使用 Zod 验证和类型安全
602
- return PluginStateSchema.parse(parsed);
603
- }
604
- } catch (error) {
605
- console.warn(`读取插件状态失败: ${error instanceof Error ? error.message : 'Unknown error'}`);
606
- }
607
-
608
- // 返回默认状态 - 增强版
609
- return {
610
- version: VERSION,
611
- installed: false,
612
- installTime: null,
613
- lastUpdate: new Date().toISOString(),
614
- agents: [],
615
- commands: [],
616
- hooks: [],
617
- skills: [],
618
- checksums: {},
619
- tasks: {
620
- activeTasks: [],
621
- completedTasks: [],
622
- failedTasks: [],
623
- taskHistory: []
624
- },
625
- agentCollaboration: {
626
- activeSessions: [],
627
- agentStates: {},
628
- collaborationHistory: []
629
- },
630
- context: {
631
- compressedContexts: {},
632
- keyReferences: {},
633
- contextSnapshots: []
634
- },
635
- performance: {
636
- taskCompletionRate: 0,
637
- averageTaskDuration: 0,
638
- agentUtilization: {},
639
- errorRates: {}
640
- }
641
- };
425
+ function getPluginState() {
426
+ try {
427
+ if (fs.existsSync(PLUGIN_STATE_PATH)) {
428
+ const stateData = fs.readFileSync(PLUGIN_STATE_PATH, 'utf8');
429
+ const parsed = JSON.parse(stateData);
430
+ // 使用 Zod 验证和类型安全
431
+ return PluginStateSchema.parse(parsed);
432
+ }
433
+ }
434
+ catch (error) {
435
+ console.warn(`读取插件状态失败: ${error instanceof Error ? error.message : 'Unknown error'}`);
436
+ }
437
+ // 返回默认状态 - 增强版
438
+ return {
439
+ version: VERSION,
440
+ installed: false,
441
+ installTime: null,
442
+ lastUpdate: new Date().toISOString(),
443
+ agents: [],
444
+ commands: [],
445
+ hooks: [],
446
+ skills: [],
447
+ checksums: {},
448
+ tasks: {
449
+ activeTasks: [],
450
+ completedTasks: [],
451
+ failedTasks: [],
452
+ taskHistory: []
453
+ },
454
+ agentCollaboration: {
455
+ activeSessions: [],
456
+ agentStates: {},
457
+ collaborationHistory: []
458
+ },
459
+ context: {
460
+ compressedContexts: {},
461
+ keyReferences: {},
462
+ contextSnapshots: []
463
+ },
464
+ performance: {
465
+ taskCompletionRate: 0,
466
+ averageTaskDuration: 0,
467
+ agentUtilization: {},
468
+ errorRates: {}
469
+ }
470
+ };
642
471
  }
643
-
644
472
  /**
645
473
  * 保存插件状态 (带 Zod 验证)
646
474
  */
647
- function savePluginState(state: PluginState): void {
648
- try {
649
- // 验证状态数据
650
- const validatedState = PluginStateSchema.parse(state);
651
-
652
- // 确保状态目录存在
653
- fs.mkdirSync(PLUGIN_STATE_DIR, { recursive: true });
654
-
655
- // 添加时间戳
656
- validatedState.lastUpdate = new Date().toISOString();
657
-
658
- // 限制历史记录数量,避免文件过大
659
- if (validatedState.tasks && validatedState.tasks.taskHistory) {
660
- validatedState.tasks.taskHistory = validatedState.tasks.taskHistory.slice(-100);
661
- }
662
- if (validatedState.agentCollaboration && validatedState.agentCollaboration.collaborationHistory) {
663
- validatedState.agentCollaboration.collaborationHistory = validatedState.agentCollaboration.collaborationHistory.slice(-50);
664
- }
665
- if (validatedState.context && validatedState.context.contextSnapshots) {
666
- validatedState.context.contextSnapshots = validatedState.context.contextSnapshots.slice(-20);
667
- }
668
-
669
- fs.writeFileSync(PLUGIN_STATE_PATH, JSON.stringify(validatedState, null, 2), 'utf8');
670
- console.log('插件状态已保存 (TypeScript + Zod 验证)');
671
- } catch (error) {
672
- console.error(`保存插件状态失败: ${error instanceof Error ? error.message : 'Unknown error'}`);
673
- }
674
- }
675
-
676
- // ==================== 并发锁机制 ====================
677
-
678
- interface LockContent {
679
- pid: number;
680
- timestamp: number;
475
+ function savePluginState(state) {
476
+ try {
477
+ // 验证状态数据
478
+ const validatedState = PluginStateSchema.parse(state);
479
+ // 确保状态目录存在
480
+ fs.mkdirSync(PLUGIN_STATE_DIR, { recursive: true });
481
+ // 添加时间戳
482
+ validatedState.lastUpdate = new Date().toISOString();
483
+ // 限制历史记录数量,避免文件过大
484
+ if (validatedState.tasks && validatedState.tasks.taskHistory) {
485
+ validatedState.tasks.taskHistory = validatedState.tasks.taskHistory.slice(-100);
486
+ }
487
+ if (validatedState.agentCollaboration && validatedState.agentCollaboration.collaborationHistory) {
488
+ validatedState.agentCollaboration.collaborationHistory = validatedState.agentCollaboration.collaborationHistory.slice(-50);
489
+ }
490
+ if (validatedState.context && validatedState.context.contextSnapshots) {
491
+ validatedState.context.contextSnapshots = validatedState.context.contextSnapshots.slice(-20);
492
+ }
493
+ fs.writeFileSync(PLUGIN_STATE_PATH, JSON.stringify(validatedState, null, 2), 'utf8');
494
+ console.log('插件状态已保存 (TypeScript + Zod 验证)');
495
+ }
496
+ catch (error) {
497
+ console.error(`保存插件状态失败: ${error instanceof Error ? error.message : 'Unknown error'}`);
498
+ }
681
499
  }
682
-
683
500
  /**
684
501
  * 获取锁文件路径
685
502
  */
686
- function getLockFilePath(pluginDir: string): string {
687
- return `${pluginDir}.lock`;
503
+ function getLockFilePath(pluginDir) {
504
+ return `${pluginDir}.lock`;
688
505
  }
689
-
690
506
  /**
691
507
  * 尝试获取文件锁(防止并发操作)
692
508
  */
693
- function acquireLock(lockFile: string, timeout: number = LOCK_TIMEOUT_MS): boolean {
694
- // 验证输入参数
695
- const validatedLockFile = FilePathSchema.parse(lockFile);
696
- const validatedTimeout = LockTimeoutSchema.parse(timeout);
697
-
698
- const startTime = Date.now();
699
- const pid = process.pid;
700
- const lockContent: LockContent = { pid, timestamp: Date.now() };
701
-
702
- while (Date.now() - startTime < timeout) {
703
- try {
704
- // 尝试以排他模式创建锁文件
705
- fs.writeFileSync(validatedLockFile, JSON.stringify(lockContent), { flag: 'wx' });
706
- return true;
707
- } catch (err) {
708
- if (err instanceof Error && 'code' in err && err.code === 'EEXIST') {
709
- // 锁文件已存在,检查是否过期
509
+ function acquireLock(lockFile, timeout = LOCK_TIMEOUT_MS) {
510
+ // 验证输入参数
511
+ const validatedLockFile = FilePathSchema.parse(lockFile);
512
+ const validatedTimeout = LockTimeoutSchema.parse(timeout);
513
+ const startTime = Date.now();
514
+ const pid = process.pid;
515
+ const lockContent = { pid, timestamp: Date.now() };
516
+ while (Date.now() - startTime < timeout) {
710
517
  try {
711
- const lockData = fs.readFileSync(validatedLockFile, 'utf8');
712
- let existingLock: LockContent;
713
-
714
- try {
715
- existingLock = JSON.parse(lockData);
716
- } catch (parseErr) {
717
- // JSON 解析失败,锁文件可能已损坏,清理后重试
718
- warn('锁文件格式损坏,正在清理...');
719
- fs.unlinkSync(validatedLockFile);
720
- continue;
721
- }
722
-
723
- // 验证锁内容的有效性
724
- if (!existingLock || typeof existingLock.timestamp !== 'number') {
725
- warn('锁文件内容无效,正在清理...');
726
- fs.unlinkSync(validatedLockFile);
727
- continue;
728
- }
729
-
730
- const lockAge = Date.now() - existingLock.timestamp;
731
-
732
- // 如果锁超过阈值,认为是陈旧锁,强制删除
733
- if (lockAge > LOCK_STALE_MS) {
734
- warn('检测到陈旧锁文件,正在清理...');
735
- fs.unlinkSync(validatedLockFile);
736
- continue;
737
- }
738
-
739
- // 如果是同一进程的锁,允许通过(可重入)
740
- if (existingLock.pid === pid) {
518
+ // 尝试以排他模式创建锁文件
519
+ fs.writeFileSync(validatedLockFile, JSON.stringify(lockContent), { flag: 'wx' });
741
520
  return true;
742
- }
743
-
744
- // 其他进程持有锁,等待
745
- info(`另一个 oh-my-claude 进程 (PID: ${existingLock.pid}) 正在运行,等待中...`);
746
- } catch (readErr) {
747
- // 读取锁文件失败,可能已被删除或权限问题
748
- continue;
749
- }
750
-
751
- // 使用系统 sleep 命令等待,避免忙等待占用 CPU
752
- const waitTime = Math.min(LOCK_RETRY_INTERVAL_MS, timeout - (Date.now() - startTime));
753
- if (waitTime > 0) {
754
- try {
755
- if (process.platform === 'win32') {
756
- // Windows: 使用 ping localhost 实现延时
757
- execSync(`ping -n 2 127.0.0.1`, { stdio: 'ignore', timeout: waitTime + 1000 });
758
- } else {
759
- // Unix: 使用 sleep 命令
760
- execSync(`sleep ${(waitTime / 1000).toFixed(1)}`, { stdio: 'ignore', timeout: waitTime + 1000 });
521
+ }
522
+ catch (err) {
523
+ if (err instanceof Error && 'code' in err && err.code === 'EEXIST') {
524
+ // 锁文件已存在,检查是否过期
525
+ try {
526
+ const lockData = fs.readFileSync(validatedLockFile, 'utf8');
527
+ let existingLock;
528
+ try {
529
+ existingLock = JSON.parse(lockData);
530
+ }
531
+ catch (parseErr) {
532
+ // JSON 解析失败,锁文件可能已损坏,清理后重试
533
+ warn('锁文件格式损坏,正在清理...');
534
+ fs.unlinkSync(validatedLockFile);
535
+ continue;
536
+ }
537
+ // 验证锁内容的有效性
538
+ if (!existingLock || typeof existingLock.timestamp !== 'number') {
539
+ warn('锁文件内容无效,正在清理...');
540
+ fs.unlinkSync(validatedLockFile);
541
+ continue;
542
+ }
543
+ const lockAge = Date.now() - existingLock.timestamp;
544
+ // 如果锁超过阈值,认为是陈旧锁,强制删除
545
+ if (lockAge > LOCK_STALE_MS) {
546
+ warn('检测到陈旧锁文件,正在清理...');
547
+ fs.unlinkSync(validatedLockFile);
548
+ continue;
549
+ }
550
+ // 如果是同一进程的锁,允许通过(可重入)
551
+ if (existingLock.pid === pid) {
552
+ return true;
553
+ }
554
+ // 其他进程持有锁,等待
555
+ info(`另一个 oh-my-claude 进程 (PID: ${existingLock.pid}) 正在运行,等待中...`);
556
+ }
557
+ catch (readErr) {
558
+ // 读取锁文件失败,可能已被删除或权限问题
559
+ continue;
560
+ }
561
+ // 使用系统 sleep 命令等待,避免忙等待占用 CPU
562
+ const waitTime = Math.min(LOCK_RETRY_INTERVAL_MS, timeout - (Date.now() - startTime));
563
+ if (waitTime > 0) {
564
+ try {
565
+ if (process.platform === 'win32') {
566
+ // Windows: 使用 ping localhost 实现延时
567
+ execSync(`ping -n 2 127.0.0.1`, { stdio: 'ignore', timeout: waitTime + 1000 });
568
+ }
569
+ else {
570
+ // Unix: 使用 sleep 命令
571
+ execSync(`sleep ${(waitTime / 1000).toFixed(1)}`, { stdio: 'ignore', timeout: waitTime + 1000 });
572
+ }
573
+ }
574
+ catch {
575
+ // 如果 sleep 失败,使用短暂的忙等待作为后备
576
+ const endWait = Date.now() + Math.min(waitTime, 100);
577
+ while (Date.now() < endWait) { /* 短暂等待 */ }
578
+ }
579
+ }
761
580
  }
762
- } catch {
763
- // 如果 sleep 失败,使用短暂的忙等待作为后备
764
- const endWait = Date.now() + Math.min(waitTime, 100);
765
- while (Date.now() < endWait) { /* 短暂等待 */ }
766
- }
767
- }
768
- } else {
769
- // 其他错误,直接失败
770
- return false;
771
- }
581
+ else {
582
+ // 其他错误,直接失败
583
+ return false;
584
+ }
585
+ }
772
586
  }
773
- }
774
-
775
- error('获取锁超时,可能有其他 oh-my-claude 进程正在运行');
776
- return false;
587
+ error('获取锁超时,可能有其他 oh-my-claude 进程正在运行');
588
+ return false;
777
589
  }
778
-
779
590
  /**
780
591
  * 释放文件锁
781
592
  */
782
- function releaseLock(lockFile: string): void {
783
- try {
784
- if (fs.existsSync(lockFile)) {
785
- // 读取并验证是我们自己的锁
786
- const lockData = fs.readFileSync(lockFile, 'utf8');
787
-
788
- let lockContent: LockContent;
789
- try {
790
- lockContent = JSON.parse(lockData);
791
- } catch {
792
- // JSON 解析失败,锁文件可能已损坏
793
- // 安全删除损坏的锁文件
794
- fs.unlinkSync(lockFile);
795
- return;
796
- }
797
-
798
- // 验证是我们自己的锁
799
- if (lockContent && lockContent.pid === process.pid) {
800
- fs.unlinkSync(lockFile);
801
- }
802
- }
803
- } catch {
804
- // 忽略释放锁时的错误(文件可能已被删除等)
805
- }
593
+ function releaseLock(lockFile) {
594
+ try {
595
+ if (fs.existsSync(lockFile)) {
596
+ // 读取并验证是我们自己的锁
597
+ const lockData = fs.readFileSync(lockFile, 'utf8');
598
+ let lockContent;
599
+ try {
600
+ lockContent = JSON.parse(lockData);
601
+ }
602
+ catch {
603
+ // JSON 解析失败,锁文件可能已损坏
604
+ // 安全删除损坏的锁文件
605
+ fs.unlinkSync(lockFile);
606
+ return;
607
+ }
608
+ // 验证是我们自己的锁
609
+ if (lockContent && lockContent.pid === process.pid) {
610
+ fs.unlinkSync(lockFile);
611
+ }
612
+ }
613
+ }
614
+ catch {
615
+ // 忽略释放锁时的错误(文件可能已被删除等)
616
+ }
806
617
  }
807
-
808
618
  // ==================== 带回滚保护的执行器 ====================
809
-
810
619
  /**
811
620
  * 执行带备份/回滚保护的操作(带并发锁)
812
621
  */
813
- function executeWithRollback(
814
- pluginDir: string,
815
- operation: (targetDir: string, backupDir: string) => void,
816
- operationName: string = '操作'
817
- ): boolean {
818
- const backupDir = `${pluginDir}.backup-${Date.now()}`;
819
- const lockFile = getLockFilePath(pluginDir);
820
-
821
- // 步骤0: 获取锁
822
- if (!acquireLock(lockFile)) {
823
- return false;
824
- }
825
-
826
- try {
827
- // 步骤1: 备份现有安装
828
- if (fs.existsSync(pluginDir)) {
829
- info('备份现有安装...');
830
- fs.renameSync(pluginDir, backupDir);
831
- }
832
-
833
- // 步骤2: 创建目录结构
834
- const parentDir = path.dirname(pluginDir);
835
- if (!fs.existsSync(parentDir)) {
836
- fs.mkdirSync(parentDir, { recursive: true });
837
- }
838
- fs.mkdirSync(pluginDir, { recursive: true });
839
-
840
- // 步骤3: 执行操作
841
- operation(pluginDir, backupDir);
842
-
843
- // 步骤4: 成功后删除备份
844
- if (fs.existsSync(backupDir)) {
845
- fs.rmSync(backupDir, { recursive: true, force: true });
846
- }
847
-
848
- return true;
849
-
850
- } catch (err) {
851
- // 回滚机制
852
- error(`${operationName}失败: ${err instanceof Error ? err.message : 'Unknown error'}`);
853
-
854
- // 清理失败的操作
855
- if (fs.existsSync(pluginDir)) {
856
- warn(`正在清理失败的${operationName}...`);
857
- fs.rmSync(pluginDir, { recursive: true, force: true });
858
- }
859
-
860
- // 恢复备份
861
- if (fs.existsSync(backupDir)) {
862
- info('正在恢复之前的安装...');
863
- fs.renameSync(backupDir, pluginDir);
864
- success('已恢复之前的安装');
865
- }
866
-
867
- return false;
868
-
869
- } finally {
870
- // 步骤5: 释放锁
871
- releaseLock(lockFile);
872
- }
873
- }
874
-
875
- // ==================== 文件操作工具函数 ====================
876
-
877
- interface CopyStats {
878
- files: number;
879
- dirs: number;
880
- emptyDirs: number;
622
+ function executeWithRollback(pluginDir, operation, operationName = '操作') {
623
+ const backupDir = `${pluginDir}.backup-${Date.now()}`;
624
+ const lockFile = getLockFilePath(pluginDir);
625
+ // 步骤0: 获取锁
626
+ if (!acquireLock(lockFile)) {
627
+ return false;
628
+ }
629
+ try {
630
+ // 步骤1: 备份现有安装
631
+ if (fs.existsSync(pluginDir)) {
632
+ info('备份现有安装...');
633
+ fs.renameSync(pluginDir, backupDir);
634
+ }
635
+ // 步骤2: 创建目录结构
636
+ const parentDir = path.dirname(pluginDir);
637
+ if (!fs.existsSync(parentDir)) {
638
+ fs.mkdirSync(parentDir, { recursive: true });
639
+ }
640
+ fs.mkdirSync(pluginDir, { recursive: true });
641
+ // 步骤3: 执行操作
642
+ operation(pluginDir, backupDir);
643
+ // 步骤4: 成功后删除备份
644
+ if (fs.existsSync(backupDir)) {
645
+ fs.rmSync(backupDir, { recursive: true, force: true });
646
+ }
647
+ return true;
648
+ }
649
+ catch (err) {
650
+ // 回滚机制
651
+ error(`${operationName}失败: ${err instanceof Error ? err.message : 'Unknown error'}`);
652
+ // 清理失败的操作
653
+ if (fs.existsSync(pluginDir)) {
654
+ warn(`正在清理失败的${operationName}...`);
655
+ fs.rmSync(pluginDir, { recursive: true, force: true });
656
+ }
657
+ // 恢复备份
658
+ if (fs.existsSync(backupDir)) {
659
+ info('正在恢复之前的安装...');
660
+ fs.renameSync(backupDir, pluginDir);
661
+ success('已恢复之前的安装');
662
+ }
663
+ return false;
664
+ }
665
+ finally {
666
+ // 步骤5: 释放锁
667
+ releaseLock(lockFile);
668
+ }
881
669
  }
882
-
883
670
  /**
884
671
  * 使用流式方式复制大文件
885
672
  */
886
- function copyFileStream(src: string, dest: string): Promise<void> {
887
- return new Promise((resolve, reject) => {
888
- const readStream = fs.createReadStream(src);
889
- const writeStream = fs.createWriteStream(dest);
890
-
891
- readStream.on('error', reject);
892
- writeStream.on('error', reject);
893
- writeStream.on('finish', resolve);
894
-
895
- readStream.pipe(writeStream);
896
- });
673
+ function copyFileStream(src, dest) {
674
+ return new Promise((resolve, reject) => {
675
+ const readStream = fs.createReadStream(src);
676
+ const writeStream = fs.createWriteStream(dest);
677
+ readStream.on('error', reject);
678
+ writeStream.on('error', reject);
679
+ writeStream.on('finish', resolve);
680
+ readStream.pipe(writeStream);
681
+ });
897
682
  }
898
-
899
683
  /**
900
684
  * 智能复制文件(小文件同步,大文件流式)
901
685
  */
902
- function smartCopyFile(src: string, dest: string): void {
903
- const stats = fs.statSync(src);
904
-
905
- if (stats.size > LARGE_FILE_THRESHOLD_BYTES) {
906
- // 大文件使用流式复制(但为了保持同步接口,使用同步方式等待)
907
- // CLI 工具中,大文件较少,这种处理方式可接受
908
- const buffer = Buffer.alloc(COPY_BUFFER_SIZE);
909
- const fdSrc = fs.openSync(src, 'r');
910
- const fdDest = fs.openSync(dest, 'w');
911
-
912
- try {
913
- let bytesRead;
914
- while ((bytesRead = fs.readSync(fdSrc, buffer)) > 0) {
915
- fs.writeSync(fdDest, buffer, 0, bytesRead);
916
- }
917
- } finally {
918
- fs.closeSync(fdSrc);
919
- fs.closeSync(fdDest);
920
- }
921
-
922
- // 保留文件时间戳
923
- fs.utimesSync(dest, stats.atime, stats.mtime);
924
- } else {
925
- // 小文件直接同步复制
926
- fs.copyFileSync(src, dest);
927
- }
686
+ function smartCopyFile(src, dest) {
687
+ const stats = fs.statSync(src);
688
+ if (stats.size > LARGE_FILE_THRESHOLD_BYTES) {
689
+ // 大文件使用流式复制(但为了保持同步接口,使用同步方式等待)
690
+ // 在 CLI 工具中,大文件较少,这种处理方式可接受
691
+ const buffer = Buffer.alloc(COPY_BUFFER_SIZE);
692
+ const fdSrc = fs.openSync(src, 'r');
693
+ const fdDest = fs.openSync(dest, 'w');
694
+ try {
695
+ let bytesRead;
696
+ while ((bytesRead = fs.readSync(fdSrc, buffer)) > 0) {
697
+ fs.writeSync(fdDest, buffer, 0, bytesRead);
698
+ }
699
+ }
700
+ finally {
701
+ fs.closeSync(fdSrc);
702
+ fs.closeSync(fdDest);
703
+ }
704
+ // 保留文件时间戳
705
+ fs.utimesSync(dest, stats.atime, stats.mtime);
706
+ }
707
+ else {
708
+ // 小文件直接同步复制
709
+ fs.copyFileSync(src, dest);
710
+ }
928
711
  }
929
-
930
712
  /**
931
713
  * 复制目录(支持大文件流式处理和空目录处理)
932
714
  */
933
- function copyDir(
934
- src: string,
935
- dest: string,
936
- options: { preserveEmpty?: boolean } = {}
937
- ): CopyStats {
938
- const { preserveEmpty = true } = options;
939
- const stats: CopyStats = { files: 0, dirs: 0, emptyDirs: 0 };
940
-
941
- fs.mkdirSync(dest, { recursive: true });
942
- stats.dirs++;
943
-
944
- const entries = fs.readdirSync(src, { withFileTypes: true });
945
-
946
- // 检查是否为空目录
947
- if (entries.length === 0) {
948
- stats.emptyDirs++;
949
- if (!preserveEmpty) {
950
- // 如果不保留空目录,删除刚创建的目录
951
- fs.rmdirSync(dest);
952
- stats.dirs--;
715
+ function copyDir(src, dest, options = {}) {
716
+ const { preserveEmpty = true } = options;
717
+ const stats = { files: 0, dirs: 0, emptyDirs: 0 };
718
+ fs.mkdirSync(dest, { recursive: true });
719
+ stats.dirs++;
720
+ const entries = fs.readdirSync(src, { withFileTypes: true });
721
+ // 检查是否为空目录
722
+ if (entries.length === 0) {
723
+ stats.emptyDirs++;
724
+ if (!preserveEmpty) {
725
+ // 如果不保留空目录,删除刚创建的目录
726
+ fs.rmdirSync(dest);
727
+ stats.dirs--;
728
+ }
729
+ return stats;
730
+ }
731
+ for (const entry of entries) {
732
+ const srcPath = path.join(src, entry.name);
733
+ const destPath = path.join(dest, entry.name);
734
+ if (entry.isDirectory()) {
735
+ const subStats = copyDir(srcPath, destPath, options);
736
+ stats.files += subStats.files;
737
+ stats.dirs += subStats.dirs;
738
+ stats.emptyDirs += subStats.emptyDirs;
739
+ }
740
+ else {
741
+ smartCopyFile(srcPath, destPath);
742
+ stats.files++;
743
+ }
953
744
  }
954
745
  return stats;
955
- }
956
-
957
- for (const entry of entries) {
958
- const srcPath = path.join(src, entry.name);
959
- const destPath = path.join(dest, entry.name);
960
-
961
- if (entry.isDirectory()) {
962
- const subStats = copyDir(srcPath, destPath, options);
963
- stats.files += subStats.files;
964
- stats.dirs += subStats.dirs;
965
- stats.emptyDirs += subStats.emptyDirs;
966
- } else {
967
- smartCopyFile(srcPath, destPath);
968
- stats.files++;
969
- }
970
- }
971
-
972
- return stats;
973
746
  }
974
-
975
747
  // ==================== 插件文件操作 ====================
976
-
977
748
  // 插件目录和文件配置
978
749
  const PLUGIN_DIRS = ['agents', 'commands', 'hooks', 'skills', '.claude-plugin'];
979
750
  const PLUGIN_FILES = ['README.md', 'README_EN.md', 'LICENSE'];
980
-
981
- interface CopyResult {
982
- dirs: number;
983
- files: number;
984
- errors: string[];
985
- }
986
-
987
- interface InstallResult {
988
- count: number;
989
- errors: string[];
990
- cleaned?: boolean;
991
- }
992
-
993
751
  /**
994
752
  * 复制插件文件到目标目录(带进度显示和 Zod 验证)
995
753
  */
996
- function copyPluginFiles(packageDir: string, pluginDir: string, showProgress: boolean = true): CopyResult {
997
- // 验证输入参数
998
- const validatedPackageDir = FilePathSchema.parse(packageDir);
999
- const validatedPluginDir = FilePathSchema.parse(pluginDir);
1000
-
1001
- const stats: CopyResult = { dirs: 0, files: 0, errors: [] };
1002
-
1003
- // 计算总步骤数
1004
- const totalSteps = PLUGIN_DIRS.length + PLUGIN_FILES.length;
1005
- const progress = showProgress ? new ProgressIndicator(totalSteps, '复制文件') : null;
1006
-
1007
- // 复制目录
1008
- for (const dir of PLUGIN_DIRS) {
1009
- const src = path.join(validatedPackageDir, dir);
1010
- const dest = path.join(validatedPluginDir, dir);
1011
-
1012
- if (!fs.existsSync(src)) {
1013
- stats.errors.push(`目录不存在: ${dir}`);
1014
- if (progress) progress.update(`跳过 ${dir}/`);
1015
- continue;
1016
- }
1017
-
1018
- try {
1019
- copyDir(src, dest);
1020
- stats.dirs++;
1021
- if (progress) progress.update(`${dir}/`);
1022
- } catch (err) {
1023
- stats.errors.push(`复制目录失败 ${dir}: ${err instanceof Error ? err.message : 'Unknown error'}`);
1024
- if (progress) progress.update(`失败 ${dir}/`);
1025
- }
1026
- }
1027
-
1028
- // 复制文件
1029
- for (const file of PLUGIN_FILES) {
1030
- const src = path.join(validatedPackageDir, file);
1031
- const dest = path.join(validatedPluginDir, file);
1032
-
1033
- if (!fs.existsSync(src)) {
1034
- if (progress) progress.update(`跳过 ${file}`);
1035
- continue; // 文件可选,不记录错误
1036
- }
1037
-
1038
- try {
1039
- fs.copyFileSync(src, dest);
1040
- stats.files++;
1041
- if (progress) progress.update(file);
1042
- } catch (err) {
1043
- stats.errors.push(`复制文件失败 ${file}: ${err instanceof Error ? err.message : 'Unknown error'}`);
1044
- if (progress) progress.update(`失败 ${file}`);
1045
- }
1046
- }
1047
-
1048
- if (progress) {
1049
- const hasErrors = stats.errors.length > 0;
1050
- if (hasErrors) {
1051
- progress.fail('复制完成(有警告)');
1052
- } else {
1053
- progress.complete('文件复制完成');
1054
- }
1055
- }
1056
-
1057
- // 验证输出
1058
- return CopyResultSchema.parse(stats);
754
+ function copyPluginFiles(packageDir, pluginDir, showProgress = true) {
755
+ // 验证输入参数
756
+ const validatedPackageDir = FilePathSchema.parse(packageDir);
757
+ const validatedPluginDir = FilePathSchema.parse(pluginDir);
758
+ const stats = { dirs: 0, files: 0, errors: [] };
759
+ // 计算总步骤数
760
+ const totalSteps = PLUGIN_DIRS.length + PLUGIN_FILES.length;
761
+ const progress = showProgress ? new ProgressIndicator(totalSteps, '复制文件') : null;
762
+ // 复制目录
763
+ for (const dir of PLUGIN_DIRS) {
764
+ const src = path.join(validatedPackageDir, dir);
765
+ const dest = path.join(validatedPluginDir, dir);
766
+ if (!fs.existsSync(src)) {
767
+ stats.errors.push(`目录不存在: ${dir}`);
768
+ if (progress)
769
+ progress.update(`跳过 ${dir}/`);
770
+ continue;
771
+ }
772
+ try {
773
+ copyDir(src, dest);
774
+ stats.dirs++;
775
+ if (progress)
776
+ progress.update(`${dir}/`);
777
+ }
778
+ catch (err) {
779
+ stats.errors.push(`复制目录失败 ${dir}: ${err instanceof Error ? err.message : 'Unknown error'}`);
780
+ if (progress)
781
+ progress.update(`失败 ${dir}/`);
782
+ }
783
+ }
784
+ // 复制文件
785
+ for (const file of PLUGIN_FILES) {
786
+ const src = path.join(validatedPackageDir, file);
787
+ const dest = path.join(validatedPluginDir, file);
788
+ if (!fs.existsSync(src)) {
789
+ if (progress)
790
+ progress.update(`跳过 ${file}`);
791
+ continue; // 文件可选,不记录错误
792
+ }
793
+ try {
794
+ fs.copyFileSync(src, dest);
795
+ stats.files++;
796
+ if (progress)
797
+ progress.update(file);
798
+ }
799
+ catch (err) {
800
+ stats.errors.push(`复制文件失败 ${file}: ${err instanceof Error ? err.message : 'Unknown error'}`);
801
+ if (progress)
802
+ progress.update(`失败 ${file}`);
803
+ }
804
+ }
805
+ if (progress) {
806
+ const hasErrors = stats.errors.length > 0;
807
+ if (hasErrors) {
808
+ progress.fail('复制完成(有警告)');
809
+ }
810
+ else {
811
+ progress.complete('文件复制完成');
812
+ }
813
+ }
814
+ // 验证输出
815
+ return CopyResultSchema.parse(stats);
1059
816
  }
1060
-
1061
817
  /**
1062
818
  * 设置 hook 脚本权限(Unix)
1063
819
  */
1064
- function setHookPermissions(pluginDir: string): void {
1065
- if (os.platform() === 'win32') {
1066
- return; // Windows 不需要设置权限
1067
- }
1068
-
1069
- const hooksDir = path.join(pluginDir, 'hooks');
1070
- if (!fs.existsSync(hooksDir)) {
1071
- return;
1072
- }
1073
-
1074
- const hookFiles = fs.readdirSync(hooksDir).filter(f => f.endsWith('.sh'));
1075
- for (const hookFile of hookFiles) {
1076
- try {
1077
- fs.chmodSync(path.join(hooksDir, hookFile), '755');
1078
- } catch (err) {
1079
- warn(`设置权限失败: ${hookFile}`);
820
+ function setHookPermissions(pluginDir) {
821
+ if (os.platform() === 'win32') {
822
+ return; // Windows 不需要设置权限
823
+ }
824
+ const hooksDir = path.join(pluginDir, 'hooks');
825
+ if (!fs.existsSync(hooksDir)) {
826
+ return;
827
+ }
828
+ const hookFiles = fs.readdirSync(hooksDir).filter(f => f.endsWith('.sh'));
829
+ for (const hookFile of hookFiles) {
830
+ try {
831
+ fs.chmodSync(path.join(hooksDir, hookFile), '755');
832
+ }
833
+ catch (err) {
834
+ warn(`设置权限失败: ${hookFile}`);
835
+ }
1080
836
  }
1081
- }
1082
837
  }
1083
-
1084
838
  /**
1085
839
  * 安装 slash commands 到 ~/.claude/commands/
1086
840
  */
1087
- function installCommands(packageDir: string): InstallResult {
1088
- const commandsDir = getCommandsDir();
1089
- const commandsSrc = path.join(packageDir, 'commands');
1090
- const stats: InstallResult = { count: 0, errors: [], cleaned: false };
1091
-
1092
- info('正在安装 slash commands...');
1093
-
1094
- // 清理旧版本的 zcf 子目录(1.0.8 及之前版本使用 /zcf:* 命令格式)
1095
- const legacyZcfDir = path.join(commandsDir, 'zcf');
1096
- if (fs.existsSync(legacyZcfDir)) {
1097
- try {
1098
- fs.rmSync(legacyZcfDir, { recursive: true, force: true });
1099
- stats.cleaned = true;
1100
- info('已清理旧版本 zcf 子目录');
1101
- } catch (err) {
1102
- warn(`清理旧版本目录失败: ${err instanceof Error ? err.message : 'Unknown error'}`);
1103
- }
1104
- }
1105
-
1106
- // 创建 commands 目录
1107
- if (!fs.existsSync(commandsDir)) {
1108
- fs.mkdirSync(commandsDir, { recursive: true });
1109
- }
1110
-
1111
- // 复制所有 .md 文件
1112
- if (fs.existsSync(commandsSrc)) {
1113
- const mdFiles = fs.readdirSync(commandsSrc).filter(f => f.endsWith('.md'));
1114
- for (const file of mdFiles) {
1115
- try {
1116
- fs.copyFileSync(
1117
- path.join(commandsSrc, file),
1118
- path.join(commandsDir, file)
1119
- );
1120
- stats.count++;
1121
- } catch (err) {
1122
- stats.errors.push(`复制 ${file} 失败: ${err instanceof Error ? err.message : 'Unknown error'}`);
1123
- }
1124
- }
1125
- }
1126
-
1127
- if (stats.count > 0) {
1128
- success(`Slash commands 安装完成 (${stats.count} 个命令)`);
1129
- info(`Commands 位置: ${commandsDir}`);
1130
- if (stats.cleaned) {
1131
- warn('⚠️ 命令格式已更改:请使用 /yishan 而非 /zcf:yishan');
1132
- }
1133
- } else {
1134
- warn('未找到任何命令文件');
1135
- }
1136
-
1137
- return InstallResultSchema.parse(stats);
841
+ function installCommands(packageDir) {
842
+ const commandsDir = getCommandsDir();
843
+ const commandsSrc = path.join(packageDir, 'commands');
844
+ const stats = { count: 0, errors: [], cleaned: false };
845
+ info('正在安装 slash commands...');
846
+ // 清理旧版本的 zcf 子目录(1.0.8 及之前版本使用 /zcf:* 命令格式)
847
+ const legacyZcfDir = path.join(commandsDir, 'zcf');
848
+ if (fs.existsSync(legacyZcfDir)) {
849
+ try {
850
+ fs.rmSync(legacyZcfDir, { recursive: true, force: true });
851
+ stats.cleaned = true;
852
+ info('已清理旧版本 zcf 子目录');
853
+ }
854
+ catch (err) {
855
+ warn(`清理旧版本目录失败: ${err instanceof Error ? err.message : 'Unknown error'}`);
856
+ }
857
+ }
858
+ // 创建 commands 目录
859
+ if (!fs.existsSync(commandsDir)) {
860
+ fs.mkdirSync(commandsDir, { recursive: true });
861
+ }
862
+ // 复制所有 .md 文件
863
+ if (fs.existsSync(commandsSrc)) {
864
+ const mdFiles = fs.readdirSync(commandsSrc).filter(f => f.endsWith('.md'));
865
+ for (const file of mdFiles) {
866
+ try {
867
+ fs.copyFileSync(path.join(commandsSrc, file), path.join(commandsDir, file));
868
+ stats.count++;
869
+ }
870
+ catch (err) {
871
+ stats.errors.push(`复制 ${file} 失败: ${err instanceof Error ? err.message : 'Unknown error'}`);
872
+ }
873
+ }
874
+ }
875
+ if (stats.count > 0) {
876
+ success(`Slash commands 安装完成 (${stats.count} 个命令)`);
877
+ info(`Commands 位置: ${commandsDir}`);
878
+ if (stats.cleaned) {
879
+ warn('⚠️ 命令格式已更改:请使用 /yishan 而非 /zcf:yishan');
880
+ }
881
+ }
882
+ else {
883
+ warn('未找到任何命令文件');
884
+ }
885
+ return InstallResultSchema.parse(stats);
1138
886
  }
1139
-
1140
887
  /**
1141
888
  * 安装 skills 到 ~/.claude/skills/
1142
889
  */
1143
- function installSkills(packageDir: string): InstallResult {
1144
- const skillsDir = getSkillsDir();
1145
- const skillsSrc = path.join(packageDir, 'skills');
1146
- const stats: InstallResult = { count: 0, errors: [] };
1147
-
1148
- info('正在安装 skills...');
1149
-
1150
- if (fs.existsSync(skillsSrc)) {
1151
- const skillDirs = fs.readdirSync(skillsSrc, { withFileTypes: true })
1152
- .filter(d => d.isDirectory());
1153
-
1154
- for (const skillDir of skillDirs) {
1155
- const skillName = skillDir.name;
1156
- const skillSrcDir = path.join(skillsSrc, skillName);
1157
- const skillDestDir = path.join(skillsDir, skillName);
1158
-
1159
- try {
1160
- // 创建 skill 目录
1161
- if (!fs.existsSync(skillDestDir)) {
1162
- fs.mkdirSync(skillDestDir, { recursive: true });
1163
- }
1164
-
1165
- // 复制 SKILL.md (如果存在)
1166
- const skillMdSrc = path.join(skillSrcDir, 'SKILL.md');
1167
- if (fs.existsSync(skillMdSrc)) {
1168
- fs.copyFileSync(skillMdSrc, path.join(skillDestDir, 'SKILL.md'));
1169
- }
1170
-
1171
- // 复制其他支持文件(排除 skill.json)
1172
- const files = fs.readdirSync(skillSrcDir, { withFileTypes: true })
1173
- .filter(f => f.isFile() && f.name !== 'skill.json');
1174
-
1175
- for (const file of files) {
1176
- try {
1177
- fs.copyFileSync(
1178
- path.join(skillSrcDir, file.name),
1179
- path.join(skillDestDir, file.name)
1180
- );
1181
- } catch {
1182
- // 忽略非关键文件的复制错误
1183
- }
1184
- }
1185
-
1186
- stats.count++;
1187
- } catch (err) {
1188
- stats.errors.push(`安装 skill ${skillName} 失败: ${err instanceof Error ? err.message : 'Unknown error'}`);
1189
- }
1190
- }
1191
- }
1192
-
1193
- if (stats.count > 0) {
1194
- success(`Skills 安装完成 (${stats.count} 个 skill)`);
1195
- info(`Skills 位置: ${skillsDir}`);
1196
- }
1197
-
1198
- return InstallResultSchema.parse(stats);
890
+ function installSkills(packageDir) {
891
+ const skillsDir = getSkillsDir();
892
+ const skillsSrc = path.join(packageDir, 'skills');
893
+ const stats = { count: 0, errors: [] };
894
+ info('正在安装 skills...');
895
+ if (fs.existsSync(skillsSrc)) {
896
+ const skillDirs = fs.readdirSync(skillsSrc, { withFileTypes: true })
897
+ .filter(d => d.isDirectory());
898
+ for (const skillDir of skillDirs) {
899
+ const skillName = skillDir.name;
900
+ const skillSrcDir = path.join(skillsSrc, skillName);
901
+ const skillDestDir = path.join(skillsDir, skillName);
902
+ try {
903
+ // 创建 skill 目录
904
+ if (!fs.existsSync(skillDestDir)) {
905
+ fs.mkdirSync(skillDestDir, { recursive: true });
906
+ }
907
+ // 复制 SKILL.md (如果存在)
908
+ const skillMdSrc = path.join(skillSrcDir, 'SKILL.md');
909
+ if (fs.existsSync(skillMdSrc)) {
910
+ fs.copyFileSync(skillMdSrc, path.join(skillDestDir, 'SKILL.md'));
911
+ }
912
+ // 复制其他支持文件(排除 skill.json)
913
+ const files = fs.readdirSync(skillSrcDir, { withFileTypes: true })
914
+ .filter(f => f.isFile() && f.name !== 'skill.json');
915
+ for (const file of files) {
916
+ try {
917
+ fs.copyFileSync(path.join(skillSrcDir, file.name), path.join(skillDestDir, file.name));
918
+ }
919
+ catch {
920
+ // 忽略非关键文件的复制错误
921
+ }
922
+ }
923
+ stats.count++;
924
+ }
925
+ catch (err) {
926
+ stats.errors.push(`安装 skill ${skillName} 失败: ${err instanceof Error ? err.message : 'Unknown error'}`);
927
+ }
928
+ }
929
+ }
930
+ if (stats.count > 0) {
931
+ success(`Skills 安装完成 (${stats.count} 个 skill)`);
932
+ info(`Skills 位置: ${skillsDir}`);
933
+ }
934
+ return InstallResultSchema.parse(stats);
1199
935
  }
1200
-
1201
936
  /**
1202
937
  * 验证安装结果
1203
938
  */
1204
- function verifyInstallation(): VerificationResult {
1205
- info('验证安装...');
1206
- const commandsDir = getCommandsDir();
1207
- const result: VerificationResult = { success: true, errors: [] };
1208
-
1209
- // 检查关键命令文件
1210
- const yishanPath = path.join(commandsDir, 'yishan.md');
1211
- if (fs.existsSync(yishanPath)) {
1212
- success('✓ yishan.md 已安装');
1213
- } else {
1214
- warn('✗ yishan.md 未找到');
1215
- result.errors.push('yishan.md 未找到');
1216
- result.success = false;
1217
- }
1218
-
1219
- // 检查命令数量
1220
- if (fs.existsSync(commandsDir)) {
1221
- const cmdFiles = fs.readdirSync(commandsDir).filter(f => f.endsWith('.md'));
1222
- if (cmdFiles.length > 0) {
1223
- success(`✓ 已安装 ${cmdFiles.length} 个命令`);
1224
- } else {
1225
- warn('✗ 未检测到任何命令文件');
1226
- result.errors.push('未检测到命令文件');
1227
- result.success = false;
1228
- }
1229
- } else {
1230
- warn('✗ commands 目录不存在');
1231
- result.errors.push('commands 目录不存在');
1232
- result.success = false;
1233
- }
1234
-
1235
- if (!result.success) {
1236
- warn('安装可能不完整,请检查上述警告');
1237
- }
1238
-
1239
- return VerificationResultSchema.parse(result);
939
+ function verifyInstallation() {
940
+ info('验证安装...');
941
+ const commandsDir = getCommandsDir();
942
+ const result = { success: true, errors: [] };
943
+ // 检查关键命令文件
944
+ const yishanPath = path.join(commandsDir, 'yishan.md');
945
+ if (fs.existsSync(yishanPath)) {
946
+ success('✓ yishan.md 已安装');
947
+ }
948
+ else {
949
+ warn('✗ yishan.md 未找到');
950
+ result.errors.push('yishan.md 未找到');
951
+ result.success = false;
952
+ }
953
+ // 检查命令数量
954
+ if (fs.existsSync(commandsDir)) {
955
+ const cmdFiles = fs.readdirSync(commandsDir).filter(f => f.endsWith('.md'));
956
+ if (cmdFiles.length > 0) {
957
+ success(`✓ 已安装 ${cmdFiles.length} 个命令`);
958
+ }
959
+ else {
960
+ warn('✗ 未检测到任何命令文件');
961
+ result.errors.push('未检测到命令文件');
962
+ result.success = false;
963
+ }
964
+ }
965
+ else {
966
+ warn('commands 目录不存在');
967
+ result.errors.push('commands 目录不存在');
968
+ result.success = false;
969
+ }
970
+ if (!result.success) {
971
+ warn('安装可能不完整,请检查上述警告');
972
+ }
973
+ return VerificationResultSchema.parse(result);
1240
974
  }
1241
-
1242
975
  /**
1243
976
  * 注册插件到 Claude Code
1244
977
  */
1245
- function registerPlugin(pluginDir: string): boolean {
1246
- try {
1247
- info('正在注册插件...');
1248
- // 使用 execSync 执行命令
1249
- execSync(`claude plugins install "${pluginDir}"`, {
1250
- stdio: 'inherit'
1251
- });
1252
-
1253
- success('插件注册成功');
1254
- return true;
1255
- } catch (e) {
1256
- warn('自动注册失败,请手动运行:');
1257
- warn(` claude plugins install "${pluginDir}"`);
1258
- return false;
1259
- }
978
+ function registerPlugin(pluginDir) {
979
+ try {
980
+ info('正在注册插件...');
981
+ // 使用 execSync 执行命令
982
+ execSync(`claude plugins install "${pluginDir}"`, {
983
+ stdio: 'inherit'
984
+ });
985
+ success('插件注册成功');
986
+ return true;
987
+ }
988
+ catch (e) {
989
+ warn('自动注册失败,请手动运行:');
990
+ warn(` claude plugins install "${pluginDir}"`);
991
+ return false;
992
+ }
1260
993
  }
1261
-
1262
994
  // ==================== ProgressIndicator 类 ====================
1263
-
1264
995
  /**
1265
996
  * 进度显示器类
1266
997
  * 用于在 CLI 中显示操作进度
1267
998
  */
1268
999
  class ProgressIndicator {
1269
- private totalSteps: number;
1270
- private currentStep: number;
1271
- private description: string;
1272
- private startTime: number;
1273
- private isInteractive: boolean;
1274
-
1275
- constructor(totalSteps: number, description: string = '处理中') {
1276
- this.totalSteps = totalSteps;
1277
- this.currentStep = 0;
1278
- this.description = description;
1279
- this.startTime = Date.now();
1280
- this.isInteractive = process.stdout.isTTY;
1281
- }
1282
-
1283
- /**
1284
- * 更新进度
1285
- */
1286
- update(stepDescription: string = ''): void {
1287
- this.currentStep++;
1288
- const percent = Math.round((this.currentStep / this.totalSteps) * 100);
1289
- const elapsed = ((Date.now() - this.startTime) / 1000).toFixed(1);
1290
-
1291
- if (this.isInteractive) {
1292
- // 交互式终端:使用回车覆盖当前行
1293
- const progressBar = this._createProgressBar(percent);
1294
- process.stdout.write(`\r${colors.cyan}${progressBar} ${percent}%${colors.reset} ${stepDescription}`.padEnd(80));
1295
- } else {
1296
- // 非交互式终端:每次输出新行
1297
- info(`[${this.currentStep}/${this.totalSteps}] ${stepDescription}`);
1298
- }
1299
- }
1300
-
1301
- /**
1302
- * 创建进度条
1303
- */
1304
- private _createProgressBar(percent: number): string {
1305
- const total = 20;
1306
- const filled = Math.round((percent / 100) * total);
1307
- const empty = total - filled;
1308
- return `[${'█'.repeat(filled)}${'░'.repeat(empty)}]`;
1309
- }
1310
-
1311
- /**
1312
- * 完成进度
1313
- */
1314
- complete(message: string = '完成'): void {
1315
- if (this.isInteractive) {
1316
- process.stdout.write('\r' + ' '.repeat(80) + '\r'); // 清除进度行
1317
- }
1318
- const elapsed = ((Date.now() - this.startTime) / 1000).toFixed(1);
1319
- success(`${message} (耗时 ${elapsed}s)`);
1320
- }
1321
-
1322
- /**
1323
- * 失败时清理
1324
- */
1325
- fail(message: string = '失败'): void {
1326
- if (this.isInteractive) {
1327
- process.stdout.write('\r' + ' '.repeat(80) + '\r'); // 清除进度行
1328
- }
1329
- error(`${message}`);
1330
- }
1000
+ constructor(totalSteps, description = '处理中') {
1001
+ this.totalSteps = totalSteps;
1002
+ this.currentStep = 0;
1003
+ this.description = description;
1004
+ this.startTime = Date.now();
1005
+ this.isInteractive = process.stdout.isTTY;
1006
+ }
1007
+ /**
1008
+ * 更新进度
1009
+ */
1010
+ update(stepDescription = '') {
1011
+ this.currentStep++;
1012
+ const percent = Math.round((this.currentStep / this.totalSteps) * 100);
1013
+ const elapsed = ((Date.now() - this.startTime) / 1000).toFixed(1);
1014
+ if (this.isInteractive) {
1015
+ // 交互式终端:使用回车覆盖当前行
1016
+ const progressBar = this._createProgressBar(percent);
1017
+ process.stdout.write(`\r${colors.cyan}${progressBar} ${percent}%${colors.reset} ${stepDescription}`.padEnd(80));
1018
+ }
1019
+ else {
1020
+ // 非交互式终端:每次输出新行
1021
+ info(`[${this.currentStep}/${this.totalSteps}] ${stepDescription}`);
1022
+ }
1023
+ }
1024
+ /**
1025
+ * 创建进度条
1026
+ */
1027
+ _createProgressBar(percent) {
1028
+ const total = 20;
1029
+ const filled = Math.round((percent / 100) * total);
1030
+ const empty = total - filled;
1031
+ return `[${'█'.repeat(filled)}${'░'.repeat(empty)}]`;
1032
+ }
1033
+ /**
1034
+ * 完成进度
1035
+ */
1036
+ complete(message = '完成') {
1037
+ if (this.isInteractive) {
1038
+ process.stdout.write('\r' + ' '.repeat(80) + '\r'); // 清除进度行
1039
+ }
1040
+ const elapsed = ((Date.now() - this.startTime) / 1000).toFixed(1);
1041
+ success(`${message} (耗时 ${elapsed}s)`);
1042
+ }
1043
+ /**
1044
+ * 失败时清理
1045
+ */
1046
+ fail(message = '失败') {
1047
+ if (this.isInteractive) {
1048
+ process.stdout.write('\r' + ' '.repeat(80) + '\r'); // 清除进度行
1049
+ }
1050
+ error(`${message}`);
1051
+ }
1331
1052
  }
1332
-
1333
1053
  // ==================== 主要命令函数 ====================
1334
-
1335
1054
  /**
1336
1055
  * 执行卸载操作
1337
1056
  */
1338
- function performUninstall(pluginDir: string): void {
1339
- // 使用 Claude CLI 卸载
1340
- try {
1341
- info('正在卸载插件...');
1342
- execSync(`claude plugins uninstall ${PLUGIN_NAME}`, {
1343
- stdio: 'inherit'
1344
- });
1345
- } catch {
1346
- // 忽略错误,继续删除文件
1347
- }
1348
-
1349
- // 删除文件
1350
- fs.rmSync(pluginDir, { recursive: true, force: true });
1351
-
1352
- success('插件已卸载');
1353
- log('');
1057
+ function performUninstall(pluginDir) {
1058
+ // 使用 Claude CLI 卸载
1059
+ try {
1060
+ info('正在卸载插件...');
1061
+ execSync(`claude plugins uninstall ${PLUGIN_NAME}`, {
1062
+ stdio: 'inherit'
1063
+ });
1064
+ }
1065
+ catch {
1066
+ // 忽略错误,继续删除文件
1067
+ }
1068
+ // 删除文件
1069
+ fs.rmSync(pluginDir, { recursive: true, force: true });
1070
+ success('插件已卸载');
1071
+ log('');
1354
1072
  }
1355
-
1356
1073
  /**
1357
1074
  * 注册核心 Agent
1358
1075
  */
1359
- function registerCoreAgents(): void {
1360
- const coreAgents = [
1361
- { id: 'yugong', name: '愚公 (YuGong)' },
1362
- { id: 'zhuge', name: '诸葛 (ZhuGe)' },
1363
- { id: 'luban', name: '鲁班 (LuBan)' },
1364
- { id: 'wukong', name: '悟空 (WuKong)' },
1365
- { id: 'bianque', name: '扁鹊 (BianQue)' },
1366
- { id: 'mozi', name: '墨子 (MoZi)' },
1367
- { id: 'sunzi', name: '孙子 (SunZi)' },
1368
- { id: 'simaqian', name: '司马迁 (SimaQian)' },
1369
- { id: 'zhenghe', name: '郑和 (ZhengHe)' },
1370
- { id: 'zhangheng', name: '张衡 (ZhangHeng)' },
1371
- { id: 'libing', name: '李冰 (LiBing)' },
1372
- { id: 'laozi', name: '老子 (LaoZi)' },
1373
- { id: 'baozheng', name: '包拯 (BaoZheng)' },
1374
- { id: 'weizheng', name: '魏征 (WeiZheng)' },
1375
- { id: 'cangjie', name: '仓颉 (CangJie)' },
1376
- { id: 'libai', name: '李白 (LiBai)' },
1377
- { id: 'gukaizhi', name: '顾恺之 (GuKaiZhi)' },
1378
- { id: 'change', name: '嫦娥 (ChangE)' }
1379
- ];
1380
-
1381
- for (const agent of coreAgents) {
1382
- try {
1383
- agentStateManager.registerAgent(agent.id, agent.name);
1384
- info(`已注册 Agent: ${agent.name}`);
1385
- } catch (error) {
1386
- warn(`注册 Agent ${agent.name} 失败: ${error instanceof Error ? error.message : 'Unknown error'}`);
1076
+ function registerCoreAgents() {
1077
+ const coreAgents = [
1078
+ { id: 'yugong', name: '愚公 (YuGong)' },
1079
+ { id: 'zhuge', name: '诸葛 (ZhuGe)' },
1080
+ { id: 'luban', name: '鲁班 (LuBan)' },
1081
+ { id: 'wukong', name: '悟空 (WuKong)' },
1082
+ { id: 'bianque', name: '扁鹊 (BianQue)' },
1083
+ { id: 'mozi', name: '墨子 (MoZi)' },
1084
+ { id: 'sunzi', name: '孙子 (SunZi)' },
1085
+ { id: 'simaqian', name: '司马迁 (SimaQian)' },
1086
+ { id: 'zhenghe', name: '郑和 (ZhengHe)' },
1087
+ { id: 'zhangheng', name: '张衡 (ZhangHeng)' },
1088
+ { id: 'libing', name: '李冰 (LiBing)' },
1089
+ { id: 'laozi', name: '老子 (LaoZi)' },
1090
+ { id: 'baozheng', name: '包拯 (BaoZheng)' },
1091
+ { id: 'weizheng', name: '魏征 (WeiZheng)' },
1092
+ { id: 'cangjie', name: '仓颉 (CangJie)' },
1093
+ { id: 'libai', name: '李白 (LiBai)' },
1094
+ { id: 'gukaizhi', name: '顾恺之 (GuKaiZhi)' },
1095
+ { id: 'change', name: '嫦娥 (ChangE)' }
1096
+ ];
1097
+ for (const agent of coreAgents) {
1098
+ try {
1099
+ agentStateManager.registerAgent(agent.id, agent.name);
1100
+ info(`已注册 Agent: ${agent.name}`);
1101
+ }
1102
+ catch (error) {
1103
+ warn(`注册 Agent ${agent.name} 失败: ${error instanceof Error ? error.message : 'Unknown error'}`);
1104
+ }
1387
1105
  }
1388
- }
1389
1106
  }
1390
-
1391
1107
  /**
1392
1108
  * 安装插件(带回滚机制)
1393
1109
  */
1394
- async function install(): Promise<void> {
1395
- log('\n🏔️ oh-my-claude 安装程序', 'cyan');
1396
- log('━'.repeat(DIVIDER_LENGTH), 'cyan');
1397
-
1398
- // 检查 Claude Code
1399
- if (!checkClaudeCode()) {
1400
- error('未检测到 Claude Code CLI');
1401
- info('请先安装 Claude Code: https://claude.ai/code');
1402
- process.exit(1);
1403
- }
1404
-
1405
- const packageDir = getPackageDir();
1406
- const pluginDir = getPluginDir();
1407
-
1408
- // 使用公共的回滚保护执行器
1409
- const installSuccess = executeWithRollback(pluginDir, (targetDir) => {
1410
- // 复制插件文件
1411
- info('正在安装插件文件...');
1412
- const stats = copyPluginFiles(packageDir, targetDir);
1413
-
1414
- // 检查是否有关键错误
1415
- const criticalErrors = stats.errors.filter(e => e.includes('.claude-plugin') || e.includes('agents'));
1416
- if (criticalErrors.length > 0) {
1417
- throw new Error(`关键目录复制失败: ${criticalErrors.join(', ')}`);
1418
- }
1419
-
1420
- if (stats.errors.length > 0) {
1421
- warn('部分文件复制出现问题:');
1422
- stats.errors.forEach(e => console.log(` - ${e}`));
1423
- }
1424
-
1425
- // 设置权限
1426
- setHookPermissions(targetDir);
1427
-
1428
- success(`插件文件安装完成 (${stats.dirs} 目录, ${stats.files} 文件)`);
1429
- info(`安装位置: ${targetDir}`);
1430
- }, '安装');
1431
-
1432
- if (!installSuccess) {
1433
- process.exit(1);
1434
- }
1435
-
1436
- // 安装 commands 到 ~/.claude/commands/
1437
- installCommands(packageDir);
1438
-
1439
- // 安装 skills 到 ~/.claude/skills/
1440
- installSkills(packageDir);
1441
-
1442
- // 注册核心 Agent
1443
- await registerCoreAgents();
1444
-
1445
- // 验证安装
1446
- verifyInstallation();
1447
-
1448
- // 显示完成信息
1449
- log('\n🎉 安装完成!', 'green');
1450
- log('━'.repeat(DIVIDER_LENGTH), 'green');
1451
- log('');
1452
- warn('⚠️ 重要:请完全退出并重新启动 Claude Code 以加载新命令');
1453
- warn(' (仅关闭窗口可能不够,需要完全退出应用)');
1454
- if (os.platform() === 'darwin') {
1455
- warn(' macOS: 使用 Cmd+Q 完全退出应用');
1456
- warn(' 如果命令仍未加载,尝试: rm ~/.claude.json && 重启 Claude Code');
1457
- }
1458
- log('');
1459
- log('快速开始:', 'cyan');
1460
- info(' /yishan - 愚公移山模式(大规模任务)');
1461
- info(' /zhuge - 诸葛顾问(架构设计)');
1462
- info(' /bianque - 扁鹊诊断(调试问题)');
1463
- info(' /luban - 鲁班巧工(代码实现)');
1464
- info(' /wukong - 悟空侦察(代码搜索)');
1465
- log('');
1466
- log('安装位置:', 'cyan');
1467
- info(` Commands: ${getCommandsDir()}`);
1468
- info(` Skills: ${getSkillsDir()}`);
1469
- info(` Plugin: ${pluginDir}`);
1470
- log('');
1471
- log('故障排除:', 'cyan');
1472
- info(' 如果命令未出现在 /help 中:');
1473
- info(' 1. 确保完全退出 Claude Code(不只是关闭窗口)');
1474
- info(' 2. 检查文件是否存在: ls ~/.claude/commands/');
1475
- info(' 3. 清除缓存: rm ~/.claude.json && 重启');
1476
- log('');
1110
+ async function install() {
1111
+ log('\n🏔️ oh-my-claude 安装程序', 'cyan');
1112
+ log('━'.repeat(DIVIDER_LENGTH), 'cyan');
1113
+ // 检查 Claude Code
1114
+ if (!checkClaudeCode()) {
1115
+ error('未检测到 Claude Code CLI');
1116
+ info('请先安装 Claude Code: https://claude.ai/code');
1117
+ process.exit(1);
1118
+ }
1119
+ const packageDir = getPackageDir();
1120
+ const pluginDir = getPluginDir();
1121
+ // 使用公共的回滚保护执行器
1122
+ const installSuccess = executeWithRollback(pluginDir, (targetDir) => {
1123
+ // 复制插件文件
1124
+ info('正在安装插件文件...');
1125
+ const stats = copyPluginFiles(packageDir, targetDir);
1126
+ // 检查是否有关键错误
1127
+ const criticalErrors = stats.errors.filter(e => e.includes('.claude-plugin') || e.includes('agents'));
1128
+ if (criticalErrors.length > 0) {
1129
+ throw new Error(`关键目录复制失败: ${criticalErrors.join(', ')}`);
1130
+ }
1131
+ if (stats.errors.length > 0) {
1132
+ warn('部分文件复制出现问题:');
1133
+ stats.errors.forEach(e => console.log(` - ${e}`));
1134
+ }
1135
+ // 设置权限
1136
+ setHookPermissions(targetDir);
1137
+ success(`插件文件安装完成 (${stats.dirs} 目录, ${stats.files} 文件)`);
1138
+ info(`安装位置: ${targetDir}`);
1139
+ }, '安装');
1140
+ if (!installSuccess) {
1141
+ process.exit(1);
1142
+ }
1143
+ // 安装 commands 到 ~/.claude/commands/
1144
+ installCommands(packageDir);
1145
+ // 安装 skills 到 ~/.claude/skills/
1146
+ installSkills(packageDir);
1147
+ // 注册核心 Agent
1148
+ await registerCoreAgents();
1149
+ // 验证安装
1150
+ verifyInstallation();
1151
+ // 显示完成信息
1152
+ log('\n🎉 安装完成!', 'green');
1153
+ log('━'.repeat(DIVIDER_LENGTH), 'green');
1154
+ log('');
1155
+ warn('⚠️ 重要:请完全退出并重新启动 Claude Code 以加载新命令');
1156
+ warn(' (仅关闭窗口可能不够,需要完全退出应用)');
1157
+ if (os.platform() === 'darwin') {
1158
+ warn(' macOS: 使用 Cmd+Q 完全退出应用');
1159
+ warn(' 如果命令仍未加载,尝试: rm ~/.claude.json && 重启 Claude Code');
1160
+ }
1161
+ log('');
1162
+ log('快速开始:', 'cyan');
1163
+ info(' /yishan - 愚公移山模式(大规模任务)');
1164
+ info(' /zhuge - 诸葛顾问(架构设计)');
1165
+ info(' /bianque - 扁鹊诊断(调试问题)');
1166
+ info(' /luban - 鲁班巧工(代码实现)');
1167
+ info(' /wukong - 悟空侦察(代码搜索)');
1168
+ log('');
1169
+ log('安装位置:', 'cyan');
1170
+ info(` Commands: ${getCommandsDir()}`);
1171
+ info(` Skills: ${getSkillsDir()}`);
1172
+ info(` Plugin: ${pluginDir}`);
1173
+ log('');
1174
+ log('故障排除:', 'cyan');
1175
+ info(' 如果命令未出现在 /help 中:');
1176
+ info(' 1. 确保完全退出 Claude Code(不只是关闭窗口)');
1177
+ info(' 2. 检查文件是否存在: ls ~/.claude/commands/');
1178
+ info(' 3. 清除缓存: rm ~/.claude.json && 重启');
1179
+ log('');
1477
1180
  }
1478
-
1479
1181
  /**
1480
1182
  * 卸载插件(带确认提示)
1481
1183
  */
1482
- function uninstall(): void {
1483
- log('\n🏔️ oh-my-claude 卸载程序', 'cyan');
1484
- log('━'.repeat(DIVIDER_LENGTH), 'cyan');
1485
-
1486
- const pluginDir = getPluginDir();
1487
-
1488
- if (!fs.existsSync(pluginDir)) {
1489
- warn('插件未安装');
1490
- process.exit(0);
1491
- }
1492
-
1493
- // 检查是否有 -y/--yes 参数跳过确认
1494
- const skipConfirm = process.argv.includes('-y') || process.argv.includes('--yes');
1495
-
1496
- if (skipConfirm) {
1497
- // 跳过确认,直接卸载
1498
- performUninstall(pluginDir);
1499
- return;
1500
- }
1501
-
1502
- // 显示确认提示
1503
- warn('即将卸载插件,所有配置将被删除');
1504
- info(`插件位置: ${pluginDir}`);
1505
- log('');
1506
-
1507
- const readline = require('readline');
1508
- const rl = readline.createInterface({
1509
- input: process.stdin,
1510
- output: process.stdout
1511
- });
1512
-
1513
- rl.question('确认卸载?(y/N): ', (answer) => {
1514
- rl.close();
1515
- if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
1516
- performUninstall(pluginDir);
1517
- } else {
1518
- info('已取消卸载');
1519
- process.exit(0);
1520
- }
1521
- });
1184
+ function uninstall() {
1185
+ log('\n🏔️ oh-my-claude 卸载程序', 'cyan');
1186
+ log('━'.repeat(DIVIDER_LENGTH), 'cyan');
1187
+ const pluginDir = getPluginDir();
1188
+ if (!fs.existsSync(pluginDir)) {
1189
+ warn('插件未安装');
1190
+ process.exit(0);
1191
+ }
1192
+ // 检查是否有 -y/--yes 参数跳过确认
1193
+ const skipConfirm = process.argv.includes('-y') || process.argv.includes('--yes');
1194
+ if (skipConfirm) {
1195
+ // 跳过确认,直接卸载
1196
+ performUninstall(pluginDir);
1197
+ return;
1198
+ }
1199
+ // 显示确认提示
1200
+ warn('即将卸载插件,所有配置将被删除');
1201
+ info(`插件位置: ${pluginDir}`);
1202
+ log('');
1203
+ const readline = require('readline');
1204
+ const rl = readline.createInterface({
1205
+ input: process.stdin,
1206
+ output: process.stdout
1207
+ });
1208
+ rl.question('确认卸载?(y/N): ', (answer) => {
1209
+ rl.close();
1210
+ if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
1211
+ performUninstall(pluginDir);
1212
+ }
1213
+ else {
1214
+ info('已取消卸载');
1215
+ process.exit(0);
1216
+ }
1217
+ });
1522
1218
  }
1523
-
1524
1219
  /**
1525
1220
  * 更新插件(使用公共函数,带回滚)
1526
1221
  */
1527
- async function update(): Promise<void> {
1528
- log('\n🏔️ oh-my-claude 更新程序', 'cyan');
1529
- log('━'.repeat(DIVIDER_LENGTH), 'cyan');
1530
-
1531
- const pluginDir = getPluginDir();
1532
-
1533
- if (!fs.existsSync(pluginDir)) {
1534
- warn('插件未安装,将执行全新安装');
1535
- await install();
1536
- return;
1537
- }
1538
-
1539
- info('正在检查更新...');
1540
-
1541
- // 获取当前安装的版本
1542
- const pluginJsonPath = path.join(pluginDir, '.claude-plugin', 'plugin.json');
1543
- let installedVersion = 'unknown';
1544
- if (fs.existsSync(pluginJsonPath)) {
1545
- try {
1546
- const pluginJson = JSON.parse(fs.readFileSync(pluginJsonPath, 'utf8'));
1547
- installedVersion = pluginJson.version || 'unknown';
1548
- } catch {
1549
- // 忽略解析错误
1550
- }
1551
- }
1552
-
1553
- info(`当前安装版本: ${installedVersion}`);
1554
- info(`包版本: ${VERSION}`);
1555
-
1556
- if (installedVersion === VERSION) {
1557
- success('已是最新版本');
1558
- return;
1559
- }
1560
-
1561
- const packageDir = getPackageDir();
1562
-
1563
- // 使用公共的回滚保护执行器
1564
- const updateSuccess = executeWithRollback(pluginDir, (targetDir) => {
1565
- // 复制新文件
1566
- info('正在更新插件文件...');
1567
- const stats = copyPluginFiles(packageDir, targetDir);
1568
-
1569
- // 检查关键错误
1570
- const criticalErrors = stats.errors.filter(e => e.includes('.claude-plugin') || e.includes('agents'));
1571
- if (criticalErrors.length > 0) {
1572
- throw new Error(`关键目录复制失败: ${criticalErrors.join(', ')}`);
1573
- }
1574
-
1575
- // 设置权限
1576
- setHookPermissions(targetDir);
1577
-
1578
- success(`已更新到版本 ${VERSION}`);
1579
- }, '更新');
1580
-
1581
- if (!updateSuccess) {
1582
- process.exit(1);
1583
- }
1584
-
1585
- // 更新 commands ~/.claude/commands/
1586
- installCommands(packageDir);
1587
-
1588
- // 更新 skills 到 ~/.claude/skills/
1589
- installSkills(packageDir);
1590
-
1591
- // 验证安装
1592
- verifyInstallation();
1593
-
1594
- // 显示完成信息
1595
- log('\n🎉 更新完成!', 'green');
1596
- log('━'.repeat(DIVIDER_LENGTH), 'green');
1597
- log('');
1598
- warn('⚠️ 重要:请完全退出并重新启动 Claude Code 以加载新命令');
1599
- warn(' (仅关闭窗口可能不够,需要完全退出应用)');
1600
- if (os.platform() === 'darwin') {
1601
- warn(' macOS: 使用 Cmd+Q 完全退出应用');
1602
- warn(' 如果命令仍未加载,尝试: rm ~/.claude.json && 重启 Claude Code');
1603
- }
1604
- log('');
1605
-
1606
- // 如果清理了旧版本,显示命令格式变化提示
1607
- // 这里需要获取 installCommands 的返回值来检查是否清理了旧版本
1608
- // 暂时注释掉这个逻辑
1609
- // if (cmdStats.cleaned) {
1610
- // log('📝 命令格式变更提示:', 'yellow');
1611
- // info(' 旧格式: /zcf:yishan, /zcf:zhuge, /zcf:bianque ...');
1612
- // info(' 新格式: /yishan, /zhuge, /bianque ...');
1613
- // log('');
1614
- // }
1615
-
1616
- log('故障排除:', 'cyan');
1617
- info(' 如果命令未出现在 /help 中:');
1618
- info(' 1. 确保完全退出 Claude Code(不只是关闭窗口)');
1619
- info(' 2. 检查文件是否存在: ls ~/.claude/commands/');
1620
- info(' 3. 清除缓存: rm ~/.claude.json && 重启');
1621
- log('');
1222
+ async function update() {
1223
+ log('\n🏔️ oh-my-claude 更新程序', 'cyan');
1224
+ log('━'.repeat(DIVIDER_LENGTH), 'cyan');
1225
+ const pluginDir = getPluginDir();
1226
+ if (!fs.existsSync(pluginDir)) {
1227
+ warn('插件未安装,将执行全新安装');
1228
+ await install();
1229
+ return;
1230
+ }
1231
+ info('正在检查更新...');
1232
+ // 获取当前安装的版本
1233
+ const pluginJsonPath = path.join(pluginDir, '.claude-plugin', 'plugin.json');
1234
+ let installedVersion = 'unknown';
1235
+ if (fs.existsSync(pluginJsonPath)) {
1236
+ try {
1237
+ const pluginJson = JSON.parse(fs.readFileSync(pluginJsonPath, 'utf8'));
1238
+ installedVersion = pluginJson.version || 'unknown';
1239
+ }
1240
+ catch {
1241
+ // 忽略解析错误
1242
+ }
1243
+ }
1244
+ info(`当前安装版本: ${installedVersion}`);
1245
+ info(`包版本: ${VERSION}`);
1246
+ if (installedVersion === VERSION) {
1247
+ success('已是最新版本');
1248
+ return;
1249
+ }
1250
+ const packageDir = getPackageDir();
1251
+ // 使用公共的回滚保护执行器
1252
+ const updateSuccess = executeWithRollback(pluginDir, (targetDir) => {
1253
+ // 复制新文件
1254
+ info('正在更新插件文件...');
1255
+ const stats = copyPluginFiles(packageDir, targetDir);
1256
+ // 检查关键错误
1257
+ const criticalErrors = stats.errors.filter(e => e.includes('.claude-plugin') || e.includes('agents'));
1258
+ if (criticalErrors.length > 0) {
1259
+ throw new Error(`关键目录复制失败: ${criticalErrors.join(', ')}`);
1260
+ }
1261
+ // 设置权限
1262
+ setHookPermissions(targetDir);
1263
+ success(`已更新到版本 ${VERSION}`);
1264
+ }, '更新');
1265
+ if (!updateSuccess) {
1266
+ process.exit(1);
1267
+ }
1268
+ // 更新 commands 到 ~/.claude/commands/
1269
+ installCommands(packageDir);
1270
+ // 更新 skills 到 ~/.claude/skills/
1271
+ installSkills(packageDir);
1272
+ // 验证安装
1273
+ verifyInstallation();
1274
+ // 显示完成信息
1275
+ log('\n🎉 更新完成!', 'green');
1276
+ log('━'.repeat(DIVIDER_LENGTH), 'green');
1277
+ log('');
1278
+ warn('⚠️ 重要:请完全退出并重新启动 Claude Code 以加载新命令');
1279
+ warn(' (仅关闭窗口可能不够,需要完全退出应用)');
1280
+ if (os.platform() === 'darwin') {
1281
+ warn(' macOS: 使用 Cmd+Q 完全退出应用');
1282
+ warn(' 如果命令仍未加载,尝试: rm ~/.claude.json && 重启 Claude Code');
1283
+ }
1284
+ log('');
1285
+ // 如果清理了旧版本,显示命令格式变化提示
1286
+ // 这里需要获取 installCommands 的返回值来检查是否清理了旧版本
1287
+ // 暂时注释掉这个逻辑
1288
+ // if (cmdStats.cleaned) {
1289
+ // log('📝 命令格式变更提示:', 'yellow');
1290
+ // info(' 旧格式: /zcf:yishan, /zcf:zhuge, /zcf:bianque ...');
1291
+ // info(' 新格式: /yishan, /zhuge, /bianque ...');
1292
+ // log('');
1293
+ // }
1294
+ log('故障排除:', 'cyan');
1295
+ info(' 如果命令未出现在 /help 中:');
1296
+ info(' 1. 确保完全退出 Claude Code(不只是关闭窗口)');
1297
+ info(' 2. 检查文件是否存在: ls ~/.claude/commands/');
1298
+ info(' 3. 清除缓存: rm ~/.claude.json && 重启');
1299
+ log('');
1622
1300
  }
1623
-
1624
1301
  /**
1625
1302
  * 显示配置信息
1626
1303
  */
1627
- function showConfig(): void {
1628
- log('\n🏔️ oh-my-claude 配置信息', 'cyan');
1629
- log('━'.repeat(DIVIDER_LENGTH), 'cyan');
1630
-
1631
- const config = configManager.getConfig();
1632
-
1633
- console.log('\n📋 当前配置:');
1634
- console.log(`版本: ${config.version}`);
1635
- console.log(`调试模式: ${config.debug ? '开启' : '关闭'}`);
1636
- console.log(`日志级别: ${config.logLevel}`);
1637
-
1638
- console.log('\n🤖 Agent 配置:');
1639
- console.log(`默认超时: ${config.agents.defaultTimeout}ms`);
1640
- console.log(`最大并发任务: ${config.agents.maxConcurrentTasks}`);
1641
- console.log(`启用协作: ${config.agents.enableCollaboration ? '是' : '否'}`);
1642
- console.log(`上下文压缩: ${config.agents.contextCompression ? '是' : '否'}`);
1643
-
1644
- console.log('\n🎨 UI 配置:');
1645
- console.log(`主题: ${config.ui.theme}`);
1646
- console.log(`语言: ${config.ui.language}`);
1647
- console.log(`显示进度: ${config.ui.showProgress ? '是' : '否'}`);
1648
- console.log(`启用通知: ${config.ui.enableNotifications ? '是' : '否'}`);
1649
-
1650
- console.log('\n 性能配置:');
1651
- console.log(`启用缓存: ${config.performance.enableCache ? '是' : '否'}`);
1652
- console.log(`缓存大小: ${config.performance.cacheSize}`);
1653
- console.log(`最大内存: ${config.performance.maxMemoryUsage}MB`);
1654
- console.log(`启用指标: ${config.performance.enableMetrics ? '是' : '否'}`);
1655
-
1656
- console.log('\n🌐 网络配置:');
1657
- console.log(`超时时间: ${config.network.timeout}ms`);
1658
- console.log(`重试次数: ${config.network.retries}`);
1659
- if (config.network.proxy) {
1660
- console.log(`代理服务器: ${config.network.proxy}`);
1661
- }
1662
-
1663
- console.log('\n🔌 插件配置:');
1664
- console.log(`自动更新: ${config.plugins.autoUpdate ? '是' : ''}`);
1665
- console.log(`允许第三方: ${config.plugins.enableThirdParty ? '是' : '否'}`);
1666
- console.log(`可信域名: ${config.plugins.trustedDomains.join(', ')}`);
1667
-
1668
- console.log('\n⚙️ 高级配置:');
1669
- console.log(`启用性能分析: ${config.advanced.enableProfiling ? '是' : '否'}`);
1670
- console.log(`启用追踪: ${config.advanced.enableTracing ? '是' : '否'}`);
1671
- console.log(`自定义钩子: ${Object.keys(config.advanced.customHooks).length} 个`);
1672
-
1673
- log('\n📁 配置文件位置:', 'cyan');
1674
- info(`全局配置: ~/.oh-my-claude/config/global.json`);
1675
- info(`用户配置: ~/.oh-my-claude/config.json`);
1676
- info(`项目配置: ./.oh-my-claude.json 或 ./oh-my-claude.config.json`);
1677
-
1678
- log('\n💡 配置提示:', 'cyan');
1679
- info('使用环境变量覆盖: OH_MY_CLAUDE_DEBUG=true');
1680
- info('使用 oh-my-claude config 查看此信息');
1681
- info('配置文件修改后自动热重载 (如果启用追踪)');
1304
+ function showConfig() {
1305
+ log('\n🏔️ oh-my-claude 配置信息', 'cyan');
1306
+ log('━'.repeat(DIVIDER_LENGTH), 'cyan');
1307
+ const config = configManager.getConfig();
1308
+ console.log('\n📋 当前配置:');
1309
+ console.log(`版本: ${config.version}`);
1310
+ console.log(`调试模式: ${config.debug ? '开启' : '关闭'}`);
1311
+ console.log(`日志级别: ${config.logLevel}`);
1312
+ console.log('\n🤖 Agent 配置:');
1313
+ console.log(`默认超时: ${config.agents.defaultTimeout}ms`);
1314
+ console.log(`最大并发任务: ${config.agents.maxConcurrentTasks}`);
1315
+ console.log(`启用协作: ${config.agents.enableCollaboration ? '是' : '否'}`);
1316
+ console.log(`上下文压缩: ${config.agents.contextCompression ? '是' : '否'}`);
1317
+ console.log('\n🎨 UI 配置:');
1318
+ console.log(`主题: ${config.ui.theme}`);
1319
+ console.log(`语言: ${config.ui.language}`);
1320
+ console.log(`显示进度: ${config.ui.showProgress ? '是' : '否'}`);
1321
+ console.log(`启用通知: ${config.ui.enableNotifications ? '是' : '否'}`);
1322
+ console.log('\n⚡ 性能配置:');
1323
+ console.log(`启用缓存: ${config.performance.enableCache ? '是' : '否'}`);
1324
+ console.log(`缓存大小: ${config.performance.cacheSize}`);
1325
+ console.log(`最大内存: ${config.performance.maxMemoryUsage}MB`);
1326
+ console.log(`启用指标: ${config.performance.enableMetrics ? '是' : '否'}`);
1327
+ console.log('\n🌐 网络配置:');
1328
+ console.log(`超时时间: ${config.network.timeout}ms`);
1329
+ console.log(`重试次数: ${config.network.retries}`);
1330
+ if (config.network.proxy) {
1331
+ console.log(`代理服务器: ${config.network.proxy}`);
1332
+ }
1333
+ console.log('\n🔌 插件配置:');
1334
+ console.log(`自动更新: ${config.plugins.autoUpdate ? '是' : '否'}`);
1335
+ console.log(`允许第三方: ${config.plugins.enableThirdParty ? '是' : '否'}`);
1336
+ console.log(`可信域名: ${config.plugins.trustedDomains.join(', ')}`);
1337
+ console.log('\n⚙️ 高级配置:');
1338
+ console.log(`启用性能分析: ${config.advanced.enableProfiling ? '是' : '否'}`);
1339
+ console.log(`启用追踪: ${config.advanced.enableTracing ? '是' : '否'}`);
1340
+ console.log(`自定义钩子: ${Object.keys(config.advanced.customHooks).length} 个`);
1341
+ log('\n📁 配置文件位置:', 'cyan');
1342
+ info(`全局配置: ~/.oh-my-claude/config/global.json`);
1343
+ info(`用户配置: ~/.oh-my-claude/config.json`);
1344
+ info(`项目配置: ./.oh-my-claude.json 或 ./oh-my-claude.config.json`);
1345
+ log('\n💡 配置提示:', 'cyan');
1346
+ info('使用环境变量覆盖: OH_MY_CLAUDE_DEBUG=true');
1347
+ info('使用 oh-my-claude config 查看此信息');
1348
+ info('配置文件修改后自动热重载 (如果启用追踪)');
1682
1349
  }
1683
-
1684
1350
  /**
1685
1351
  * 获取配置值
1686
1352
  */
1687
- function getConfigValue(key: string): void {
1688
- try {
1689
- // 支持点号分隔的嵌套键,如 "agents.defaultTimeout"
1690
- const keys = key.split('.');
1691
- let value: any = configManager.getConfig();
1692
-
1693
- for (const k of keys) {
1694
- if (value && typeof value === 'object' && k in value) {
1695
- value = value[k];
1696
- } else {
1697
- throw new Error(`配置键不存在: ${key}`);
1698
- }
1699
- }
1700
-
1701
- console.log(`${key}: ${JSON.stringify(value, null, 2)}`);
1702
- } catch (err) {
1703
- const errorMessage = err instanceof Error ? err.message : String(err);
1704
- error(`获取配置失败: ${errorMessage}`);
1705
- process.exit(1);
1706
- }
1353
+ function getConfigValue(key) {
1354
+ try {
1355
+ // 支持点号分隔的嵌套键,如 "agents.defaultTimeout"
1356
+ const keys = key.split('.');
1357
+ let value = configManager.getConfig();
1358
+ for (const k of keys) {
1359
+ if (value && typeof value === 'object' && k in value) {
1360
+ value = value[k];
1361
+ }
1362
+ else {
1363
+ throw new Error(`配置键不存在: ${key}`);
1364
+ }
1365
+ }
1366
+ console.log(`${key}: ${JSON.stringify(value, null, 2)}`);
1367
+ }
1368
+ catch (err) {
1369
+ const errorMessage = err instanceof Error ? err.message : String(err);
1370
+ error(`获取配置失败: ${errorMessage}`);
1371
+ process.exit(1);
1372
+ }
1707
1373
  }
1708
-
1709
1374
  /**
1710
1375
  * 设置配置值
1711
1376
  */
1712
- function setConfigValue(key: string, valueStr: string): void {
1713
- try {
1714
- // 解析值
1715
- let value: any;
1716
- if (valueStr === 'true') value = true;
1717
- else if (valueStr === 'false') value = false;
1718
- else if (!isNaN(Number(valueStr))) value = Number(valueStr);
1719
- else if (valueStr.startsWith('[') && valueStr.endsWith(']')) {
1720
- value = JSON.parse(valueStr);
1721
- } else {
1722
- value = valueStr;
1723
- }
1724
-
1725
- // 支持点号分隔的嵌套设置
1726
- const keys = key.split('.');
1727
- if (keys.length === 1) {
1728
- // 顶级配置键
1729
- configManager.set(key as keyof OhMyClaudeConfig, value);
1730
- } else {
1731
- // 嵌套配置键
1732
- const topKey = keys[0];
1733
- const subKey = keys.slice(1).join('.');
1734
- const currentValue = configManager.get(topKey as keyof OhMyClaudeConfig);
1735
-
1736
- // 深度设置嵌套值
1737
- const updatedValue = setNestedValue(currentValue, keys.slice(1), value);
1738
- configManager.set(topKey as keyof OhMyClaudeConfig, updatedValue);
1739
- }
1740
-
1741
- console.log(`✅ 配置已更新: ${key} = ${JSON.stringify(value)}`);
1742
- } catch (err) {
1743
- const errorMessage = err instanceof Error ? err.message : String(err);
1744
- error(`设置配置失败: ${errorMessage}`);
1745
- process.exit(1);
1746
- }
1377
+ function setConfigValue(key, valueStr) {
1378
+ try {
1379
+ // 解析值
1380
+ let value;
1381
+ if (valueStr === 'true')
1382
+ value = true;
1383
+ else if (valueStr === 'false')
1384
+ value = false;
1385
+ else if (!isNaN(Number(valueStr)))
1386
+ value = Number(valueStr);
1387
+ else if (valueStr.startsWith('[') && valueStr.endsWith(']')) {
1388
+ value = JSON.parse(valueStr);
1389
+ }
1390
+ else {
1391
+ value = valueStr;
1392
+ }
1393
+ // 支持点号分隔的嵌套设置
1394
+ const keys = key.split('.');
1395
+ if (keys.length === 1) {
1396
+ // 顶级配置键
1397
+ configManager.set(key, value);
1398
+ }
1399
+ else {
1400
+ // 嵌套配置键
1401
+ const topKey = keys[0];
1402
+ const subKey = keys.slice(1).join('.');
1403
+ const currentValue = configManager.get(topKey);
1404
+ // 深度设置嵌套值
1405
+ const updatedValue = setNestedValue(currentValue, keys.slice(1), value);
1406
+ configManager.set(topKey, updatedValue);
1407
+ }
1408
+ console.log(`✅ 配置已更新: ${key} = ${JSON.stringify(value)}`);
1409
+ }
1410
+ catch (err) {
1411
+ const errorMessage = err instanceof Error ? err.message : String(err);
1412
+ error(`设置配置失败: ${errorMessage}`);
1413
+ process.exit(1);
1414
+ }
1747
1415
  }
1748
-
1749
1416
  /**
1750
1417
  * 深度设置嵌套对象的值
1751
1418
  */
1752
- function setNestedValue(obj: any, keys: string[], value: any): any {
1753
- if (keys.length === 1) {
1754
- return { ...obj, [keys[0]]: value };
1755
- }
1756
-
1757
- const [currentKey, ...remainingKeys] = keys;
1758
- return {
1759
- ...obj,
1760
- [currentKey]: setNestedValue(obj[currentKey] || {}, remainingKeys, value)
1761
- };
1419
+ function setNestedValue(obj, keys, value) {
1420
+ if (keys.length === 1) {
1421
+ return { ...obj, [keys[0]]: value };
1422
+ }
1423
+ const [currentKey, ...remainingKeys] = keys;
1424
+ return {
1425
+ ...obj,
1426
+ [currentKey]: setNestedValue(obj[currentKey] || {}, remainingKeys, value)
1427
+ };
1762
1428
  }
1763
-
1764
1429
  /**
1765
1430
  * 保存配置到文件
1766
1431
  */
1767
- async function saveConfigToFile(filePath?: string): Promise<void> {
1768
- try {
1769
- await configManager.saveConfig(filePath);
1770
- console.log(`✅ 配置已保存${filePath ? `到 ${filePath}` : '到用户配置文件'}`);
1771
- } catch (err) {
1772
- const errorMessage = err instanceof Error ? err.message : String(err);
1773
- error(`保存配置失败: ${errorMessage}`);
1774
- process.exit(1);
1775
- }
1432
+ async function saveConfigToFile(filePath) {
1433
+ try {
1434
+ await configManager.saveConfig(filePath);
1435
+ console.log(`✅ 配置已保存${filePath ? `到 ${filePath}` : '到用户配置文件'}`);
1436
+ }
1437
+ catch (err) {
1438
+ const errorMessage = err instanceof Error ? err.message : String(err);
1439
+ error(`保存配置失败: ${errorMessage}`);
1440
+ process.exit(1);
1441
+ }
1776
1442
  }
1777
-
1778
1443
  /**
1779
1444
  * 处理配置命令
1780
1445
  */
1781
- async function handleConfigCommand(args: string[]): Promise<void> {
1782
- if (args.length === 0) {
1783
- // 默认显示配置
1784
- showConfig();
1785
- return;
1786
- }
1787
-
1788
- const subcommand = args[0];
1789
-
1790
- switch (subcommand) {
1791
- case 'show':
1792
- showConfig();
1793
- break;
1794
-
1795
- case 'get':
1796
- if (args.length < 2) {
1797
- error('用法: oh-my-claude config get <key>');
1798
- error('示例: oh-my-claude config get debug');
1799
- error(' oh-my-claude config get agents.defaultTimeout');
1800
- process.exit(1);
1801
- }
1802
- getConfigValue(args[1]);
1803
- break;
1804
-
1805
- case 'set':
1806
- if (args.length < 3) {
1807
- error('用法: oh-my-claude config set <key> <value>');
1808
- error('示例: oh-my-claude config set debug true');
1809
- error(' oh-my-claude config set agents.defaultTimeout 60000');
1810
- error(' oh-my-claude config set ui.theme dark');
1811
- process.exit(1);
1812
- }
1813
- setConfigValue(args[1], args[2]);
1814
- break;
1815
-
1816
- case 'save':
1817
- const filePath = args.length > 1 ? args[1] : undefined;
1818
- await saveConfigToFile(filePath);
1819
- break;
1820
-
1821
- case 'reset':
1822
- configManager.resetToDefaults();
1823
- await configManager.saveConfig();
1824
- console.log(' 配置已重置为默认值');
1825
- break;
1826
-
1827
- case 'help':
1828
- default:
1829
- console.log('\n🏔️ oh-my-claude 配置管理命令', 'cyan');
1830
- console.log(''.repeat(DIVIDER_LENGTH), 'cyan');
1831
- console.log('\n用法: oh-my-claude config <subcommand> [参数...]');
1832
- console.log('\n子命令:');
1833
- console.log(' show 显示当前配置');
1834
- console.log(' get <key> 获取配置值');
1835
- console.log(' set <key> <value> 设置配置值');
1836
- console.log(' save [file] 保存配置到文件');
1837
- console.log(' reset 重置为默认配置');
1838
- console.log(' help 显示此帮助信息');
1839
- console.log('\n示例:');
1840
- console.log(' oh-my-claude config show');
1841
- console.log(' oh-my-claude config get debug');
1842
- console.log(' oh-my-claude config set debug true');
1843
- console.log(' oh-my-claude config set agents.defaultTimeout 60000');
1844
- console.log(' oh-my-claude config save');
1845
- console.log(' oh-my-claude config save ~/.oh-my-claude/my-config.json');
1846
- console.log('\n配置键支持点号分隔的嵌套访问:');
1847
- console.log(' agents.defaultTimeout, ui.theme, performance.cacheSize 等');
1848
- break;
1849
- }
1446
+ async function handleConfigCommand(args) {
1447
+ if (args.length === 0) {
1448
+ // 默认显示配置
1449
+ showConfig();
1450
+ return;
1451
+ }
1452
+ const subcommand = args[0];
1453
+ switch (subcommand) {
1454
+ case 'show':
1455
+ showConfig();
1456
+ break;
1457
+ case 'get':
1458
+ if (args.length < 2) {
1459
+ error('用法: oh-my-claude config get <key>');
1460
+ error('示例: oh-my-claude config get debug');
1461
+ error(' oh-my-claude config get agents.defaultTimeout');
1462
+ process.exit(1);
1463
+ }
1464
+ getConfigValue(args[1]);
1465
+ break;
1466
+ case 'set':
1467
+ if (args.length < 3) {
1468
+ error('用法: oh-my-claude config set <key> <value>');
1469
+ error('示例: oh-my-claude config set debug true');
1470
+ error(' oh-my-claude config set agents.defaultTimeout 60000');
1471
+ error(' oh-my-claude config set ui.theme dark');
1472
+ process.exit(1);
1473
+ }
1474
+ setConfigValue(args[1], args[2]);
1475
+ break;
1476
+ case 'save':
1477
+ const filePath = args.length > 1 ? args[1] : undefined;
1478
+ await saveConfigToFile(filePath);
1479
+ break;
1480
+ case 'reset':
1481
+ configManager.resetToDefaults();
1482
+ await configManager.saveConfig();
1483
+ console.log('✅ 配置已重置为默认值');
1484
+ break;
1485
+ case 'help':
1486
+ default:
1487
+ console.log('\n🏔️ oh-my-claude 配置管理命令', 'cyan');
1488
+ console.log('━'.repeat(DIVIDER_LENGTH), 'cyan');
1489
+ console.log('\n用法: oh-my-claude config <subcommand> [参数...]');
1490
+ console.log('\n子命令:');
1491
+ console.log(' show 显示当前配置');
1492
+ console.log(' get <key> 获取配置值');
1493
+ console.log(' set <key> <value> 设置配置值');
1494
+ console.log(' save [file] 保存配置到文件');
1495
+ console.log(' reset 重置为默认配置');
1496
+ console.log(' help 显示此帮助信息');
1497
+ console.log('\n示例:');
1498
+ console.log(' oh-my-claude config show');
1499
+ console.log(' oh-my-claude config get debug');
1500
+ console.log(' oh-my-claude config set debug true');
1501
+ console.log(' oh-my-claude config set agents.defaultTimeout 60000');
1502
+ console.log(' oh-my-claude config save');
1503
+ console.log(' oh-my-claude config save ~/.oh-my-claude/my-config.json');
1504
+ console.log('\n配置键支持点号分隔的嵌套访问:');
1505
+ console.log(' agents.defaultTimeout, ui.theme, performance.cacheSize 等');
1506
+ break;
1507
+ }
1850
1508
  }
1851
-
1852
1509
  /**
1853
1510
  * 验证安装
1854
1511
  */
1855
- function verify(): void {
1856
- log('\n🏔️ oh-my-claude 安装验证', 'cyan');
1857
- log('━'.repeat(DIVIDER_LENGTH), 'cyan');
1858
-
1859
- const pluginDir = getPluginDir();
1860
- let hasErrors = false;
1861
-
1862
- // 检查插件目录
1863
- info('检查插件目录...');
1864
- if (!fs.existsSync(pluginDir)) {
1865
- error(`插件目录不存在: ${pluginDir}`);
1866
- log('\n请运行 oh-my-claude install 安装插件\n');
1867
- process.exit(1);
1868
- }
1869
- success(`插件目录存在: ${pluginDir}`);
1870
-
1871
- // 检查必需目录
1872
- const requiredDirs = ['agents', 'commands', 'skills', '.claude-plugin'];
1873
- const optionalDirs = ['hooks'];
1874
-
1875
- info('\n检查目录结构...');
1876
- for (const dir of requiredDirs) {
1877
- const dirPath = path.join(pluginDir, dir);
1878
- if (fs.existsSync(dirPath)) {
1879
- const files = fs.readdirSync(dirPath);
1880
- success(`${dir}/ (${files.length} 个文件)`);
1881
- } else {
1882
- error(`缺少必需目录: ${dir}/`);
1883
- hasErrors = true;
1884
- }
1885
- }
1886
-
1887
- for (const dir of optionalDirs) {
1888
- const dirPath = path.join(pluginDir, dir);
1889
- if (fs.existsSync(dirPath)) {
1890
- const files = fs.readdirSync(dirPath);
1891
- success(`${dir}/ (${files.length} 个文件)`);
1892
- } else {
1893
- warn(`可选目录不存在: ${dir}/`);
1894
- }
1895
- }
1896
-
1897
- // 检查 plugin.json
1898
- info('\n检查插件清单...');
1899
- const pluginJsonPath = path.join(pluginDir, '.claude-plugin', 'plugin.json');
1900
- if (fs.existsSync(pluginJsonPath)) {
1901
- try {
1902
- const pluginJson = JSON.parse(fs.readFileSync(pluginJsonPath, 'utf8'));
1903
- success(`插件名称: ${pluginJson.name || 'N/A'}`);
1904
- success(`插件版本: ${pluginJson.version || 'N/A'}`);
1905
- } catch (e) {
1906
- error(`plugin.json 解析失败: ${e instanceof Error ? e.message : 'Unknown error'}`);
1907
- hasErrors = true;
1908
- }
1909
- } else {
1910
- error('plugin.json 不存在');
1911
- hasErrors = true;
1912
- }
1913
-
1914
- // 检查关键 Agent 文件
1915
- info('\n检查核心 Agent...');
1916
- const coreAgents = ['yugong.md', 'zhuge.md', 'baozheng.md'];
1917
- for (const agent of coreAgents) {
1918
- const agentPath = path.join(pluginDir, 'agents', agent);
1919
- if (fs.existsSync(agentPath)) {
1920
- success(`agents/${agent}`);
1921
- } else {
1922
- error(`缺少核心 Agent: ${agent}`);
1923
- hasErrors = true;
1924
- }
1925
- }
1926
-
1927
- // 检查 Claude Code
1928
- info('\n检查 Claude Code CLI...');
1929
- if (checkClaudeCode()) {
1930
- success('Claude Code CLI 可用');
1931
- } else {
1932
- error('Claude Code CLI 不可用');
1933
- hasErrors = true;
1934
- }
1935
-
1936
- // 总结
1937
- log('\n' + '━'.repeat(DIVIDER_LENGTH), 'cyan');
1938
- if (hasErrors) {
1939
- error('验证发现问题,请检查上述错误');
1940
- log('\n修复建议: oh-my-claude uninstall && oh-my-claude install\n');
1941
- process.exit(1);
1942
- } else {
1943
- success('所有检查通过!');
1944
- log('\n插件已正确安装,可以正常使用\n');
1945
- }
1512
+ function verify() {
1513
+ log('\n🏔️ oh-my-claude 安装验证', 'cyan');
1514
+ log('━'.repeat(DIVIDER_LENGTH), 'cyan');
1515
+ const pluginDir = getPluginDir();
1516
+ let hasErrors = false;
1517
+ // 检查插件目录
1518
+ info('检查插件目录...');
1519
+ if (!fs.existsSync(pluginDir)) {
1520
+ error(`插件目录不存在: ${pluginDir}`);
1521
+ log('\n请运行 oh-my-claude install 安装插件\n');
1522
+ process.exit(1);
1523
+ }
1524
+ success(`插件目录存在: ${pluginDir}`);
1525
+ // 检查必需目录
1526
+ const requiredDirs = ['agents', 'commands', 'skills', '.claude-plugin'];
1527
+ const optionalDirs = ['hooks'];
1528
+ info('\n检查目录结构...');
1529
+ for (const dir of requiredDirs) {
1530
+ const dirPath = path.join(pluginDir, dir);
1531
+ if (fs.existsSync(dirPath)) {
1532
+ const files = fs.readdirSync(dirPath);
1533
+ success(`${dir}/ (${files.length} 个文件)`);
1534
+ }
1535
+ else {
1536
+ error(`缺少必需目录: ${dir}/`);
1537
+ hasErrors = true;
1538
+ }
1539
+ }
1540
+ for (const dir of optionalDirs) {
1541
+ const dirPath = path.join(pluginDir, dir);
1542
+ if (fs.existsSync(dirPath)) {
1543
+ const files = fs.readdirSync(dirPath);
1544
+ success(`${dir}/ (${files.length} 个文件)`);
1545
+ }
1546
+ else {
1547
+ warn(`可选目录不存在: ${dir}/`);
1548
+ }
1549
+ }
1550
+ // 检查 plugin.json
1551
+ info('\n检查插件清单...');
1552
+ const pluginJsonPath = path.join(pluginDir, '.claude-plugin', 'plugin.json');
1553
+ if (fs.existsSync(pluginJsonPath)) {
1554
+ try {
1555
+ const pluginJson = JSON.parse(fs.readFileSync(pluginJsonPath, 'utf8'));
1556
+ success(`插件名称: ${pluginJson.name || 'N/A'}`);
1557
+ success(`插件版本: ${pluginJson.version || 'N/A'}`);
1558
+ }
1559
+ catch (e) {
1560
+ error(`plugin.json 解析失败: ${e instanceof Error ? e.message : 'Unknown error'}`);
1561
+ hasErrors = true;
1562
+ }
1563
+ }
1564
+ else {
1565
+ error('plugin.json 不存在');
1566
+ hasErrors = true;
1567
+ }
1568
+ // 检查关键 Agent 文件
1569
+ info('\n检查核心 Agent...');
1570
+ const coreAgents = ['yugong.md', 'zhuge.md', 'baozheng.md'];
1571
+ for (const agent of coreAgents) {
1572
+ const agentPath = path.join(pluginDir, 'agents', agent);
1573
+ if (fs.existsSync(agentPath)) {
1574
+ success(`agents/${agent}`);
1575
+ }
1576
+ else {
1577
+ error(`缺少核心 Agent: ${agent}`);
1578
+ hasErrors = true;
1579
+ }
1580
+ }
1581
+ // 检查 Claude Code
1582
+ info('\n检查 Claude Code CLI...');
1583
+ if (checkClaudeCode()) {
1584
+ success('Claude Code CLI 可用');
1585
+ }
1586
+ else {
1587
+ error('Claude Code CLI 不可用');
1588
+ hasErrors = true;
1589
+ }
1590
+ // 总结
1591
+ log('\n' + '━'.repeat(DIVIDER_LENGTH), 'cyan');
1592
+ if (hasErrors) {
1593
+ error('验证发现问题,请检查上述错误');
1594
+ log('\n修复建议: oh-my-claude uninstall && oh-my-claude install\n');
1595
+ process.exit(1);
1596
+ }
1597
+ else {
1598
+ success('所有检查通过!');
1599
+ log('\n插件已正确安装,可以正常使用\n');
1600
+ }
1946
1601
  }
1947
-
1948
1602
  // ==================== 主入口 ====================
1949
-
1950
1603
  // 仅在直接运行时执行
1951
1604
  if (require.main === module) {
1952
- (async () => {
1953
- const args = process.argv.slice(2);
1954
- const command = args[0] || 'help';
1955
-
1956
- switch (command) {
1957
- case 'install':
1958
- case 'i':
1959
- await install();
1960
- break;
1961
- case 'uninstall':
1962
- case 'remove':
1963
- case 'rm':
1964
- uninstall();
1965
- break;
1966
- case 'update':
1967
- case 'upgrade':
1968
- case 'up':
1969
- await update();
1970
- break;
1971
- case 'verify':
1972
- case 'check':
1973
- case 'doctor':
1974
- verify();
1975
- break;
1976
- case 'version':
1977
- case '-v':
1978
- case '--version':
1979
- console.log(`oh-my-claude v${VERSION}`);
1980
- break;
1981
- case 'config':
1982
- await handleConfigCommand(process.argv.slice(3));
1983
- break;
1984
- case 'benchmark':
1985
- console.log('性能基准测试 (TypeScript 版本)');
1986
- break;
1987
- case 'help':
1988
- case '-h':
1989
- case '--help':
1990
- default:
1991
- console.log('\n🏔️ oh-my-claude - 基于中国传统文化的 Claude Code 智能编排插件 (TypeScript 版本)');
1992
- console.log(''.repeat(DIVIDER_LONG_LENGTH));
1993
- console.log('\n用法: oh-my-claude <command>\n');
1994
- console.log('命令:');
1995
- console.log(' install 安装插件到 Claude Code');
1996
- console.log(' config 配置管理 (show/get/set/save)');
1997
- console.log(' version 显示版本号');
1998
- console.log(' benchmark 运行性能基准测试');
1999
- console.log(' help 显示帮助信息');
2000
- console.log('\n示例:');
2001
- console.log(' npx oh-my-claude install');
2002
- console.log(' oh-my-claude config show');
2003
- console.log(' oh-my-claude config set debug true');
2004
- console.log(' oh-my-claude benchmark');
2005
- console.log(' oh-my-claude version');
2006
- break;
2007
- }
2008
- })();
2009
- }
1605
+ (async () => {
1606
+ const args = process.argv.slice(2);
1607
+ const command = args[0] || 'help';
1608
+ switch (command) {
1609
+ case 'install':
1610
+ case 'i':
1611
+ await install();
1612
+ break;
1613
+ case 'uninstall':
1614
+ case 'remove':
1615
+ case 'rm':
1616
+ uninstall();
1617
+ break;
1618
+ case 'update':
1619
+ case 'upgrade':
1620
+ case 'up':
1621
+ await update();
1622
+ break;
1623
+ case 'verify':
1624
+ case 'check':
1625
+ case 'doctor':
1626
+ verify();
1627
+ break;
1628
+ case 'version':
1629
+ case '-v':
1630
+ case '--version':
1631
+ console.log(`oh-my-claude v${VERSION}`);
1632
+ break;
1633
+ case 'config':
1634
+ await handleConfigCommand(process.argv.slice(3));
1635
+ break;
1636
+ case 'benchmark':
1637
+ console.log('性能基准测试 (TypeScript 版本)');
1638
+ break;
1639
+ case 'help':
1640
+ case '-h':
1641
+ case '--help':
1642
+ default:
1643
+ console.log('\n🏔️ oh-my-claude - 基于中国传统文化的 Claude Code 智能编排插件 (TypeScript 版本)');
1644
+ console.log('━'.repeat(DIVIDER_LONG_LENGTH));
1645
+ console.log('\n用法: oh-my-claude <command>\n');
1646
+ console.log('命令:');
1647
+ console.log(' install 安装插件到 Claude Code');
1648
+ console.log(' config 配置管理 (show/get/set/save)');
1649
+ console.log(' version 显示版本号');
1650
+ console.log(' benchmark 运行性能基准测试');
1651
+ console.log(' help 显示帮助信息');
1652
+ console.log('\n示例:');
1653
+ console.log(' npx oh-my-claude install');
1654
+ console.log(' oh-my-claude config show');
1655
+ console.log(' oh-my-claude config set debug true');
1656
+ console.log(' oh-my-claude benchmark');
1657
+ console.log(' oh-my-claude version');
1658
+ break;
1659
+ }
1660
+ })();
1661
+ }
1662
+ //# sourceMappingURL=cli.js.map