aico-cli 0.1.8 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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, readdirSync, rmSync, unlinkSync, readFileSync, writeFileSync, statSync } 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.8";
16
+ const version = "0.2.0";
17
17
 
18
18
  function displayBanner(subtitle) {
19
19
  const defaultSubtitle = "\u4E00\u952E\u914D\u7F6E\u4F60\u7684\u5F00\u53D1\u73AF\u5883";
@@ -4848,6 +4848,10 @@ class ConfigInstaller extends AbstractInstaller {
4848
4848
  }
4849
4849
  copyConfigFiles("zh-CN", false);
4850
4850
  await this.copyAiPersonalityFiles();
4851
+ const workflowResult = await this.installWorkflows();
4852
+ if (!workflowResult.success) {
4853
+ this.log(`\u5DE5\u4F5C\u6D41\u5B89\u88C5\u5931\u8D25: ${workflowResult.message}`, "warning");
4854
+ }
4851
4855
  const apiConfig = options.configData?.apiConfig;
4852
4856
  if (apiConfig) {
4853
4857
  const configuredApi = configureApi(apiConfig);
@@ -4919,7 +4923,38 @@ class ConfigInstaller extends AbstractInstaller {
4919
4923
  copyFile(src, dest);
4920
4924
  }
4921
4925
  }
4922
- await this.copyAgentsAndCommandsDirectories(sourceDir);
4926
+ }
4927
+ /**
4928
+ * 安装工作流文件
4929
+ * 复制 agents 和 commands 目录
4930
+ */
4931
+ async installWorkflows() {
4932
+ try {
4933
+ this.log("\u5F00\u59CB\u5B89\u88C5\u5DE5\u4F5C\u6D41\u6587\u4EF6...", "info");
4934
+ const sourceDir = join(getPackageRoot(), "templates");
4935
+ const agentsDir = join(sourceDir, "agents");
4936
+ if (exists(agentsDir) && isDirectory(agentsDir)) {
4937
+ const destAgentsDir = join(CLAUDE_DIR, "agents");
4938
+ copyDir(agentsDir, destAgentsDir, { overwrite: true });
4939
+ this.log("\u5DF2\u590D\u5236: agents/ \u76EE\u5F55", "info");
4940
+ }
4941
+ const commandsDir = join(sourceDir, "commands");
4942
+ if (exists(commandsDir) && isDirectory(commandsDir)) {
4943
+ const destCommandsDir = join(CLAUDE_DIR, "commands");
4944
+ copyDir(commandsDir, destCommandsDir, { overwrite: true });
4945
+ this.log("\u5DF2\u590D\u5236: commands/ \u76EE\u5F55", "info");
4946
+ }
4947
+ this.log("\u5DE5\u4F5C\u6D41\u6587\u4EF6\u5B89\u88C5\u5B8C\u6210", "success");
4948
+ return this.createSuccessResult("\u5DE5\u4F5C\u6D41\u6587\u4EF6\u5B89\u88C5\u5B8C\u6210");
4949
+ } catch (error) {
4950
+ return this.handleError(error, "\u5DE5\u4F5C\u6D41\u6587\u4EF6\u5B89\u88C5");
4951
+ }
4952
+ }
4953
+ /**
4954
+ * 安装所有工作流
4955
+ */
4956
+ async installAllWorkflows() {
4957
+ return await this.installWorkflows();
4923
4958
  }
4924
4959
  /**
4925
4960
  * 复制 agents 和 commands 目录
@@ -4952,26 +4987,72 @@ class ConfigInstaller extends AbstractInstaller {
4952
4987
  this.log(`\u5DF2\u5907\u4EFD ${dirName} \u76EE\u5F55\u5230: ${backupDir}`, "info");
4953
4988
  }
4954
4989
  /**
4955
- * 清空目录内容
4990
+ * 清空目录内容(Windows兼容版本)
4956
4991
  */
4957
4992
  async cleanDirectory(dirPath) {
4958
4993
  if (!exists(dirPath)) {
4959
4994
  ensureDir(dirPath);
4960
4995
  return;
4961
4996
  }
4962
- const files = readDir(dirPath);
4963
- if (files && files.length > 0) {
4964
- for (const file of files) {
4965
- const filePath = join(dirPath, file);
4966
- if (isDirectory(filePath)) {
4967
- await this.cleanDirectory(filePath);
4968
- removeDir(filePath);
4969
- } else {
4970
- removeFile(filePath);
4997
+ try {
4998
+ const tempBackupDir = join(CLAUDE_DIR, ".temp-clean");
4999
+ const tempDir = join(tempBackupDir, `clean-${Date.now()}`);
5000
+ ensureDir(tempDir);
5001
+ const files = readDir(dirPath);
5002
+ if (files && files.length > 0) {
5003
+ for (const file of files) {
5004
+ const filePath = join(dirPath, file);
5005
+ try {
5006
+ if (isDirectory(filePath)) {
5007
+ removeDir(filePath);
5008
+ } else {
5009
+ removeFile(filePath);
5010
+ }
5011
+ } catch (error) {
5012
+ if (process.platform === "win32") {
5013
+ const tempFile = join(tempDir, file);
5014
+ try {
5015
+ renameSync(filePath, tempFile);
5016
+ this.log(`Windows\u517C\u5BB9\u5904\u7406: \u5DF2\u79FB\u52A8\u6587\u4EF6 ${file}`, "info");
5017
+ } catch (renameError) {
5018
+ this.log(`\u65E0\u6CD5\u79FB\u52A8\u6587\u4EF6 ${file}: ${renameError}`, "warning");
5019
+ }
5020
+ }
5021
+ }
5022
+ }
5023
+ }
5024
+ if (exists(tempDir)) {
5025
+ try {
5026
+ removeDir(tempDir);
5027
+ } catch (tempError) {
5028
+ this.log(`\u65E0\u6CD5\u6E05\u7406\u4E34\u65F6\u6587\u4EF6: ${tempError}`, "warning");
5029
+ }
5030
+ }
5031
+ this.log(`\u5DF2\u6E05\u7A7A ${dirPath} \u76EE\u5F55`, "info");
5032
+ } catch (error) {
5033
+ this.log(`\u6E05\u7A7A\u76EE\u5F55\u5931\u8D25: ${error}`, "warning");
5034
+ if (process.platform === "win32") {
5035
+ try {
5036
+ const dirName = basename(dirPath);
5037
+ const parentDir = dirname(dirPath);
5038
+ const backupDir = join(parentDir, `${dirName}.backup.${Date.now()}`);
5039
+ if (exists(dirPath)) {
5040
+ renameSync(dirPath, backupDir);
5041
+ ensureDir(dirPath);
5042
+ setTimeout(() => {
5043
+ try {
5044
+ removeDir(backupDir);
5045
+ } catch (backupError) {
5046
+ this.log(`\u65E0\u6CD5\u5220\u9664\u5907\u4EFD\u76EE\u5F55: ${backupError}`, "warning");
5047
+ }
5048
+ }, 1e3);
5049
+ this.log(`\u4F7F\u7528Windows\u5907\u9009\u65B9\u6848\u6E05\u7A7A\u76EE\u5F55: ${dirPath}`, "info");
5050
+ }
5051
+ } catch (fallbackError) {
5052
+ this.log(`Windows\u5907\u9009\u65B9\u6848\u4E5F\u5931\u8D25: ${fallbackError}`, "error");
4971
5053
  }
4972
5054
  }
4973
5055
  }
4974
- this.log(`\u5DF2\u6E05\u7A7A ${dirPath} \u76EE\u5F55`, "info");
4975
5056
  }
4976
5057
  }
4977
5058
 
@@ -5065,175 +5146,6 @@ class MCPInstaller extends AbstractInstaller {
5065
5146
  }
5066
5147
  }
5067
5148
 
5068
- async function createBackup(sourceDir, backupDir, options = {}) {
5069
- const { include, exclude = [], recursive = true } = options;
5070
- if (!exists(sourceDir)) {
5071
- return void 0;
5072
- }
5073
- const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
5074
- const timestampedBackupDir = join(backupDir, `backup-${timestamp}`);
5075
- ensureDir(timestampedBackupDir);
5076
- if (include && include.length > 0) {
5077
- for (const fileName of include) {
5078
- const srcPath = join(sourceDir, fileName);
5079
- const destPath = join(timestampedBackupDir, fileName);
5080
- if (exists(srcPath)) {
5081
- if (isDirectory(srcPath)) {
5082
- copyDir(srcPath, destPath, { overwrite: true });
5083
- } else {
5084
- copyFile(srcPath, destPath);
5085
- }
5086
- }
5087
- }
5088
- } else {
5089
- const entries = readDir(sourceDir);
5090
- for (const entry of entries) {
5091
- if (exclude.includes(entry)) {
5092
- continue;
5093
- }
5094
- const srcPath = join(sourceDir, entry);
5095
- const destPath = join(timestampedBackupDir, entry);
5096
- if (isDirectory(srcPath) && recursive) {
5097
- copyDir(srcPath, destPath, { overwrite: true });
5098
- } else if (isFile(srcPath)) {
5099
- copyFile(srcPath, destPath);
5100
- }
5101
- }
5102
- }
5103
- return timestampedBackupDir;
5104
- }
5105
- async function restoreFromBackup(backupDir, targetDir) {
5106
- if (!exists(backupDir)) {
5107
- return false;
5108
- }
5109
- const latestBackup = await getLatestBackup(backupDir);
5110
- if (!latestBackup) {
5111
- return false;
5112
- }
5113
- copyDir(latestBackup, targetDir, { overwrite: true });
5114
- return true;
5115
- }
5116
- async function getLatestBackup(backupBaseDir) {
5117
- if (!exists(backupBaseDir)) {
5118
- return void 0;
5119
- }
5120
- const entries = readDir(backupBaseDir);
5121
- const backupDirs = entries.filter((entry) => {
5122
- const fullPath = join(backupBaseDir, entry);
5123
- return isDirectory(fullPath) && entry.startsWith("backup-");
5124
- }).sort().reverse();
5125
- return backupDirs.length > 0 ? join(backupBaseDir, backupDirs[0]) : void 0;
5126
- }
5127
-
5128
- class WorkflowInstaller extends AbstractInstaller {
5129
- name = "Workflow";
5130
- sourceDir;
5131
- targetDir;
5132
- backupDir;
5133
- constructor(context) {
5134
- super(context);
5135
- this.sourceDir = join(getPackageRoot(), "templates");
5136
- this.targetDir = CLAUDE_DIR;
5137
- this.backupDir = join(CLAUDE_DIR, ".backups", "workflow");
5138
- }
5139
- async checkStatus() {
5140
- try {
5141
- const commandsDir = join(this.targetDir, "commands");
5142
- const isInstalled = exists(commandsDir) && isDirectory(commandsDir);
5143
- return { isInstalled };
5144
- } catch (error) {
5145
- this.log(`\u72B6\u6001\u68C0\u67E5\u5931\u8D25: ${error}`, "error");
5146
- return { isInstalled: false };
5147
- }
5148
- }
5149
- requiresInteraction(options) {
5150
- return false;
5151
- }
5152
- async install(options = {}) {
5153
- try {
5154
- this.log("\u5F00\u59CB\u5B89\u88C5\u5DE5\u4F5C\u6D41\u6587\u4EF6...", "info");
5155
- if (!exists(this.sourceDir)) {
5156
- return this.createFailureResult(`\u6E90\u76EE\u5F55\u4E0D\u5B58\u5728: ${this.sourceDir}`);
5157
- }
5158
- ensureDir(this.targetDir);
5159
- const backupPath = await this.createBackupIfNeeded(options);
5160
- await this.copyWorkflowFiles();
5161
- if (this.context?.lang) {
5162
- applyAiLanguageDirective(this.context.lang);
5163
- this.log("\u8BED\u8A00\u6307\u4EE4\u5E94\u7528\u6210\u529F", "success");
5164
- }
5165
- this.log("\u5DE5\u4F5C\u6D41\u6587\u4EF6\u5B89\u88C5\u5B8C\u6210", "success");
5166
- return this.createSuccessResult("\u5DE5\u4F5C\u6D41\u6587\u4EF6\u5B89\u88C5\u5B8C\u6210", backupPath);
5167
- } catch (error) {
5168
- return this.handleError(error, "\u5DE5\u4F5C\u6D41\u6587\u4EF6\u5B89\u88C5");
5169
- }
5170
- }
5171
- /**
5172
- * 卸载工作流文件,恢复备份
5173
- */
5174
- async uninstall() {
5175
- try {
5176
- this.log("\u5F00\u59CB\u5378\u8F7D\u5DE5\u4F5C\u6D41\u6587\u4EF6...", "info");
5177
- const restored = await restoreFromBackup(this.backupDir, this.targetDir);
5178
- if (restored) {
5179
- this.log("\u5DE5\u4F5C\u6D41\u6587\u4EF6\u5DF2\u6062\u590D\u5230\u4E4B\u524D\u7684\u72B6\u6001", "success");
5180
- return this.createSuccessResult("\u5DE5\u4F5C\u6D41\u6587\u4EF6\u5DF2\u6062\u590D\u5230\u4E4B\u524D\u7684\u72B6\u6001");
5181
- } else {
5182
- this.log("\u672A\u627E\u5230\u5907\u4EFD\u6587\u4EF6\uFF0C\u8DF3\u8FC7\u6062\u590D", "warning");
5183
- return this.createSkipResult("\u672A\u627E\u5230\u5907\u4EFD\u6587\u4EF6", "no_backup");
5184
- }
5185
- } catch (error) {
5186
- return this.handleError(error, "\u5DE5\u4F5C\u6D41\u6587\u4EF6\u5378\u8F7D");
5187
- }
5188
- }
5189
- /**
5190
- * 创建备份(如果需要)
5191
- */
5192
- async createBackupIfNeeded(options) {
5193
- if (options.force) {
5194
- return void 0;
5195
- }
5196
- const status = await this.checkStatus();
5197
- if (status.isInstalled) {
5198
- this.log("\u68C0\u6D4B\u5230\u5DF2\u5B58\u5728\u7684\u5DE5\u4F5C\u6D41\u6587\u4EF6\uFF0C\u521B\u5EFA\u5907\u4EFD...", "info");
5199
- return await createBackup(this.targetDir, this.backupDir, {
5200
- include: ["commands"]
5201
- });
5202
- }
5203
- return void 0;
5204
- }
5205
- /**
5206
- * 复制工作流相关文件
5207
- * 遵循 SOLID 原则的单一职责原则
5208
- */
5209
- async copyWorkflowFiles() {
5210
- const agentsDir = join(this.sourceDir, "agents");
5211
- if (exists(agentsDir) && isDirectory(agentsDir)) {
5212
- const destAgentsDir = join(this.targetDir, "agents");
5213
- copyDir(agentsDir, destAgentsDir, { overwrite: true });
5214
- this.log("\u5DF2\u590D\u5236: agents/ \u76EE\u5F55", "info");
5215
- }
5216
- const commandsDir = join(this.sourceDir, "commands");
5217
- if (exists(commandsDir) && isDirectory(commandsDir)) {
5218
- const destCommandsDir = join(this.targetDir, "commands");
5219
- copyDir(commandsDir, destCommandsDir, { overwrite: true });
5220
- this.log("\u5DF2\u590D\u5236: commands/ \u76EE\u5F55", "info");
5221
- }
5222
- }
5223
- /**
5224
- * 安装指定的工作流
5225
- */
5226
- async installSpecificWorkflows(workflowIds) {
5227
- return await this.install({ skipPrompt: true });
5228
- }
5229
- /**
5230
- * 安装所有工作流
5231
- */
5232
- async installAllWorkflows() {
5233
- return await this.install({ skipPrompt: true });
5234
- }
5235
- }
5236
-
5237
5149
  class ConfigCheckerInstaller extends AbstractInstaller {
5238
5150
  name = "Config Checker";
5239
5151
  async checkStatus() {
@@ -5302,7 +5214,6 @@ const INSTALLER_NAMES = {
5302
5214
  CCOMETIX_LINE: "ccometix-line",
5303
5215
  CONFIG: "config",
5304
5216
  MCP: "mcp",
5305
- WORKFLOW: "workflow",
5306
5217
  CONFIG_CHECKER: "config-checker"
5307
5218
  };
5308
5219
 
@@ -5339,10 +5250,6 @@ class InstallationComposer {
5339
5250
  INSTALLER_NAMES.MCP,
5340
5251
  () => new MCPInstaller(this.context)
5341
5252
  );
5342
- this.executor.register(
5343
- INSTALLER_NAMES.WORKFLOW,
5344
- () => new WorkflowInstaller(this.context)
5345
- );
5346
5253
  this.executor.register(
5347
5254
  INSTALLER_NAMES.CONFIG_CHECKER,
5348
5255
  () => new ConfigCheckerInstaller(this.context)
@@ -5379,8 +5286,7 @@ class InstallationComposer {
5379
5286
  INSTALLER_NAMES.CLAUDE_CODE,
5380
5287
  INSTALLER_NAMES.CONFIG,
5381
5288
  INSTALLER_NAMES.CCOMETIX_LINE,
5382
- INSTALLER_NAMES.MCP,
5383
- INSTALLER_NAMES.WORKFLOW
5289
+ INSTALLER_NAMES.MCP
5384
5290
  ];
5385
5291
  const companyResults = await this.executor.executeBatch(companySteps, configOptions);
5386
5292
  const configInstaller = new ConfigInstaller(this.context);
@@ -5393,8 +5299,7 @@ class InstallationComposer {
5393
5299
  INSTALLER_NAMES.CLAUDE_CODE,
5394
5300
  INSTALLER_NAMES.CONFIG,
5395
5301
  INSTALLER_NAMES.CCOMETIX_LINE,
5396
- INSTALLER_NAMES.MCP,
5397
- INSTALLER_NAMES.WORKFLOW
5302
+ INSTALLER_NAMES.MCP
5398
5303
  ];
5399
5304
  const companyResults = await this.executor.executeBatch(companySteps, configOptions);
5400
5305
  const configInstaller = new ConfigInstaller(this.context);
@@ -5414,8 +5319,7 @@ class InstallationComposer {
5414
5319
  INSTALLER_NAMES.CCR,
5415
5320
  INSTALLER_NAMES.CONFIG,
5416
5321
  INSTALLER_NAMES.CCOMETIX_LINE,
5417
- INSTALLER_NAMES.MCP,
5418
- INSTALLER_NAMES.WORKFLOW
5322
+ INSTALLER_NAMES.MCP
5419
5323
  ];
5420
5324
  const configOptions = {
5421
5325
  ...options,
@@ -5529,8 +5433,7 @@ class InstallationComposer {
5529
5433
  [INSTALLER_NAMES.CCR]: "CCR \u8DEF\u7531\u5668",
5530
5434
  [INSTALLER_NAMES.CCOMETIX_LINE]: "CCometixLine \u72B6\u6001\u680F",
5531
5435
  [INSTALLER_NAMES.CONFIG]: "\u914D\u7F6E\u6587\u4EF6",
5532
- [INSTALLER_NAMES.MCP]: "MCP \u670D\u52A1",
5533
- [INSTALLER_NAMES.WORKFLOW]: "\u5DE5\u4F5C\u6D41"
5436
+ [INSTALLER_NAMES.MCP]: "MCP \u670D\u52A1"
5534
5437
  };
5535
5438
  return nameMap[name] || name;
5536
5439
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aico-cli",
3
- "version": "0.1.8",
3
+ "version": "0.2.0",
4
4
  "packageManager": "pnpm@9.15.9",
5
5
  "description": "AI CLI",
6
6
  "repository": {