@mathripper/ideal-cli 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3752,20 +3752,15 @@ init_esm_shims();
3752
3752
 
3753
3753
  // src/constants/messages.ts
3754
3754
  init_esm_shims();
3755
- var MSG_INIT_SUCCESS = "\u9879\u76EE\u521D\u59CB\u5316\u5B8C\u6210\uFF01";
3756
3755
  var MSG_UPDATE_SUCCESS = "\u66F4\u65B0\u5B8C\u6210\uFF01";
3757
3756
  var ERR_DIR_EXISTS = "\u76EE\u5F55\u5DF2\u5B58\u5728\uFF0C\u8BF7\u4F7F\u7528 --force \u5F3A\u5236\u8986\u76D6";
3758
3757
  var ERR_CONFIG_NOT_FOUND = "\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u8FD0\u884C ideal init";
3759
3758
  var WARN_FILE_MODIFIED = "\u6587\u4EF6\u5DF2\u88AB\u4FEE\u6539\uFF0C\u66F4\u65B0\u65F6\u53EF\u80FD\u88AB\u8986\u76D6";
3760
3759
  var WARN_CONFLICT_DETECTED = "\u68C0\u6D4B\u5230\u51B2\u7A81\uFF0C\u8BF7\u624B\u52A8\u89E3\u51B3";
3761
3760
  var INFO_CREATING_DIR = "\u6B63\u5728\u521B\u5EFA\u76EE\u5F55...";
3762
- var INFO_DOWNLOADING = "\u6B63\u5728\u4E0B\u8F7D\u6A21\u677F...";
3763
3761
  var INFO_CHECKING = "\u6B63\u5728\u68C0\u67E5...";
3764
3762
  var INFO_UPDATING = "\u6B63\u5728\u66F4\u65B0...";
3765
3763
  var HELP_DOCTOR = "\u68C0\u67E5\u5DE5\u4F5C\u6D41\u914D\u7F6E\u5B8C\u6574\u6027\u548C\u6709\u6548\u6027";
3766
- var PROMPT_PROJECT_NAME = "\u9879\u76EE\u540D\u79F0";
3767
- var PROMPT_GIT_BRANCH = "Git \u4E3B\u5206\u652F\u540D\u79F0";
3768
- var PROMPT_TECH_STACK = "\u6280\u672F\u6808\u7C7B\u578B";
3769
3764
 
3770
3765
  // src/commands/doctor.ts
3771
3766
  init_esm_shims();
@@ -4390,30 +4385,6 @@ ${source_default.bold.cyan(message)}
4390
4385
  var logger = Logger.getInstance();
4391
4386
 
4392
4387
  // src/utils/detector.ts
4393
- var TECH_STACK_RULES = [
4394
- {
4395
- name: "React",
4396
- files: ["react.config.js", "react.config.ts"],
4397
- dependencies: ["react", "react-dom"]
4398
- },
4399
- {
4400
- name: "Vue",
4401
- files: ["vue.config.js", "vue.config.ts", "vite.config.js", "vite.config.ts"],
4402
- directories: ["src/views", "src/components"],
4403
- dependencies: ["vue"]
4404
- },
4405
- {
4406
- name: "Node.js",
4407
- files: ["nest-cli.json", "tsconfig.build.json"],
4408
- directories: ["src/controllers", "src/services", "src/modules"],
4409
- dependencies: ["express", "nestjs", "@nestjs/core", "koa", "fastify"]
4410
- },
4411
- {
4412
- name: "Python",
4413
- files: ["requirements.txt", "setup.py", "pyproject.toml", "Pipfile"],
4414
- directories: ["src", "tests"]
4415
- }
4416
- ];
4417
4388
  async function detectProjectRoot(startPath = process.cwd()) {
4418
4389
  const idealRoot = await getProjectRoot(startPath);
4419
4390
  if (idealRoot) {
@@ -4434,56 +4405,6 @@ async function detectProjectRoot(startPath = process.cwd()) {
4434
4405
  }
4435
4406
  return null;
4436
4407
  }
4437
- async function detectTechStack(projectRoot) {
4438
- const packageJsonPath = path3.join(projectRoot, "package.json");
4439
- const hasPackageJson = await fileExists(packageJsonPath);
4440
- if (hasPackageJson) {
4441
- try {
4442
- const packageJson = JSON.parse(await readFile(packageJsonPath));
4443
- const allDeps = {
4444
- ...packageJson.dependencies,
4445
- ...packageJson.devDependencies
4446
- };
4447
- for (const rule of TECH_STACK_RULES) {
4448
- if (rule.name === "Python") continue;
4449
- if (rule.files) {
4450
- for (const file of rule.files) {
4451
- if (await fileExists(path3.join(projectRoot, file))) {
4452
- return rule.name;
4453
- }
4454
- }
4455
- }
4456
- if (rule.directories) {
4457
- for (const dir of rule.directories) {
4458
- if (await pathExists(path3.join(projectRoot, dir))) {
4459
- return rule.name;
4460
- }
4461
- }
4462
- }
4463
- if (rule.dependencies) {
4464
- for (const dep of rule.dependencies) {
4465
- if (allDeps[dep]) {
4466
- return rule.name;
4467
- }
4468
- }
4469
- }
4470
- }
4471
- return "Node.js";
4472
- } catch (error2) {
4473
- logger.debug(
4474
- `Failed to parse package.json: ${error2 instanceof Error ? error2.message : "Unknown error"}`
4475
- );
4476
- }
4477
- }
4478
- const requirementsPath = path3.join(projectRoot, "requirements.txt");
4479
- const pyprojectPath = path3.join(projectRoot, "pyproject.toml");
4480
- const setupPath = path3.join(projectRoot, "setup.py");
4481
- const pipfilePath = path3.join(projectRoot, "Pipfile");
4482
- if (await fileExists(requirementsPath) || await fileExists(pyprojectPath) || await fileExists(setupPath) || await fileExists(pipfilePath)) {
4483
- return "Python";
4484
- }
4485
- return "Other";
4486
- }
4487
4408
  async function detectGitRepo(projectRoot) {
4488
4409
  const gitDir = path3.join(projectRoot, ".git");
4489
4410
  return pathExists(gitDir);
@@ -4500,59 +4421,6 @@ function getGitBranch(projectRoot) {
4500
4421
  return null;
4501
4422
  }
4502
4423
  }
4503
- function getGitRemoteUrl(projectRoot) {
4504
- try {
4505
- const url = execSync("git config --get remote.origin.url", {
4506
- cwd: projectRoot,
4507
- encoding: "utf-8",
4508
- stdio: ["pipe", "pipe", "pipe"]
4509
- }).trim();
4510
- return url || null;
4511
- } catch {
4512
- return null;
4513
- }
4514
- }
4515
- function extractProjectNameFromGitUrl(gitUrl) {
4516
- let projectName = gitUrl;
4517
- if (projectName.endsWith(".git")) {
4518
- projectName = projectName.slice(0, -4);
4519
- }
4520
- const parts = projectName.split("/");
4521
- if (parts.length > 0) {
4522
- projectName = parts[parts.length - 1];
4523
- }
4524
- if (projectName.includes(":")) {
4525
- const colonParts = projectName.split(":");
4526
- projectName = colonParts[colonParts.length - 1];
4527
- if (projectName.includes("/")) {
4528
- projectName = projectName.split("/").pop() || projectName;
4529
- }
4530
- }
4531
- return projectName;
4532
- }
4533
- async function getProjectNameFromPackageJson(projectRoot) {
4534
- const packageJsonPath = path3.join(projectRoot, "package.json");
4535
- if (!await fileExists(packageJsonPath)) {
4536
- return null;
4537
- }
4538
- try {
4539
- const packageJson = JSON.parse(await readFile(packageJsonPath));
4540
- return packageJson.name || null;
4541
- } catch {
4542
- return null;
4543
- }
4544
- }
4545
- async function detectProjectName(projectRoot) {
4546
- const packageName = await getProjectNameFromPackageJson(projectRoot);
4547
- if (packageName) {
4548
- return packageName;
4549
- }
4550
- const gitUrl = getGitRemoteUrl(projectRoot);
4551
- if (gitUrl) {
4552
- return extractProjectNameFromGitUrl(gitUrl);
4553
- }
4554
- return path3.basename(projectRoot);
4555
- }
4556
4424
  async function detectIdealInitialized(projectRoot) {
4557
4425
  const idealDir = path3.join(projectRoot, ".ideal");
4558
4426
  const configPath = path3.join(idealDir, "config.json");
@@ -5877,13 +5745,17 @@ var DEFAULT_PROJECT_CONFIG = {
5877
5745
  var TemplateManager = class {
5878
5746
  githubClient;
5879
5747
  repoOwner;
5748
+ repoName;
5880
5749
  templatePath;
5881
5750
  branch;
5882
5751
  cacheDir;
5883
5752
  verbose;
5884
5753
  constructor(config = {}) {
5885
5754
  this.githubClient = config.githubClient ?? new GitHubClient();
5886
- this.repoOwner = config.repoOwner ?? `${DEFAULT_REPO_OWNER}/${DEFAULT_REPO_NAME}`;
5755
+ const repoConfig = config.repoOwner ?? `${DEFAULT_REPO_OWNER}/${DEFAULT_REPO_NAME}`;
5756
+ const parts = repoConfig.split("/");
5757
+ this.repoOwner = parts[0] ?? DEFAULT_REPO_OWNER;
5758
+ this.repoName = parts[1] ?? DEFAULT_REPO_NAME;
5887
5759
  this.templatePath = config.templatePath ?? DEFAULT_TEMPLATE_PATH;
5888
5760
  this.branch = config.branch ?? DEFAULT_BRANCH;
5889
5761
  this.verbose = config.verbose ?? false;
@@ -5901,7 +5773,7 @@ var TemplateManager = class {
5901
5773
  * @returns 模板缓存目录路径
5902
5774
  */
5903
5775
  getTemplateCachePath() {
5904
- return path7.join(this.cacheDir, this.repoOwner, this.templatePath);
5776
+ return path7.join(this.cacheDir, this.repoOwner, this.repoName, this.templatePath);
5905
5777
  }
5906
5778
  /**
5907
5779
  * 获取版本缓存文件路径
@@ -5917,7 +5789,7 @@ var TemplateManager = class {
5917
5789
  */
5918
5790
  async fetchTemplate(force = false) {
5919
5791
  if (this.verbose) {
5920
- logger.debug(`Fetching template from ${this.repoOwner}/${this.templatePath}`);
5792
+ logger.debug(`Fetching template from ${this.repoOwner}/${this.repoName}/${this.templatePath}`);
5921
5793
  }
5922
5794
  const cachePath = this.getTemplateCachePath();
5923
5795
  const cacheExists = await pathExists(cachePath);
@@ -5934,12 +5806,12 @@ var TemplateManager = class {
5934
5806
  await ensureDir(cachePath);
5935
5807
  const files = await this.githubClient.fetchDirectory(
5936
5808
  this.repoOwner,
5809
+ this.repoName,
5937
5810
  this.templatePath,
5938
- "",
5939
5811
  this.branch
5940
5812
  );
5941
5813
  if (files.length === 0) {
5942
- logger.warn(`No files found in ${this.repoOwner}/${this.templatePath}`);
5814
+ logger.warn(`No files found in ${this.repoOwner}/${this.repoName}/${this.templatePath}`);
5943
5815
  return 0;
5944
5816
  }
5945
5817
  let downloaded = 0;
@@ -5948,7 +5820,7 @@ var TemplateManager = class {
5948
5820
  try {
5949
5821
  const content = await this.githubClient.getFileContent(
5950
5822
  this.repoOwner,
5951
- this.templatePath,
5823
+ this.repoName,
5952
5824
  file.path,
5953
5825
  this.branch
5954
5826
  );
@@ -5995,7 +5867,7 @@ var TemplateManager = class {
5995
5867
  async getTemplateVersion() {
5996
5868
  return this.githubClient.getVersion(
5997
5869
  this.repoOwner,
5998
- this.templatePath,
5870
+ this.repoName,
5999
5871
  "version.json",
6000
5872
  this.branch
6001
5873
  );
@@ -6940,7 +6812,8 @@ ${WARN_CONFLICT_DETECTED}`);
6940
6812
  // src/commands/init.ts
6941
6813
  init_esm_shims();
6942
6814
  import path10 from "path";
6943
- import inquirer from "inquirer";
6815
+ import { exec } from "child_process";
6816
+ import { promisify } from "util";
6944
6817
 
6945
6818
  // src/utils/spinner.ts
6946
6819
  init_esm_shims();
@@ -9886,6 +9759,7 @@ function spinnerFail(spinner, text) {
9886
9759
 
9887
9760
  // src/commands/init.ts
9888
9761
  init_file_system();
9762
+ var execAsync = promisify(exec);
9889
9763
  var DIRECTORY_STRUCTURE = [
9890
9764
  ".claude/agents",
9891
9765
  ".claude/skills",
@@ -9894,21 +9768,20 @@ var DIRECTORY_STRUCTURE = [
9894
9768
  "docs/Wiki/\u5F00\u53D1\u6587\u6863",
9895
9769
  "docs/Wiki/\u63A5\u53E3\u6587\u6863"
9896
9770
  ];
9897
- var TECH_STACK_CHOICES = [
9898
- { name: "React", value: "React" },
9899
- { name: "Vue", value: "Vue" },
9900
- { name: "Node.js", value: "Node.js" },
9901
- { name: "Python", value: "Python" },
9902
- { name: "Other", value: "Other" }
9903
- ];
9771
+ var TEMPLATE_CONFIG = {
9772
+ owner: DEFAULT_REPO_OWNER,
9773
+ repo: DEFAULT_REPO_NAME,
9774
+ branch: DEFAULT_BRANCH,
9775
+ templatePath: DEFAULT_TEMPLATE_PATH,
9776
+ // GitHub 仓库 zip 下载地址
9777
+ get zipUrl() {
9778
+ return `https://github.com/${this.owner}/${this.repo}/archive/refs/heads/${this.branch}.zip`;
9779
+ }
9780
+ };
9904
9781
  var InitCommand = class {
9905
- templateManager;
9906
- configManager;
9907
9782
  projectPath;
9908
9783
  verbose;
9909
9784
  constructor(config = {}) {
9910
- this.templateManager = new TemplateManager({ verbose: config.verbose });
9911
- this.configManager = new ConfigManager();
9912
9785
  this.projectPath = config.projectPath ?? process.cwd();
9913
9786
  this.verbose = config.verbose ?? false;
9914
9787
  if (this.verbose) {
@@ -9921,63 +9794,11 @@ var InitCommand = class {
9921
9794
  */
9922
9795
  async detectExistingConfig() {
9923
9796
  const claudeDir = path10.join(this.projectPath, ".claude");
9924
- const configPath = this.configManager.getConfigPath(this.projectPath);
9797
+ const configFile = path10.join(this.projectPath, ".claude", "project-config.md");
9925
9798
  const claudeDirExists = await pathExists(claudeDir);
9926
- const configFileExists = await fileExists(configPath);
9799
+ const configFileExists = await fileExists(configFile);
9927
9800
  return claudeDirExists && configFileExists;
9928
9801
  }
9929
- /**
9930
- * 交互式引导用户输入配置
9931
- * @returns 用户输入的配置
9932
- */
9933
- async promptConfig() {
9934
- const defaultProjectName = await detectProjectName(this.projectPath);
9935
- const defaultTechStack = await detectTechStack(this.projectPath);
9936
- const isGitRepo = await detectGitRepo(this.projectPath);
9937
- const defaultGitBranch = isGitRepo ? getGitBranch(this.projectPath) ?? "main" : "main";
9938
- logger.title("\u521D\u59CB\u5316 ideal-cli \u5DE5\u4F5C\u6D41\u914D\u7F6E");
9939
- logger.newline();
9940
- const answers = await inquirer.prompt([
9941
- {
9942
- type: "input",
9943
- name: "projectName",
9944
- message: PROMPT_PROJECT_NAME,
9945
- default: defaultProjectName,
9946
- validate: (input) => {
9947
- if (!input.trim()) {
9948
- return "\u9879\u76EE\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A";
9949
- }
9950
- return true;
9951
- }
9952
- },
9953
- {
9954
- type: "input",
9955
- name: "gitBranch",
9956
- message: PROMPT_GIT_BRANCH,
9957
- default: defaultGitBranch,
9958
- validate: (input) => {
9959
- if (!input.trim()) {
9960
- return "\u5206\u652F\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A";
9961
- }
9962
- return true;
9963
- }
9964
- },
9965
- {
9966
- type: "list",
9967
- name: "techStack",
9968
- message: PROMPT_TECH_STACK,
9969
- choices: TECH_STACK_CHOICES,
9970
- default: defaultTechStack
9971
- },
9972
- {
9973
- type: "confirm",
9974
- name: "createExample",
9975
- message: "\u662F\u5426\u521B\u5EFA\u793A\u4F8B\u6587\u4EF6\uFF1F",
9976
- default: false
9977
- }
9978
- ]);
9979
- return answers;
9980
- }
9981
9802
  /**
9982
9803
  * 创建目录结构
9983
9804
  * @returns 创建的目录数量
@@ -10003,53 +9824,54 @@ var InitCommand = class {
10003
9824
  }
10004
9825
  }
10005
9826
  /**
10006
- * 应用模板文件
10007
- * @param userInput 用户输入的配置
9827
+ * 下载并应用模板
9828
+ * 使用 curl 下载 zip 文件,解压后复制到目标目录
10008
9829
  * @returns 处理的文件数量
10009
9830
  */
10010
- async applyTemplate(userInput) {
10011
- const spinner = startSpinner(INFO_DOWNLOADING);
9831
+ async downloadAndApplyTemplate() {
9832
+ const spinner = startSpinner("\u6B63\u5728\u4E0B\u8F7D\u6A21\u677F...");
10012
9833
  try {
10013
- const variables = {
10014
- projectName: userInput.projectName,
10015
- gitBranch: userInput.gitBranch,
10016
- techStack: userInput.techStack,
10017
- initializedAt: (/* @__PURE__ */ new Date()).toISOString()
10018
- };
10019
- await this.templateManager.fetchTemplate();
10020
- const claudeDir = path10.join(this.projectPath, ".claude");
10021
- const filesProcessed = await this.templateManager.applyTemplate(
10022
- claudeDir,
10023
- variables
10024
- );
10025
- spinnerSuccess(spinner, `\u5E94\u7528\u4E86 ${filesProcessed} \u4E2A\u6A21\u677F\u6587\u4EF6`);
10026
- return filesProcessed;
9834
+ const tempDir = path10.join(this.projectPath, ".ideal-cli-temp");
9835
+ await ensureDir(tempDir);
9836
+ const zipFile = path10.join(tempDir, "template.zip");
9837
+ const extractDir = path10.join(tempDir, "extracted");
9838
+ try {
9839
+ logger.debug(`Downloading from: ${TEMPLATE_CONFIG.zipUrl}`);
9840
+ await execAsync(`curl -sL "${TEMPLATE_CONFIG.zipUrl}" -o "${zipFile}"`);
9841
+ if (!await fileExists(zipFile)) {
9842
+ throw new Error("\u6A21\u677F\u4E0B\u8F7D\u5931\u8D25");
9843
+ }
9844
+ await ensureDir(extractDir);
9845
+ await execAsync(`unzip -q "${zipFile}" -d "${extractDir}"`);
9846
+ const extractedFolder = path10.join(extractDir, `${TEMPLATE_CONFIG.repo}-${TEMPLATE_CONFIG.branch}`);
9847
+ const templateSourceDir = path10.join(extractedFolder, TEMPLATE_CONFIG.templatePath);
9848
+ if (!await pathExists(templateSourceDir)) {
9849
+ throw new Error(`\u6A21\u677F\u76EE\u5F55\u4E0D\u5B58\u5728: ${TEMPLATE_CONFIG.templatePath}`);
9850
+ }
9851
+ const claudeDir = path10.join(this.projectPath, ".claude");
9852
+ await copyDir(templateSourceDir, claudeDir, {
9853
+ overwrite: true,
9854
+ filter: (src) => {
9855
+ const relativePath = path10.relative(templateSourceDir, src);
9856
+ return !relativePath.startsWith("configs") && relativePath !== "version.json";
9857
+ }
9858
+ });
9859
+ spinnerSuccess(spinner, "\u6A21\u677F\u6587\u4EF6\u5E94\u7528\u6210\u529F");
9860
+ return 0;
9861
+ } finally {
9862
+ await remove(tempDir);
9863
+ }
10027
9864
  } catch (error2) {
10028
- spinnerFail(spinner, "\u6A21\u677F\u5E94\u7528\u5931\u8D25");
9865
+ spinnerFail(spinner, "\u6A21\u677F\u4E0B\u8F7D\u5931\u8D25");
10029
9866
  throw error2;
10030
9867
  }
10031
9868
  }
10032
- /**
10033
- * 保存项目配置
10034
- * @param userInput 用户输入的配置
10035
- */
10036
- async saveConfig(userInput) {
10037
- const config = this.configManager.createDefaultConfig(
10038
- userInput.projectName,
10039
- {
10040
- gitBranch: userInput.gitBranch,
10041
- techStack: userInput.techStack
10042
- }
10043
- );
10044
- await this.configManager.write(this.projectPath, config);
10045
- logger.debug("\u914D\u7F6E\u6587\u4EF6\u5DF2\u4FDD\u5B58");
10046
- }
10047
9869
  /**
10048
9870
  * 输出成功消息和下一步指引
10049
9871
  */
10050
9872
  outputSuccessMessage() {
10051
9873
  logger.newline();
10052
- logger.success(MSG_INIT_SUCCESS);
9874
+ logger.success("\u9879\u76EE\u521D\u59CB\u5316\u5B8C\u6210\uFF01");
10053
9875
  logger.newline();
10054
9876
  logger.divider();
10055
9877
  logger.newline();
@@ -10075,10 +9897,10 @@ var InitCommand = class {
10075
9897
  logger.info("\u4F7F\u7528 --force \u9009\u9879\u53EF\u4EE5\u5F3A\u5236\u8986\u76D6\u73B0\u6709\u914D\u7F6E");
10076
9898
  return 1;
10077
9899
  }
10078
- const userInput = await this.promptConfig();
9900
+ logger.title("\u521D\u59CB\u5316 ideal-cli \u5DE5\u4F5C\u6D41\u914D\u7F6E");
9901
+ logger.newline();
10079
9902
  await this.createDirectoryStructure();
10080
- await this.applyTemplate(userInput);
10081
- await this.saveConfig(userInput);
9903
+ await this.downloadAndApplyTemplate();
10082
9904
  this.outputSuccessMessage();
10083
9905
  return 0;
10084
9906
  } catch (error2) {