aico-cli 0.1.7 → 0.1.9

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.
@@ -2,7 +2,7 @@ import ansis from 'ansis';
2
2
  import inquirer from 'inquirer';
3
3
  import { exec } from 'tinyexec';
4
4
  import { platform, homedir } from 'node:os';
5
- import { existsSync, copyFileSync, mkdirSync, readFileSync, writeFileSync, statSync, readdirSync } from 'node:fs';
5
+ import { existsSync, copyFileSync, mkdirSync, readdirSync, rmSync, unlinkSync, readFileSync, writeFileSync, statSync, renameSync } from 'node:fs';
6
6
  import { exec as exec$2 } from 'node:child_process';
7
7
  import { promisify as promisify$1 } from 'node:util';
8
8
  import { exec as exec$1 } from 'child_process';
@@ -10,10 +10,10 @@ import { promisify } from 'util';
10
10
  import ora from 'ora';
11
11
  import 'dayjs';
12
12
  import { join as join$1 } from 'node:path';
13
- import { join, dirname } from 'pathe';
13
+ import { join, dirname, basename } from 'pathe';
14
14
  import { fileURLToPath } from 'node:url';
15
15
 
16
- const version = "0.1.7";
16
+ const version = "0.1.9";
17
17
 
18
18
  function displayBanner(subtitle) {
19
19
  const defaultSubtitle = "\u4E00\u952E\u914D\u7F6E\u4F60\u7684\u5F00\u53D1\u73AF\u5883";
@@ -3417,6 +3417,32 @@ function copyDir(src, dest, options = {}) {
3417
3417
  }
3418
3418
  }
3419
3419
  }
3420
+ function removeFile(path) {
3421
+ try {
3422
+ if (exists(path) && isFile(path)) {
3423
+ unlinkSync(path);
3424
+ }
3425
+ } catch (error) {
3426
+ throw new FileSystemError(
3427
+ `Failed to remove file: ${path}`,
3428
+ path,
3429
+ error
3430
+ );
3431
+ }
3432
+ }
3433
+ function removeDir(path) {
3434
+ try {
3435
+ if (exists(path) && isDirectory(path)) {
3436
+ rmSync(path, { recursive: true, force: true });
3437
+ }
3438
+ } catch (error) {
3439
+ throw new FileSystemError(
3440
+ `Failed to remove directory: ${path}`,
3441
+ path,
3442
+ error
3443
+ );
3444
+ }
3445
+ }
3420
3446
 
3421
3447
  const messages = {
3422
3448
  // 菜单相关
@@ -4467,7 +4493,8 @@ class CCRInstaller extends AbstractInstaller {
4467
4493
  });
4468
4494
  ccrProcess.unref();
4469
4495
  this.log("CCR UI \u5DF2\u542F\u52A8", "success");
4470
- this.log("\u8BF7\u5728\u6D4F\u89C8\u5668\u4E2D\u8BBF\u95EE http://localhost:3456", "info");
4496
+ this.log("\u8BF7\u5728\u6D4F\u89C8\u5668\u4E2D\u8BBF\u95EE http://127.0.0.1:3456/ui/", "info");
4497
+ this.log("API\u5BC6\u94A5: sk-aico-x-ccr", "info");
4471
4498
  return this.createSuccessResult("CCR UI \u542F\u52A8\u6210\u529F");
4472
4499
  } catch (error) {
4473
4500
  return this.handleError(error, "CCR UI \u542F\u52A8");
@@ -4892,10 +4919,135 @@ class ConfigInstaller extends AbstractInstaller {
4892
4919
  copyFile(src, dest);
4893
4920
  }
4894
4921
  }
4895
- const agentsDir = join(sourceDir, "agents");
4896
- if (exists(agentsDir) && isDirectory(agentsDir)) {
4897
- const destAgentsDir = join(CLAUDE_DIR, "agents");
4898
- copyDir(agentsDir, destAgentsDir, { overwrite: true });
4922
+ }
4923
+ /**
4924
+ * 安装工作流文件
4925
+ * 复制 agents commands 目录
4926
+ */
4927
+ async installWorkflows() {
4928
+ try {
4929
+ this.log("\u5F00\u59CB\u5B89\u88C5\u5DE5\u4F5C\u6D41\u6587\u4EF6...", "info");
4930
+ const sourceDir = join(getPackageRoot(), "templates");
4931
+ const agentsDir = join(sourceDir, "agents");
4932
+ if (exists(agentsDir) && isDirectory(agentsDir)) {
4933
+ const destAgentsDir = join(CLAUDE_DIR, "agents");
4934
+ copyDir(agentsDir, destAgentsDir, { overwrite: true });
4935
+ this.log("\u5DF2\u590D\u5236: agents/ \u76EE\u5F55", "info");
4936
+ }
4937
+ const commandsDir = join(sourceDir, "commands");
4938
+ if (exists(commandsDir) && isDirectory(commandsDir)) {
4939
+ const destCommandsDir = join(CLAUDE_DIR, "commands");
4940
+ copyDir(commandsDir, destCommandsDir, { overwrite: true });
4941
+ this.log("\u5DF2\u590D\u5236: commands/ \u76EE\u5F55", "info");
4942
+ }
4943
+ this.log("\u5DE5\u4F5C\u6D41\u6587\u4EF6\u5B89\u88C5\u5B8C\u6210", "success");
4944
+ return this.createSuccessResult("\u5DE5\u4F5C\u6D41\u6587\u4EF6\u5B89\u88C5\u5B8C\u6210");
4945
+ } catch (error) {
4946
+ return this.handleError(error, "\u5DE5\u4F5C\u6D41\u6587\u4EF6\u5B89\u88C5");
4947
+ }
4948
+ }
4949
+ /**
4950
+ * 安装所有工作流
4951
+ */
4952
+ async installAllWorkflows() {
4953
+ return await this.installWorkflows();
4954
+ }
4955
+ /**
4956
+ * 复制 agents 和 commands 目录
4957
+ * 遵循先备份、再清空、最后复制的流程
4958
+ */
4959
+ async copyAgentsAndCommandsDirectories(sourceDir) {
4960
+ const directoriesToCopy = ["agents", "commands"];
4961
+ for (const dirName of directoriesToCopy) {
4962
+ const sourceSubDir = join(sourceDir, dirName);
4963
+ const destSubDir = join(CLAUDE_DIR, dirName);
4964
+ if (exists(sourceSubDir) && isDirectory(sourceSubDir)) {
4965
+ await this.backupDirectory(dirName, destSubDir);
4966
+ await this.cleanDirectory(destSubDir);
4967
+ copyDir(sourceSubDir, destSubDir, { overwrite: true });
4968
+ this.log(`\u5DF2\u590D\u5236 ${dirName} \u76EE\u5F55`, "success");
4969
+ }
4970
+ }
4971
+ }
4972
+ /**
4973
+ * 备份目录到 backup 文件夹
4974
+ */
4975
+ async backupDirectory(dirName, sourceDir) {
4976
+ if (!exists(sourceDir)) {
4977
+ return;
4978
+ }
4979
+ const backupBaseDir = join(CLAUDE_DIR, "backup");
4980
+ const backupDir = join(backupBaseDir, dirName);
4981
+ ensureDir(backupDir);
4982
+ copyDir(sourceDir, backupDir, { overwrite: true });
4983
+ this.log(`\u5DF2\u5907\u4EFD ${dirName} \u76EE\u5F55\u5230: ${backupDir}`, "info");
4984
+ }
4985
+ /**
4986
+ * 清空目录内容(Windows兼容版本)
4987
+ */
4988
+ async cleanDirectory(dirPath) {
4989
+ if (!exists(dirPath)) {
4990
+ ensureDir(dirPath);
4991
+ return;
4992
+ }
4993
+ try {
4994
+ const tempBackupDir = join(CLAUDE_DIR, ".temp-clean");
4995
+ const tempDir = join(tempBackupDir, `clean-${Date.now()}`);
4996
+ ensureDir(tempDir);
4997
+ const files = readDir(dirPath);
4998
+ if (files && files.length > 0) {
4999
+ for (const file of files) {
5000
+ const filePath = join(dirPath, file);
5001
+ try {
5002
+ if (isDirectory(filePath)) {
5003
+ removeDir(filePath);
5004
+ } else {
5005
+ removeFile(filePath);
5006
+ }
5007
+ } catch (error) {
5008
+ if (process.platform === "win32") {
5009
+ const tempFile = join(tempDir, file);
5010
+ try {
5011
+ renameSync(filePath, tempFile);
5012
+ this.log(`Windows\u517C\u5BB9\u5904\u7406: \u5DF2\u79FB\u52A8\u6587\u4EF6 ${file}`, "info");
5013
+ } catch (renameError) {
5014
+ this.log(`\u65E0\u6CD5\u79FB\u52A8\u6587\u4EF6 ${file}: ${renameError}`, "warning");
5015
+ }
5016
+ }
5017
+ }
5018
+ }
5019
+ }
5020
+ if (exists(tempDir)) {
5021
+ try {
5022
+ removeDir(tempDir);
5023
+ } catch (tempError) {
5024
+ this.log(`\u65E0\u6CD5\u6E05\u7406\u4E34\u65F6\u6587\u4EF6: ${tempError}`, "warning");
5025
+ }
5026
+ }
5027
+ this.log(`\u5DF2\u6E05\u7A7A ${dirPath} \u76EE\u5F55`, "info");
5028
+ } catch (error) {
5029
+ this.log(`\u6E05\u7A7A\u76EE\u5F55\u5931\u8D25: ${error}`, "warning");
5030
+ if (process.platform === "win32") {
5031
+ try {
5032
+ const dirName = basename(dirPath);
5033
+ const parentDir = dirname(dirPath);
5034
+ const backupDir = join(parentDir, `${dirName}.backup.${Date.now()}`);
5035
+ if (exists(dirPath)) {
5036
+ renameSync(dirPath, backupDir);
5037
+ ensureDir(dirPath);
5038
+ setTimeout(() => {
5039
+ try {
5040
+ removeDir(backupDir);
5041
+ } catch (backupError) {
5042
+ this.log(`\u65E0\u6CD5\u5220\u9664\u5907\u4EFD\u76EE\u5F55: ${backupError}`, "warning");
5043
+ }
5044
+ }, 1e3);
5045
+ this.log(`\u4F7F\u7528Windows\u5907\u9009\u65B9\u6848\u6E05\u7A7A\u76EE\u5F55: ${dirPath}`, "info");
5046
+ }
5047
+ } catch (fallbackError) {
5048
+ this.log(`Windows\u5907\u9009\u65B9\u6848\u4E5F\u5931\u8D25: ${fallbackError}`, "error");
5049
+ }
5050
+ }
4899
5051
  }
4900
5052
  }
4901
5053
  }
@@ -4990,175 +5142,6 @@ class MCPInstaller extends AbstractInstaller {
4990
5142
  }
4991
5143
  }
4992
5144
 
4993
- async function createBackup(sourceDir, backupDir, options = {}) {
4994
- const { include, exclude = [], recursive = true } = options;
4995
- if (!exists(sourceDir)) {
4996
- return void 0;
4997
- }
4998
- const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4999
- const timestampedBackupDir = join(backupDir, `backup-${timestamp}`);
5000
- ensureDir(timestampedBackupDir);
5001
- if (include && include.length > 0) {
5002
- for (const fileName of include) {
5003
- const srcPath = join(sourceDir, fileName);
5004
- const destPath = join(timestampedBackupDir, fileName);
5005
- if (exists(srcPath)) {
5006
- if (isDirectory(srcPath)) {
5007
- copyDir(srcPath, destPath, { overwrite: true });
5008
- } else {
5009
- copyFile(srcPath, destPath);
5010
- }
5011
- }
5012
- }
5013
- } else {
5014
- const entries = readDir(sourceDir);
5015
- for (const entry of entries) {
5016
- if (exclude.includes(entry)) {
5017
- continue;
5018
- }
5019
- const srcPath = join(sourceDir, entry);
5020
- const destPath = join(timestampedBackupDir, entry);
5021
- if (isDirectory(srcPath) && recursive) {
5022
- copyDir(srcPath, destPath, { overwrite: true });
5023
- } else if (isFile(srcPath)) {
5024
- copyFile(srcPath, destPath);
5025
- }
5026
- }
5027
- }
5028
- return timestampedBackupDir;
5029
- }
5030
- async function restoreFromBackup(backupDir, targetDir) {
5031
- if (!exists(backupDir)) {
5032
- return false;
5033
- }
5034
- const latestBackup = await getLatestBackup(backupDir);
5035
- if (!latestBackup) {
5036
- return false;
5037
- }
5038
- copyDir(latestBackup, targetDir, { overwrite: true });
5039
- return true;
5040
- }
5041
- async function getLatestBackup(backupBaseDir) {
5042
- if (!exists(backupBaseDir)) {
5043
- return void 0;
5044
- }
5045
- const entries = readDir(backupBaseDir);
5046
- const backupDirs = entries.filter((entry) => {
5047
- const fullPath = join(backupBaseDir, entry);
5048
- return isDirectory(fullPath) && entry.startsWith("backup-");
5049
- }).sort().reverse();
5050
- return backupDirs.length > 0 ? join(backupBaseDir, backupDirs[0]) : void 0;
5051
- }
5052
-
5053
- class WorkflowInstaller extends AbstractInstaller {
5054
- name = "Workflow";
5055
- sourceDir;
5056
- targetDir;
5057
- backupDir;
5058
- constructor(context) {
5059
- super(context);
5060
- this.sourceDir = join(getPackageRoot(), "templates");
5061
- this.targetDir = CLAUDE_DIR;
5062
- this.backupDir = join(CLAUDE_DIR, ".backups", "workflow");
5063
- }
5064
- async checkStatus() {
5065
- try {
5066
- const commandsDir = join(this.targetDir, "commands");
5067
- const isInstalled = exists(commandsDir) && isDirectory(commandsDir);
5068
- return { isInstalled };
5069
- } catch (error) {
5070
- this.log(`\u72B6\u6001\u68C0\u67E5\u5931\u8D25: ${error}`, "error");
5071
- return { isInstalled: false };
5072
- }
5073
- }
5074
- requiresInteraction(options) {
5075
- return false;
5076
- }
5077
- async install(options = {}) {
5078
- try {
5079
- this.log("\u5F00\u59CB\u5B89\u88C5\u5DE5\u4F5C\u6D41\u6587\u4EF6...", "info");
5080
- if (!exists(this.sourceDir)) {
5081
- return this.createFailureResult(`\u6E90\u76EE\u5F55\u4E0D\u5B58\u5728: ${this.sourceDir}`);
5082
- }
5083
- ensureDir(this.targetDir);
5084
- const backupPath = await this.createBackupIfNeeded(options);
5085
- await this.copyWorkflowFiles();
5086
- if (this.context?.lang) {
5087
- applyAiLanguageDirective(this.context.lang);
5088
- this.log("\u8BED\u8A00\u6307\u4EE4\u5E94\u7528\u6210\u529F", "success");
5089
- }
5090
- this.log("\u5DE5\u4F5C\u6D41\u6587\u4EF6\u5B89\u88C5\u5B8C\u6210", "success");
5091
- return this.createSuccessResult("\u5DE5\u4F5C\u6D41\u6587\u4EF6\u5B89\u88C5\u5B8C\u6210", backupPath);
5092
- } catch (error) {
5093
- return this.handleError(error, "\u5DE5\u4F5C\u6D41\u6587\u4EF6\u5B89\u88C5");
5094
- }
5095
- }
5096
- /**
5097
- * 卸载工作流文件,恢复备份
5098
- */
5099
- async uninstall() {
5100
- try {
5101
- this.log("\u5F00\u59CB\u5378\u8F7D\u5DE5\u4F5C\u6D41\u6587\u4EF6...", "info");
5102
- const restored = await restoreFromBackup(this.backupDir, this.targetDir);
5103
- if (restored) {
5104
- this.log("\u5DE5\u4F5C\u6D41\u6587\u4EF6\u5DF2\u6062\u590D\u5230\u4E4B\u524D\u7684\u72B6\u6001", "success");
5105
- return this.createSuccessResult("\u5DE5\u4F5C\u6D41\u6587\u4EF6\u5DF2\u6062\u590D\u5230\u4E4B\u524D\u7684\u72B6\u6001");
5106
- } else {
5107
- this.log("\u672A\u627E\u5230\u5907\u4EFD\u6587\u4EF6\uFF0C\u8DF3\u8FC7\u6062\u590D", "warning");
5108
- return this.createSkipResult("\u672A\u627E\u5230\u5907\u4EFD\u6587\u4EF6", "no_backup");
5109
- }
5110
- } catch (error) {
5111
- return this.handleError(error, "\u5DE5\u4F5C\u6D41\u6587\u4EF6\u5378\u8F7D");
5112
- }
5113
- }
5114
- /**
5115
- * 创建备份(如果需要)
5116
- */
5117
- async createBackupIfNeeded(options) {
5118
- if (options.force) {
5119
- return void 0;
5120
- }
5121
- const status = await this.checkStatus();
5122
- if (status.isInstalled) {
5123
- this.log("\u68C0\u6D4B\u5230\u5DF2\u5B58\u5728\u7684\u5DE5\u4F5C\u6D41\u6587\u4EF6\uFF0C\u521B\u5EFA\u5907\u4EFD...", "info");
5124
- return await createBackup(this.targetDir, this.backupDir, {
5125
- include: ["commands"]
5126
- });
5127
- }
5128
- return void 0;
5129
- }
5130
- /**
5131
- * 复制工作流相关文件
5132
- * 遵循 SOLID 原则的单一职责原则
5133
- */
5134
- async copyWorkflowFiles() {
5135
- const agentsDir = join(this.sourceDir, "agents");
5136
- if (exists(agentsDir) && isDirectory(agentsDir)) {
5137
- const destAgentsDir = join(this.targetDir, "agents");
5138
- copyDir(agentsDir, destAgentsDir, { overwrite: true });
5139
- this.log("\u5DF2\u590D\u5236: agents/ \u76EE\u5F55", "info");
5140
- }
5141
- const commandsDir = join(this.sourceDir, "commands");
5142
- if (exists(commandsDir) && isDirectory(commandsDir)) {
5143
- const destCommandsDir = join(this.targetDir, "commands");
5144
- copyDir(commandsDir, destCommandsDir, { overwrite: true });
5145
- this.log("\u5DF2\u590D\u5236: commands/ \u76EE\u5F55", "info");
5146
- }
5147
- }
5148
- /**
5149
- * 安装指定的工作流
5150
- */
5151
- async installSpecificWorkflows(workflowIds) {
5152
- return await this.install({ skipPrompt: true });
5153
- }
5154
- /**
5155
- * 安装所有工作流
5156
- */
5157
- async installAllWorkflows() {
5158
- return await this.install({ skipPrompt: true });
5159
- }
5160
- }
5161
-
5162
5145
  class ConfigCheckerInstaller extends AbstractInstaller {
5163
5146
  name = "Config Checker";
5164
5147
  async checkStatus() {
@@ -5227,7 +5210,6 @@ const INSTALLER_NAMES = {
5227
5210
  CCOMETIX_LINE: "ccometix-line",
5228
5211
  CONFIG: "config",
5229
5212
  MCP: "mcp",
5230
- WORKFLOW: "workflow",
5231
5213
  CONFIG_CHECKER: "config-checker"
5232
5214
  };
5233
5215
 
@@ -5264,10 +5246,6 @@ class InstallationComposer {
5264
5246
  INSTALLER_NAMES.MCP,
5265
5247
  () => new MCPInstaller(this.context)
5266
5248
  );
5267
- this.executor.register(
5268
- INSTALLER_NAMES.WORKFLOW,
5269
- () => new WorkflowInstaller(this.context)
5270
- );
5271
5249
  this.executor.register(
5272
5250
  INSTALLER_NAMES.CONFIG_CHECKER,
5273
5251
  () => new ConfigCheckerInstaller(this.context)
@@ -5276,6 +5254,7 @@ class InstallationComposer {
5276
5254
  /**
5277
5255
  * 公司配置安装
5278
5256
  * 包含:Claude Code + 固定API配置 + CCometixLine + MCP + Workflow
5257
+ * 公司配置不需要安装 CCR
5279
5258
  */
5280
5259
  async installCompanySetup(options = {}) {
5281
5260
  console.log(ansis.cyan("\n\u{1F3E2} \u5F00\u59CB\u914D\u7F6E\u5B89\u88C5...\n"));
@@ -5303,8 +5282,7 @@ class InstallationComposer {
5303
5282
  INSTALLER_NAMES.CLAUDE_CODE,
5304
5283
  INSTALLER_NAMES.CONFIG,
5305
5284
  INSTALLER_NAMES.CCOMETIX_LINE,
5306
- INSTALLER_NAMES.MCP,
5307
- INSTALLER_NAMES.WORKFLOW
5285
+ INSTALLER_NAMES.MCP
5308
5286
  ];
5309
5287
  const companyResults = await this.executor.executeBatch(companySteps, configOptions);
5310
5288
  const configInstaller = new ConfigInstaller(this.context);
@@ -5317,8 +5295,7 @@ class InstallationComposer {
5317
5295
  INSTALLER_NAMES.CLAUDE_CODE,
5318
5296
  INSTALLER_NAMES.CONFIG,
5319
5297
  INSTALLER_NAMES.CCOMETIX_LINE,
5320
- INSTALLER_NAMES.MCP,
5321
- INSTALLER_NAMES.WORKFLOW
5298
+ INSTALLER_NAMES.MCP
5322
5299
  ];
5323
5300
  const companyResults = await this.executor.executeBatch(companySteps, configOptions);
5324
5301
  const configInstaller = new ConfigInstaller(this.context);
@@ -5329,7 +5306,7 @@ class InstallationComposer {
5329
5306
  }
5330
5307
  /**
5331
5308
  * 个人配置安装
5332
- * 包含:Claude Code + CCR + 配置备份应用 + CCometixLine + MCP
5309
+ * 包含:Claude Code + CCR + 配置备份应用 + CCometixLine + MCP + Workflow
5333
5310
  */
5334
5311
  async installPersonalSetup(options = {}) {
5335
5312
  console.log(ansis.cyan("\n\u{1F464} \u5F00\u59CB\u4E2A\u4EBA\u914D\u7F6E\u5B89\u88C5...\n"));
@@ -5452,8 +5429,7 @@ class InstallationComposer {
5452
5429
  [INSTALLER_NAMES.CCR]: "CCR \u8DEF\u7531\u5668",
5453
5430
  [INSTALLER_NAMES.CCOMETIX_LINE]: "CCometixLine \u72B6\u6001\u680F",
5454
5431
  [INSTALLER_NAMES.CONFIG]: "\u914D\u7F6E\u6587\u4EF6",
5455
- [INSTALLER_NAMES.MCP]: "MCP \u670D\u52A1",
5456
- [INSTALLER_NAMES.WORKFLOW]: "\u5DE5\u4F5C\u6D41"
5432
+ [INSTALLER_NAMES.MCP]: "MCP \u670D\u52A1"
5457
5433
  };
5458
5434
  return nameMap[name] || name;
5459
5435
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aico-cli",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "packageManager": "pnpm@9.15.9",
5
5
  "description": "AI CLI",
6
6
  "repository": {