@robot-admin/git-standards 1.0.0 → 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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @robot-admin/git-standards
2
2
 
3
- 零配置 · 模块化 · Git 工程化标准工具包
3
+ > **v1.0.1** · 零配置 · 模块化 · Git 工程化标准工具包
4
4
 
5
5
  集成 Commitizen + Commitlint + Husky + ESLint + Prettier + Oxlint + lint-staged,支持按需选配。
6
6
 
@@ -236,6 +236,243 @@ module.exports = {
236
236
  | `.editorconfig` | - | ✔ | ✔ | 编辑器统一配置 |
237
237
  | `.prettierrc.js` | - | - | ✔ | 代码格式化配置 |
238
238
 
239
+ ### 各文件内容详解
240
+
241
+ #### `.cz-config.js` — Commitizen 交互式提交配置
242
+
243
+ 定义了 `git cz` 交互式提交的类型、消息模板和作用域。
244
+
245
+ **生成内容**:
246
+
247
+ ```js
248
+ module.exports = {
249
+ scopes: [], // 默认空,可自定义添加预设 scope
250
+ allowEmptyScopes: false, // 不允许空 scope
251
+ allowCustomScopes: true, // 允许自由输入 scope
252
+
253
+ types: [
254
+ { value: "wip", name: "wip: 🚧 开发中" },
255
+ { value: "feat", name: "feat: 🎯 新功能" },
256
+ { value: "fix", name: "fix: 🐛 Bug 修复" },
257
+ { value: "perf", name: "perf: ⚡️ 性能优化" },
258
+ { value: "deps", name: "deps: 📦 依赖更新" },
259
+ { value: "refactor", name: "refactor: ♻️ 重构" },
260
+ { value: "docs", name: "docs: 📚 文档变更" },
261
+ { value: "test", name: "test: 🔎 测试相关" },
262
+ { value: "style", name: "style: 💄 代码样式" },
263
+ { value: "build", name: "build: 🧳 构建/打包" },
264
+ { value: "chore", name: "chore: 🔧 其他杂项" },
265
+ { value: "revert", name: "revert: 🔙 回退" },
266
+ ],
267
+
268
+ messages: {
269
+ type: "请选择提交类型:",
270
+ customScope: "请输入修改范围(必填,格式如:模块/子模块):",
271
+ subject: "请简要描述提交(必填,不加句号):",
272
+ body: "请输入更详细的说明(可选):\n",
273
+ footer: 'Footer(可选): 例如 "Closes #123" 或 "Release-As: 1.3.1"\n',
274
+ confirmCommit: "确认提交以上内容?(y/n/e/h)",
275
+ },
276
+
277
+ skipQuestions: ["body"],
278
+ allowBreakingChanges: ["feat", "fix", "refactor"],
279
+ breakingPrefix: "BREAKING CHANGE:",
280
+ subjectLimit: 88,
281
+ };
282
+ ```
283
+
284
+ ---
285
+
286
+ #### `commitlint.config.js` — 提交信息校验规则
287
+
288
+ 校验 `git commit` 消息是否符合约定式提交规范。
289
+
290
+ **生成内容**:
291
+
292
+ ```js
293
+ module.exports = {
294
+ extends: ["@commitlint/config-conventional"],
295
+ rules: {
296
+ "type-enum": [
297
+ 2,
298
+ "always",
299
+ [
300
+ "wip",
301
+ "feat",
302
+ "fix",
303
+ "perf",
304
+ "deps",
305
+ "refactor",
306
+ "docs",
307
+ "test",
308
+ "style",
309
+ "build",
310
+ "chore",
311
+ "revert",
312
+ ],
313
+ ],
314
+ "type-case": [2, "always", "lower-case"],
315
+ "subject-empty": [2, "never"],
316
+ "type-empty": [2, "never"],
317
+ "subject-full-stop": [0, "never"],
318
+ "header-max-length": [2, "always", 88],
319
+ },
320
+ };
321
+ ```
322
+
323
+ ---
324
+
325
+ #### `.husky/commit-msg` — 提交信息校验 Hook
326
+
327
+ Git commit 时自动触发,校验提交信息格式是否合规。
328
+
329
+ **生成内容**:
330
+
331
+ ```shell
332
+ bunx --no-install commitlint --edit "$1"
333
+ ```
334
+
335
+ > 执行命令因包管理器不同而异(`bunx` / `npx` / `pnpm exec`)
336
+
337
+ ---
338
+
339
+ #### `.husky/pre-commit` — 代码质量检查 Hook
340
+
341
+ Git commit 前自动触发,根据启用功能动态生成。
342
+
343
+ **极简模式**: 无此文件
344
+
345
+ **标准模式**(ESLint + lint-staged):
346
+
347
+ ```shell
348
+ bunx lint-staged
349
+ ```
350
+
351
+ **完整模式**(Oxlint + lint-staged):
352
+
353
+ ```shell
354
+ bunx oxlint --max-warnings 0
355
+ bunx lint-staged
356
+ ```
357
+
358
+ > 文件会自动设置可执行权限(`chmod 755`),确保在 Git Bash / WSL 环境下正常运行。
359
+
360
+ ---
361
+
362
+ #### `eslint.config.ts` — ESLint Flat Config
363
+
364
+ 根据框架(Vue/React/Vanilla)、TypeScript、JSDoc 选项动态生成。
365
+
366
+ **Vue 3 + TypeScript 示例**:
367
+
368
+ ```ts
369
+ import pluginVue from "eslint-plugin-vue";
370
+ import vueTsConfigs from "@vue/eslint-config-typescript";
371
+ import skipFormatting from "@vue/eslint-config-prettier/skip-formatting";
372
+ import oxlint from "eslint-plugin-oxlint"; // 完整模式
373
+ import jsdocPlugin from "eslint-plugin-jsdoc"; // 启用 JSDoc 时
374
+ import { defineConfigWithVueTs } from "@vue/eslint-config-typescript";
375
+
376
+ export default defineConfigWithVueTs(
377
+ { name: "app/files-to-lint", files: ["**/*.{ts,mts,tsx,vue}"] },
378
+ { name: "app/files-to-ignore", ignores: ["**/dist/**", "**/coverage/**"] },
379
+
380
+ ...oxlint.configs["flat/recommended"], // Oxlint 基础规则
381
+ pluginVue.configs["flat/essential"], // Vue 规则
382
+ vueTsConfigs.recommended, // TS 规则
383
+
384
+ {
385
+ rules: {
386
+ "no-undef": "off",
387
+ "@typescript-eslint/no-explicit-any": "off",
388
+ "vue/multi-word-component-names": ["error", { ignores: ["index"] }],
389
+ // ... 更多规则
390
+ },
391
+ },
392
+ skipFormatting,
393
+ );
394
+ ```
395
+
396
+ **核心规则覆盖**: 每种框架预设都包含合理的默认规则,支持直接修改。
397
+
398
+ ---
399
+
400
+ #### `.editorconfig` — 编辑器统一配置
401
+
402
+ 统一团队成员的编辑器缩进、换行等基础格式。
403
+
404
+ **生成内容**:
405
+
406
+ ```ini
407
+ root = true
408
+
409
+ [*]
410
+ charset = utf-8
411
+ indent_style = space
412
+ indent_size = 2
413
+ end_of_line = lf
414
+ insert_final_newline = true
415
+ trim_trailing_whitespace = true
416
+
417
+ [*.md]
418
+ trim_trailing_whitespace = false
419
+
420
+ [*.{yml,yaml}]
421
+ indent_size = 2
422
+
423
+ [Makefile]
424
+ indent_style = tab
425
+ ```
426
+
427
+ ---
428
+
429
+ #### `.prettierrc.js` — Prettier 格式化配置
430
+
431
+ 代码自动格式化规则,仅完整模式生成。
432
+
433
+ **生成内容**:
434
+
435
+ ```js
436
+ module.exports = {
437
+ singleQuote: true,
438
+ semi: false,
439
+ printWidth: 80,
440
+ trailingComma: "all",
441
+ arrowParens: "avoid",
442
+ endOfLine: "auto",
443
+ };
444
+ ```
445
+
446
+ ---
447
+
448
+ ### package.json 自动变更
449
+
450
+ init 同时更新 `package.json` 中的以下字段:
451
+
452
+ | 字段 | 极简 | 标准 | 完整 | 内容 |
453
+ | ------------------- | :--: | :--: | :--: | ------------------------------ |
454
+ | `scripts.cz` | ✔ | ✔ | ✔ | `"git-cz"` |
455
+ | `scripts.prepare` | ✔ | ✔ | ✔ | `"husky"` |
456
+ | `scripts.lint` | - | ✔ | ✔ | `"eslint . --fix"` 或含 oxlint |
457
+ | `scripts.format` | - | - | ✔ | `"prettier --write src/"` |
458
+ | `config.commitizen` | ✔ | ✔ | ✔ | cz-customizable 路径 |
459
+ | `lint-staged` | - | ✔ | ✔ | 暂存区检查规则 |
460
+
461
+ **lint-staged 配置示例**(完整模式):
462
+
463
+ ```jsonc
464
+ {
465
+ "lint-staged": {
466
+ "src/**/*.{js,jsx,ts,tsx,vue}": [
467
+ "oxlint --max-warnings 0 --deny-warnings",
468
+ "eslint --fix --no-cache",
469
+ "prettier --write"
470
+ ],
471
+ "*.{json,md,yml,yaml}": ["prettier --write"]
472
+ }
473
+ }
474
+ ```
475
+
239
476
  ## Git 提交完整流程
240
477
 
241
478
  ```
@@ -261,32 +498,6 @@ git commit(由 commitizen 触发)
261
498
  提交成功 ✅
262
499
  ```
263
500
 
264
- ## package.json 变更
265
-
266
- init 会自动更新 `package.json`:
267
-
268
- ```jsonc
269
- {
270
- "scripts": {
271
- "cz": "git-cz", // 始终添加
272
- "prepare": "husky", // 始终添加
273
- "lint": "oxlint ... && eslint ...", // 仅标准/完整模式
274
- "format": "prettier --write src/" // 仅完整模式
275
- },
276
- "config": {
277
- "commitizen": { "path": "node_modules/cz-customizable" }
278
- },
279
- "lint-staged": {
280
- // 仅标准/完整模式
281
- "src/**/*.{js,jsx,ts,tsx,vue}": [
282
- "oxlint --max-warnings 0 --deny-warnings",
283
- "eslint --fix --no-cache",
284
- "prettier --write"
285
- ]
286
- }
287
- }
288
- ```
289
-
290
501
  ## Doctor 诊断
291
502
 
292
503
  检查当前项目的 Git 标准化配置状态:
@@ -319,6 +530,88 @@ node node_modules/@robot-admin/git-standards/bin/robot-standards.js doctor
319
530
  ○ EditorConfig
320
531
  ```
321
532
 
533
+ ## 故障排除 (Troubleshooting)
534
+
535
+ ### Husky Hooks 未执行
536
+
537
+ **问题现象**: 提交代码时没有触发 lint 检查,直接提交成功
538
+
539
+ **可能原因**: Git Bash / WSL 环境下,hook 文件缺少执行权限
540
+
541
+ **解决方案**:
542
+
543
+ ```bash
544
+ # 检查 hook 文件权限
545
+ ls -la .husky/pre-commit .husky/commit-msg
546
+
547
+ # 如果没有 x 权限(应该显示 -rwxr-xr-x 而非 -rw-r--r--),执行:
548
+ chmod +x .husky/pre-commit .husky/commit-msg
549
+
550
+ # 验证修复
551
+ ls -la .husky/
552
+ ```
553
+
554
+ **预防措施**: 初始化后立即检查权限
555
+
556
+ ```bash
557
+ # 在 init 完成后执行
558
+ chmod +x .husky/*
559
+ ```
560
+
561
+ ### Commitizen 提示 "git-cz command not found"
562
+
563
+ **解决方案**:
564
+
565
+ ```bash
566
+ # 方式一:使用 npm scripts(推荐)
567
+ npm run cz
568
+ bun run cz
569
+
570
+ # 方式二:全局安装
571
+ npm install -g commitizen
572
+ ```
573
+
574
+ ### lint-staged 报错 "Cannot find module"
575
+
576
+ **原因**: 依赖安装不完整
577
+
578
+ **解决方案**:
579
+
580
+ ```bash
581
+ # 重新安装依赖
582
+ rm -rf node_modules
583
+ npm install
584
+
585
+ # 或使用 doctor 检查缺失的依赖
586
+ node node_modules/@robot-admin/git-standards/bin/robot-standards.js doctor
587
+ ```
588
+
589
+ ### ESLint Flat Config 不生效
590
+
591
+ **原因**: 项目中同时存在旧版 `.eslintrc.*` 文件
592
+
593
+ **解决方案**:
594
+
595
+ ```bash
596
+ # 删除旧配置文件(保留 eslint.config.ts)
597
+ rm .eslintrc.js .eslintrc.json .eslintrc.yml
598
+ ```
599
+
600
+ ### Windows 下 Husky 不工作
601
+
602
+ **原因**: Git 配置的 core.hooksPath 可能被覆盖
603
+
604
+ **解决方案**:
605
+
606
+ ```bash
607
+ # 检查 Git 配置
608
+ git config core.hooksPath
609
+
610
+ # 如果不是 .husky,重置它
611
+ git config --unset core.hooksPath
612
+ npm run prepare # 重新初始化 husky
613
+ ```
614
+
322
615
  ## License
323
616
 
324
617
  MIT
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/doctor.ts","../../src/utils/file.ts","../../src/utils/git.ts"],"sourcesContent":["/*\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-13\r\n * @Description: Doctor 命令 - 智能诊断 Git 标准化配置\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { resolve } from \"node:path\";\r\nimport chalk from \"chalk\";\r\nimport { fileExists, readJsonFile } from \"../utils/file\";\r\nimport { isGitRepository } from \"../utils/git\";\r\n\r\n// ─── 品牌 & 符号 ──────────────────────────────────────────────────\r\nconst BRAND = \"#7C3AED\";\r\nconst S = {\r\n LOGO: chalk.hex(BRAND).bold(\"[RS]\"),\r\n OK: chalk.green(\"✔\"),\r\n FAIL: chalk.red(\"✖\"),\r\n WARN: chalk.yellow(\"▲\"),\r\n SKIP: chalk.gray(\"○\"),\r\n LINE: chalk.gray(\"─\".repeat(48)),\r\n};\r\n\r\nexport interface DoctorOptions {\r\n cwd?: string;\r\n}\r\n\r\ninterface CheckResult {\r\n name: string;\r\n status: \"pass\" | \"fail\" | \"skip\";\r\n message?: string;\r\n}\r\n\r\n/**\r\n * Doctor 命令主函数\r\n * 根据已安装的功能智能检测,未安装的功能只做提示不标记为失败\r\n */\r\nexport async function doctor(options: DoctorOptions = {}) {\r\n const cwd = options.cwd || process.cwd();\r\n\r\n // ── Banner ──\r\n console.log();\r\n console.log(S.LINE);\r\n console.log(\r\n ` ${S.LOGO} ${chalk.bold(\"Robot Standards\")} ${chalk.gray(\"Doctor\")}`,\r\n );\r\n console.log(S.LINE);\r\n console.log();\r\n\r\n const checks: CheckResult[] = [];\r\n\r\n // ── 1. 基础环境 ──\r\n const isGit = isGitRepository(cwd);\r\n checks.push({\r\n name: \"Git 仓库\",\r\n status: isGit ? \"pass\" : \"fail\",\r\n message: isGit ? undefined : \"未初始化 Git 仓库,运行 git init\",\r\n });\r\n\r\n // ── 2. 读取 package.json 探测已安装功能 ──\r\n let packageJson: any = {};\r\n const packageJsonPath = resolve(cwd, \"package.json\");\r\n const hasPackageJson = fileExists(packageJsonPath);\r\n\r\n if (hasPackageJson) {\r\n try {\r\n packageJson = await readJsonFile(packageJsonPath);\r\n } catch {\r\n // ignore\r\n }\r\n } else {\r\n checks.push({\r\n name: \"package.json\",\r\n status: \"fail\",\r\n message: \"缺少 package.json\",\r\n });\r\n }\r\n\r\n const allDeps = {\r\n ...packageJson.dependencies,\r\n ...packageJson.devDependencies,\r\n };\r\n\r\n // 检测已安装的功能模块\r\n const hasCommitizen = !!allDeps[\"commitizen\"];\r\n const hasCommitlint = !!allDeps[\"@commitlint/cli\"];\r\n const hasHusky = !!allDeps[\"husky\"];\r\n const hasEslint = !!allDeps[\"eslint\"];\r\n const hasLintStaged = !!allDeps[\"lint-staged\"];\r\n const hasPrettier = !!allDeps[\"prettier\"];\r\n\r\n // ── 3. 核心功能检查(始终检查) ──\r\n console.log(` ${chalk.bold(\"核心功能\")}`);\r\n console.log();\r\n\r\n // Husky\r\n if (hasHusky) {\r\n const huskyDir = fileExists(resolve(cwd, \".husky\"));\r\n checks.push({\r\n name: \"Husky 目录\",\r\n status: huskyDir ? \"pass\" : \"fail\",\r\n message: huskyDir ? undefined : \"缺少 .husky 目录\",\r\n });\r\n\r\n const commitMsg = fileExists(resolve(cwd, \".husky/commit-msg\"));\r\n checks.push({\r\n name: \"commit-msg hook\",\r\n status: commitMsg ? \"pass\" : \"fail\",\r\n message: commitMsg ? undefined : \"缺少 commit-msg hook\",\r\n });\r\n } else {\r\n checks.push({\r\n name: \"Husky\",\r\n status: \"fail\",\r\n message: \"未安装 husky\",\r\n });\r\n }\r\n\r\n // Commitlint\r\n if (hasCommitlint) {\r\n const hasConfig =\r\n fileExists(resolve(cwd, \"commitlint.config.cjs\")) ||\r\n fileExists(resolve(cwd, \"commitlint.config.js\")) ||\r\n fileExists(resolve(cwd, \"commitlint.config.ts\"));\r\n checks.push({\r\n name: \"Commitlint 配置\",\r\n status: hasConfig ? \"pass\" : \"fail\",\r\n message: hasConfig ? undefined : \"缺少 commitlint.config.*\",\r\n });\r\n } else {\r\n checks.push({\r\n name: \"Commitlint\",\r\n status: \"fail\",\r\n message: \"未安装 @commitlint/cli\",\r\n });\r\n }\r\n\r\n // Commitizen\r\n if (hasCommitizen) {\r\n const hasCzConfig =\r\n fileExists(resolve(cwd, \".cz-config.cjs\")) ||\r\n fileExists(resolve(cwd, \".cz-config.js\"));\r\n checks.push({\r\n name: \"Commitizen 配置\",\r\n status: hasCzConfig ? \"pass\" : \"fail\",\r\n message: hasCzConfig ? undefined : \"缺少 .cz-config.*\",\r\n });\r\n\r\n const hasCommitizenPath = !!packageJson.config?.commitizen;\r\n checks.push({\r\n name: \"commitizen path 配置\",\r\n status: hasCommitizenPath ? \"pass\" : \"fail\",\r\n message: hasCommitizenPath\r\n ? undefined\r\n : \"package.json 中缺少 config.commitizen\",\r\n });\r\n } else {\r\n checks.push({\r\n name: \"Commitizen\",\r\n status: \"fail\",\r\n message: \"未安装 commitizen\",\r\n });\r\n }\r\n\r\n // cz 脚本\r\n const hasCzScript = !!packageJson.scripts?.cz;\r\n checks.push({\r\n name: \"cz 脚本\",\r\n status: hasCzScript ? \"pass\" : \"fail\",\r\n message: hasCzScript ? undefined : \"缺少 cz 脚本 (bun run cz)\",\r\n });\r\n\r\n // ── 输出核心检查结果 ──\r\n const coreChecks = checks.slice();\r\n for (const check of coreChecks) {\r\n printCheck(check);\r\n }\r\n\r\n // ── 4. 附加功能检查(仅检测已安装的) ──\r\n const extraChecks: CheckResult[] = [];\r\n\r\n if (hasEslint) {\r\n console.log();\r\n console.log(` ${chalk.bold(\"代码检查\")}`);\r\n console.log();\r\n\r\n const hasEslintConfig =\r\n fileExists(resolve(cwd, \"eslint.config.ts\")) ||\r\n fileExists(resolve(cwd, \"eslint.config.js\")) ||\r\n fileExists(resolve(cwd, \"eslint.config.mjs\"));\r\n const eslintCheck: CheckResult = {\r\n name: \"ESLint 配置\",\r\n status: hasEslintConfig ? \"pass\" : \"fail\",\r\n message: hasEslintConfig ? undefined : \"缺少 eslint.config.*\",\r\n };\r\n extraChecks.push(eslintCheck);\r\n printCheck(eslintCheck);\r\n\r\n if (hasHusky) {\r\n const preCommit = fileExists(resolve(cwd, \".husky/pre-commit\"));\r\n const preCommitCheck: CheckResult = {\r\n name: \"pre-commit hook\",\r\n status: preCommit ? \"pass\" : \"fail\",\r\n message: preCommit ? undefined : \"缺少 pre-commit hook\",\r\n };\r\n extraChecks.push(preCommitCheck);\r\n printCheck(preCommitCheck);\r\n }\r\n }\r\n\r\n if (hasLintStaged) {\r\n const lintStagedConfig = !!packageJson[\"lint-staged\"];\r\n const lintStagedCheck: CheckResult = {\r\n name: \"lint-staged 配置\",\r\n status: lintStagedConfig ? \"pass\" : \"fail\",\r\n message: lintStagedConfig\r\n ? undefined\r\n : \"package.json 中缺少 lint-staged 配置\",\r\n };\r\n extraChecks.push(lintStagedCheck);\r\n printCheck(lintStagedCheck);\r\n }\r\n\r\n if (hasPrettier) {\r\n console.log();\r\n console.log(` ${chalk.bold(\"代码格式化\")}`);\r\n console.log();\r\n\r\n const hasPrettierConfig =\r\n fileExists(resolve(cwd, \".prettierrc.cjs\")) ||\r\n fileExists(resolve(cwd, \".prettierrc.js\")) ||\r\n fileExists(resolve(cwd, \".prettierrc.json\")) ||\r\n fileExists(resolve(cwd, \".prettierrc\"));\r\n const prettierCheck: CheckResult = {\r\n name: \"Prettier 配置\",\r\n status: hasPrettierConfig ? \"pass\" : \"fail\",\r\n message: hasPrettierConfig ? undefined : \"缺少 .prettierrc.*\",\r\n };\r\n extraChecks.push(prettierCheck);\r\n printCheck(prettierCheck);\r\n }\r\n\r\n // EditorConfig(不依赖 package.json,直接检测文件)\r\n const hasEditorConfig = fileExists(resolve(cwd, \".editorconfig\"));\r\n if (hasEditorConfig) {\r\n // 只在存在时标记 pass,不存在不报错(属于可选功能)\r\n }\r\n\r\n // ── 5. 未安装功能提示 ──\r\n const notInstalled: string[] = [];\r\n if (!hasEslint) notInstalled.push(\"ESLint\");\r\n if (!hasLintStaged) notInstalled.push(\"lint-staged\");\r\n if (!hasPrettier) notInstalled.push(\"Prettier\");\r\n if (!hasEditorConfig) notInstalled.push(\"EditorConfig\");\r\n\r\n if (notInstalled.length > 0) {\r\n console.log();\r\n console.log(` ${chalk.bold(\"未启用的功能\")}`);\r\n console.log();\r\n for (const name of notInstalled) {\r\n console.log(` ${S.SKIP} ${chalk.gray(name)}`);\r\n }\r\n }\r\n\r\n // ── 6. 汇总 ──\r\n const allChecks = [...coreChecks, ...extraChecks];\r\n const passedCount = allChecks.filter((c) => c.status === \"pass\").length;\r\n const failedCount = allChecks.filter((c) => c.status === \"fail\").length;\r\n\r\n console.log();\r\n console.log(S.LINE);\r\n console.log(\r\n ` ${chalk.bold(\"总计:\")} ${chalk.green(`${passedCount} 通过`)} ${\r\n failedCount > 0 ? chalk.red(`${failedCount} 失败`) : chalk.gray(\"0 失败\")\r\n }`,\r\n );\r\n\r\n if (failedCount > 0) {\r\n console.log();\r\n console.log(\r\n ` ${S.WARN} ${chalk.yellow(\"运行\")} ${chalk.cyan(\r\n \"robot-standards init\",\r\n )} ${chalk.yellow(\"修复问题\")}`,\r\n );\r\n } else {\r\n console.log();\r\n console.log(` ${S.OK} ${chalk.green(\"所有检查通过!\")}`);\r\n }\r\n\r\n console.log(S.LINE);\r\n console.log();\r\n\r\n return failedCount === 0;\r\n}\r\n\r\nfunction printCheck(check: CheckResult) {\r\n const icon =\r\n check.status === \"pass\" ? S.OK : check.status === \"fail\" ? S.FAIL : S.SKIP;\r\n console.log(` ${icon} ${check.name}`);\r\n if (check.message && check.status === \"fail\") {\r\n console.log(` ${chalk.gray(check.message)}`);\r\n }\r\n}\r\n","/*\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-13\r\n * @Description: 文件操作工具\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { existsSync } from \"node:fs\";\r\nimport { readFile, writeFile, mkdir } from \"node:fs/promises\";\r\nimport { resolve, dirname } from \"node:path\";\r\n\r\n/**\r\n * 检查文件是否存在\r\n */\r\nexport function fileExists(filePath: string): boolean {\r\n return existsSync(filePath);\r\n}\r\n\r\n/**\r\n * 读取文件内容\r\n */\r\nexport async function readFileContent(filePath: string): Promise<string> {\r\n return await readFile(filePath, \"utf-8\");\r\n}\r\n\r\n/**\r\n * 写入文件内容\r\n */\r\nexport async function writeFileContent(\r\n filePath: string,\r\n content: string,\r\n): Promise<void> {\r\n const dir = dirname(filePath);\r\n if (!existsSync(dir)) {\r\n await mkdir(dir, { recursive: true });\r\n }\r\n await writeFile(filePath, content, \"utf-8\");\r\n}\r\n\r\n/**\r\n * 读取并解析 JSON 文件\r\n */\r\nexport async function readJsonFile<T = any>(filePath: string): Promise<T> {\r\n const content = await readFileContent(filePath);\r\n return JSON.parse(content) as T;\r\n}\r\n\r\n/**\r\n * 写入 JSON 文件\r\n */\r\nexport async function writeJsonFile(\r\n filePath: string,\r\n data: any,\r\n pretty: boolean = true,\r\n): Promise<void> {\r\n const content = pretty\r\n ? JSON.stringify(data, null, 2) + \"\\n\"\r\n : JSON.stringify(data);\r\n await writeFileContent(filePath, content);\r\n}\r\n\r\n/**\r\n * 更新 package.json\r\n */\r\nexport async function updatePackageJson(\r\n updates: Record<string, any>,\r\n cwd: string = process.cwd(),\r\n): Promise<void> {\r\n const packageJsonPath = resolve(cwd, \"package.json\");\r\n const packageJson = await readJsonFile(packageJsonPath);\r\n const updated = { ...packageJson, ...updates };\r\n await writeJsonFile(packageJsonPath, updated);\r\n}\r\n","/*\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-13\r\n * @Description: Git 工具函数\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { existsSync } from \"node:fs\";\r\nimport { resolve } from \"node:path\";\r\nimport { execa } from \"execa\";\r\n\r\n/**\r\n * 检查是否在 Git 仓库中\r\n */\r\nexport function isGitRepository(cwd: string = process.cwd()): boolean {\r\n return existsSync(resolve(cwd, \".git\"));\r\n}\r\n\r\n/**\r\n * 初始化 Git 仓库\r\n */\r\nexport async function initGitRepository(\r\n cwd: string = process.cwd(),\r\n): Promise<void> {\r\n if (!isGitRepository(cwd)) {\r\n await execa(\"git\", [\"init\"], { cwd });\r\n }\r\n}\r\n"],"mappings":"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","names":["import_node_path","import_node_fs","import_node_path","chalk"]}
1
+ {"version":3,"sources":["../../src/cli/doctor.ts","../../src/utils/file.ts","../../src/utils/git.ts"],"sourcesContent":["/*\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-13\r\n * @Description: Doctor 命令 - 智能诊断 Git 标准化配置\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { resolve } from \"node:path\";\r\nimport chalk from \"chalk\";\r\nimport { fileExists, readJsonFile } from \"../utils/file\";\r\nimport { isGitRepository } from \"../utils/git\";\r\n\r\n// ─── 品牌 & 符号 ──────────────────────────────────────────────────\r\nconst BRAND = \"#7C3AED\";\r\nconst S = {\r\n LOGO: chalk.hex(BRAND).bold(\"[RS]\"),\r\n OK: chalk.green(\"✔\"),\r\n FAIL: chalk.red(\"✖\"),\r\n WARN: chalk.yellow(\"▲\"),\r\n SKIP: chalk.gray(\"○\"),\r\n LINE: chalk.gray(\"─\".repeat(48)),\r\n};\r\n\r\nexport interface DoctorOptions {\r\n cwd?: string;\r\n}\r\n\r\ninterface CheckResult {\r\n name: string;\r\n status: \"pass\" | \"fail\" | \"skip\";\r\n message?: string;\r\n}\r\n\r\n/**\r\n * Doctor 命令主函数\r\n * 根据已安装的功能智能检测,未安装的功能只做提示不标记为失败\r\n */\r\nexport async function doctor(options: DoctorOptions = {}) {\r\n const cwd = options.cwd || process.cwd();\r\n\r\n // ── Banner ──\r\n console.log();\r\n console.log(S.LINE);\r\n console.log(\r\n ` ${S.LOGO} ${chalk.bold(\"Robot Standards\")} ${chalk.gray(\"Doctor\")}`,\r\n );\r\n console.log(S.LINE);\r\n console.log();\r\n\r\n const checks: CheckResult[] = [];\r\n\r\n // ── 1. 基础环境 ──\r\n const isGit = isGitRepository(cwd);\r\n checks.push({\r\n name: \"Git 仓库\",\r\n status: isGit ? \"pass\" : \"fail\",\r\n message: isGit ? undefined : \"未初始化 Git 仓库,运行 git init\",\r\n });\r\n\r\n // ── 2. 读取 package.json 探测已安装功能 ──\r\n let packageJson: any = {};\r\n const packageJsonPath = resolve(cwd, \"package.json\");\r\n const hasPackageJson = fileExists(packageJsonPath);\r\n\r\n if (hasPackageJson) {\r\n try {\r\n packageJson = await readJsonFile(packageJsonPath);\r\n } catch {\r\n // ignore\r\n }\r\n } else {\r\n checks.push({\r\n name: \"package.json\",\r\n status: \"fail\",\r\n message: \"缺少 package.json\",\r\n });\r\n }\r\n\r\n const allDeps = {\r\n ...packageJson.dependencies,\r\n ...packageJson.devDependencies,\r\n };\r\n\r\n // 检测已安装的功能模块\r\n const hasCommitizen = !!allDeps[\"commitizen\"];\r\n const hasCommitlint = !!allDeps[\"@commitlint/cli\"];\r\n const hasHusky = !!allDeps[\"husky\"];\r\n const hasEslint = !!allDeps[\"eslint\"];\r\n const hasLintStaged = !!allDeps[\"lint-staged\"];\r\n const hasPrettier = !!allDeps[\"prettier\"];\r\n\r\n // ── 3. 核心功能检查(始终检查) ──\r\n console.log(` ${chalk.bold(\"核心功能\")}`);\r\n console.log();\r\n\r\n // Husky\r\n if (hasHusky) {\r\n const huskyDir = fileExists(resolve(cwd, \".husky\"));\r\n checks.push({\r\n name: \"Husky 目录\",\r\n status: huskyDir ? \"pass\" : \"fail\",\r\n message: huskyDir ? undefined : \"缺少 .husky 目录\",\r\n });\r\n\r\n const commitMsg = fileExists(resolve(cwd, \".husky/commit-msg\"));\r\n checks.push({\r\n name: \"commit-msg hook\",\r\n status: commitMsg ? \"pass\" : \"fail\",\r\n message: commitMsg ? undefined : \"缺少 commit-msg hook\",\r\n });\r\n } else {\r\n checks.push({\r\n name: \"Husky\",\r\n status: \"fail\",\r\n message: \"未安装 husky\",\r\n });\r\n }\r\n\r\n // Commitlint\r\n if (hasCommitlint) {\r\n const hasConfig =\r\n fileExists(resolve(cwd, \"commitlint.config.cjs\")) ||\r\n fileExists(resolve(cwd, \"commitlint.config.js\")) ||\r\n fileExists(resolve(cwd, \"commitlint.config.ts\"));\r\n checks.push({\r\n name: \"Commitlint 配置\",\r\n status: hasConfig ? \"pass\" : \"fail\",\r\n message: hasConfig ? undefined : \"缺少 commitlint.config.*\",\r\n });\r\n } else {\r\n checks.push({\r\n name: \"Commitlint\",\r\n status: \"fail\",\r\n message: \"未安装 @commitlint/cli\",\r\n });\r\n }\r\n\r\n // Commitizen\r\n if (hasCommitizen) {\r\n const hasCzConfig =\r\n fileExists(resolve(cwd, \".cz-config.cjs\")) ||\r\n fileExists(resolve(cwd, \".cz-config.js\"));\r\n checks.push({\r\n name: \"Commitizen 配置\",\r\n status: hasCzConfig ? \"pass\" : \"fail\",\r\n message: hasCzConfig ? undefined : \"缺少 .cz-config.*\",\r\n });\r\n\r\n const hasCommitizenPath = !!packageJson.config?.commitizen;\r\n checks.push({\r\n name: \"commitizen path 配置\",\r\n status: hasCommitizenPath ? \"pass\" : \"fail\",\r\n message: hasCommitizenPath\r\n ? undefined\r\n : \"package.json 中缺少 config.commitizen\",\r\n });\r\n } else {\r\n checks.push({\r\n name: \"Commitizen\",\r\n status: \"fail\",\r\n message: \"未安装 commitizen\",\r\n });\r\n }\r\n\r\n // cz 脚本\r\n const hasCzScript = !!packageJson.scripts?.cz;\r\n checks.push({\r\n name: \"cz 脚本\",\r\n status: hasCzScript ? \"pass\" : \"fail\",\r\n message: hasCzScript ? undefined : \"缺少 cz 脚本 (bun run cz)\",\r\n });\r\n\r\n // ── 输出核心检查结果 ──\r\n const coreChecks = checks.slice();\r\n for (const check of coreChecks) {\r\n printCheck(check);\r\n }\r\n\r\n // ── 4. 附加功能检查(仅检测已安装的) ──\r\n const extraChecks: CheckResult[] = [];\r\n\r\n if (hasEslint) {\r\n console.log();\r\n console.log(` ${chalk.bold(\"代码检查\")}`);\r\n console.log();\r\n\r\n const hasEslintConfig =\r\n fileExists(resolve(cwd, \"eslint.config.ts\")) ||\r\n fileExists(resolve(cwd, \"eslint.config.js\")) ||\r\n fileExists(resolve(cwd, \"eslint.config.mjs\"));\r\n const eslintCheck: CheckResult = {\r\n name: \"ESLint 配置\",\r\n status: hasEslintConfig ? \"pass\" : \"fail\",\r\n message: hasEslintConfig ? undefined : \"缺少 eslint.config.*\",\r\n };\r\n extraChecks.push(eslintCheck);\r\n printCheck(eslintCheck);\r\n\r\n if (hasHusky) {\r\n const preCommit = fileExists(resolve(cwd, \".husky/pre-commit\"));\r\n const preCommitCheck: CheckResult = {\r\n name: \"pre-commit hook\",\r\n status: preCommit ? \"pass\" : \"fail\",\r\n message: preCommit ? undefined : \"缺少 pre-commit hook\",\r\n };\r\n extraChecks.push(preCommitCheck);\r\n printCheck(preCommitCheck);\r\n }\r\n }\r\n\r\n if (hasLintStaged) {\r\n const lintStagedConfig = !!packageJson[\"lint-staged\"];\r\n const lintStagedCheck: CheckResult = {\r\n name: \"lint-staged 配置\",\r\n status: lintStagedConfig ? \"pass\" : \"fail\",\r\n message: lintStagedConfig\r\n ? undefined\r\n : \"package.json 中缺少 lint-staged 配置\",\r\n };\r\n extraChecks.push(lintStagedCheck);\r\n printCheck(lintStagedCheck);\r\n }\r\n\r\n if (hasPrettier) {\r\n console.log();\r\n console.log(` ${chalk.bold(\"代码格式化\")}`);\r\n console.log();\r\n\r\n const hasPrettierConfig =\r\n fileExists(resolve(cwd, \".prettierrc.cjs\")) ||\r\n fileExists(resolve(cwd, \".prettierrc.js\")) ||\r\n fileExists(resolve(cwd, \".prettierrc.json\")) ||\r\n fileExists(resolve(cwd, \".prettierrc\"));\r\n const prettierCheck: CheckResult = {\r\n name: \"Prettier 配置\",\r\n status: hasPrettierConfig ? \"pass\" : \"fail\",\r\n message: hasPrettierConfig ? undefined : \"缺少 .prettierrc.*\",\r\n };\r\n extraChecks.push(prettierCheck);\r\n printCheck(prettierCheck);\r\n }\r\n\r\n // EditorConfig(不依赖 package.json,直接检测文件)\r\n const hasEditorConfig = fileExists(resolve(cwd, \".editorconfig\"));\r\n if (hasEditorConfig) {\r\n // 只在存在时标记 pass,不存在不报错(属于可选功能)\r\n }\r\n\r\n // ── 5. 未安装功能提示 ──\r\n const notInstalled: string[] = [];\r\n if (!hasEslint) notInstalled.push(\"ESLint\");\r\n if (!hasLintStaged) notInstalled.push(\"lint-staged\");\r\n if (!hasPrettier) notInstalled.push(\"Prettier\");\r\n if (!hasEditorConfig) notInstalled.push(\"EditorConfig\");\r\n\r\n if (notInstalled.length > 0) {\r\n console.log();\r\n console.log(` ${chalk.bold(\"未启用的功能\")}`);\r\n console.log();\r\n for (const name of notInstalled) {\r\n console.log(` ${S.SKIP} ${chalk.gray(name)}`);\r\n }\r\n }\r\n\r\n // ── 6. 汇总 ──\r\n const allChecks = [...coreChecks, ...extraChecks];\r\n const passedCount = allChecks.filter((c) => c.status === \"pass\").length;\r\n const failedCount = allChecks.filter((c) => c.status === \"fail\").length;\r\n\r\n console.log();\r\n console.log(S.LINE);\r\n console.log(\r\n ` ${chalk.bold(\"总计:\")} ${chalk.green(`${passedCount} 通过`)} ${\r\n failedCount > 0 ? chalk.red(`${failedCount} 失败`) : chalk.gray(\"0 失败\")\r\n }`,\r\n );\r\n\r\n if (failedCount > 0) {\r\n console.log();\r\n console.log(\r\n ` ${S.WARN} ${chalk.yellow(\"运行\")} ${chalk.cyan(\r\n \"robot-standards init\",\r\n )} ${chalk.yellow(\"修复问题\")}`,\r\n );\r\n } else {\r\n console.log();\r\n console.log(` ${S.OK} ${chalk.green(\"所有检查通过!\")}`);\r\n }\r\n\r\n console.log(S.LINE);\r\n console.log();\r\n\r\n return failedCount === 0;\r\n}\r\n\r\nfunction printCheck(check: CheckResult) {\r\n const icon =\r\n check.status === \"pass\" ? S.OK : check.status === \"fail\" ? S.FAIL : S.SKIP;\r\n console.log(` ${icon} ${check.name}`);\r\n if (check.message && check.status === \"fail\") {\r\n console.log(` ${chalk.gray(check.message)}`);\r\n }\r\n}\r\n","/*\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-13\r\n * @Description: 文件操作工具\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { existsSync, chmodSync } from \"node:fs\";\r\nimport { readFile, writeFile, mkdir } from \"node:fs/promises\";\r\nimport { resolve, dirname } from \"node:path\";\r\n\r\n/**\r\n * 检查文件是否存在\r\n */\r\nexport function fileExists(filePath: string): boolean {\r\n return existsSync(filePath);\r\n}\r\n\r\n/**\r\n * 读取文件内容\r\n */\r\nexport async function readFileContent(filePath: string): Promise<string> {\r\n return await readFile(filePath, \"utf-8\");\r\n}\r\n\r\n/**\r\n * 写入文件内容\r\n */\r\nexport async function writeFileContent(\r\n filePath: string,\r\n content: string,\r\n): Promise<void> {\r\n const dir = dirname(filePath);\r\n if (!existsSync(dir)) {\r\n await mkdir(dir, { recursive: true });\r\n }\r\n await writeFile(filePath, content, \"utf-8\");\r\n}\r\n\r\n/**\r\n * 写入可执行文件(自动设置 chmod 755 执行权限)\r\n * 用于 Husky Hook 等需要执行权限的文件\r\n */\r\nexport async function writeExecutableFile(\r\n filePath: string,\r\n content: string,\r\n): Promise<void> {\r\n await writeFileContent(filePath, content);\r\n try {\r\n chmodSync(filePath, 0o755);\r\n } catch {\r\n // Windows 环境下 chmod 可能不生效,忽略错误\r\n }\r\n}\r\n\r\n/**\r\n * 读取并解析 JSON 文件\r\n */\r\nexport async function readJsonFile<T = any>(filePath: string): Promise<T> {\r\n const content = await readFileContent(filePath);\r\n return JSON.parse(content) as T;\r\n}\r\n\r\n/**\r\n * 写入 JSON 文件\r\n */\r\nexport async function writeJsonFile(\r\n filePath: string,\r\n data: any,\r\n pretty: boolean = true,\r\n): Promise<void> {\r\n const content = pretty\r\n ? JSON.stringify(data, null, 2) + \"\\n\"\r\n : JSON.stringify(data);\r\n await writeFileContent(filePath, content);\r\n}\r\n\r\n/**\r\n * 更新 package.json\r\n */\r\nexport async function updatePackageJson(\r\n updates: Record<string, any>,\r\n cwd: string = process.cwd(),\r\n): Promise<void> {\r\n const packageJsonPath = resolve(cwd, \"package.json\");\r\n const packageJson = await readJsonFile(packageJsonPath);\r\n const updated = { ...packageJson, ...updates };\r\n await writeJsonFile(packageJsonPath, updated);\r\n}\r\n","/*\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-13\r\n * @Description: Git 工具函数\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { existsSync } from \"node:fs\";\r\nimport { resolve } from \"node:path\";\r\nimport { execa } from \"execa\";\r\n\r\n/**\r\n * 检查是否在 Git 仓库中\r\n */\r\nexport function isGitRepository(cwd: string = process.cwd()): boolean {\r\n return existsSync(resolve(cwd, \".git\"));\r\n}\r\n\r\n/**\r\n * 初始化 Git 仓库\r\n */\r\nexport async function initGitRepository(\r\n cwd: string = process.cwd(),\r\n): Promise<void> {\r\n if (!isGitRepository(cwd)) {\r\n await execa(\"git\", [\"init\"], { cwd });\r\n }\r\n}\r\n"],"mappings":"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","names":["import_node_path","import_node_fs","import_node_path","chalk"]}
@@ -3,7 +3,7 @@ import { resolve as resolve3 } from "path";
3
3
  import chalk from "chalk";
4
4
 
5
5
  // src/utils/file.ts
6
- import { existsSync } from "fs";
6
+ import { existsSync, chmodSync } from "fs";
7
7
  import { readFile, writeFile, mkdir } from "fs/promises";
8
8
  import { resolve, dirname } from "path";
9
9
  function fileExists(filePath) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/doctor.ts","../../src/utils/file.ts","../../src/utils/git.ts"],"sourcesContent":["/*\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-13\r\n * @Description: Doctor 命令 - 智能诊断 Git 标准化配置\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { resolve } from \"node:path\";\r\nimport chalk from \"chalk\";\r\nimport { fileExists, readJsonFile } from \"../utils/file\";\r\nimport { isGitRepository } from \"../utils/git\";\r\n\r\n// ─── 品牌 & 符号 ──────────────────────────────────────────────────\r\nconst BRAND = \"#7C3AED\";\r\nconst S = {\r\n LOGO: chalk.hex(BRAND).bold(\"[RS]\"),\r\n OK: chalk.green(\"✔\"),\r\n FAIL: chalk.red(\"✖\"),\r\n WARN: chalk.yellow(\"▲\"),\r\n SKIP: chalk.gray(\"○\"),\r\n LINE: chalk.gray(\"─\".repeat(48)),\r\n};\r\n\r\nexport interface DoctorOptions {\r\n cwd?: string;\r\n}\r\n\r\ninterface CheckResult {\r\n name: string;\r\n status: \"pass\" | \"fail\" | \"skip\";\r\n message?: string;\r\n}\r\n\r\n/**\r\n * Doctor 命令主函数\r\n * 根据已安装的功能智能检测,未安装的功能只做提示不标记为失败\r\n */\r\nexport async function doctor(options: DoctorOptions = {}) {\r\n const cwd = options.cwd || process.cwd();\r\n\r\n // ── Banner ──\r\n console.log();\r\n console.log(S.LINE);\r\n console.log(\r\n ` ${S.LOGO} ${chalk.bold(\"Robot Standards\")} ${chalk.gray(\"Doctor\")}`,\r\n );\r\n console.log(S.LINE);\r\n console.log();\r\n\r\n const checks: CheckResult[] = [];\r\n\r\n // ── 1. 基础环境 ──\r\n const isGit = isGitRepository(cwd);\r\n checks.push({\r\n name: \"Git 仓库\",\r\n status: isGit ? \"pass\" : \"fail\",\r\n message: isGit ? undefined : \"未初始化 Git 仓库,运行 git init\",\r\n });\r\n\r\n // ── 2. 读取 package.json 探测已安装功能 ──\r\n let packageJson: any = {};\r\n const packageJsonPath = resolve(cwd, \"package.json\");\r\n const hasPackageJson = fileExists(packageJsonPath);\r\n\r\n if (hasPackageJson) {\r\n try {\r\n packageJson = await readJsonFile(packageJsonPath);\r\n } catch {\r\n // ignore\r\n }\r\n } else {\r\n checks.push({\r\n name: \"package.json\",\r\n status: \"fail\",\r\n message: \"缺少 package.json\",\r\n });\r\n }\r\n\r\n const allDeps = {\r\n ...packageJson.dependencies,\r\n ...packageJson.devDependencies,\r\n };\r\n\r\n // 检测已安装的功能模块\r\n const hasCommitizen = !!allDeps[\"commitizen\"];\r\n const hasCommitlint = !!allDeps[\"@commitlint/cli\"];\r\n const hasHusky = !!allDeps[\"husky\"];\r\n const hasEslint = !!allDeps[\"eslint\"];\r\n const hasLintStaged = !!allDeps[\"lint-staged\"];\r\n const hasPrettier = !!allDeps[\"prettier\"];\r\n\r\n // ── 3. 核心功能检查(始终检查) ──\r\n console.log(` ${chalk.bold(\"核心功能\")}`);\r\n console.log();\r\n\r\n // Husky\r\n if (hasHusky) {\r\n const huskyDir = fileExists(resolve(cwd, \".husky\"));\r\n checks.push({\r\n name: \"Husky 目录\",\r\n status: huskyDir ? \"pass\" : \"fail\",\r\n message: huskyDir ? undefined : \"缺少 .husky 目录\",\r\n });\r\n\r\n const commitMsg = fileExists(resolve(cwd, \".husky/commit-msg\"));\r\n checks.push({\r\n name: \"commit-msg hook\",\r\n status: commitMsg ? \"pass\" : \"fail\",\r\n message: commitMsg ? undefined : \"缺少 commit-msg hook\",\r\n });\r\n } else {\r\n checks.push({\r\n name: \"Husky\",\r\n status: \"fail\",\r\n message: \"未安装 husky\",\r\n });\r\n }\r\n\r\n // Commitlint\r\n if (hasCommitlint) {\r\n const hasConfig =\r\n fileExists(resolve(cwd, \"commitlint.config.cjs\")) ||\r\n fileExists(resolve(cwd, \"commitlint.config.js\")) ||\r\n fileExists(resolve(cwd, \"commitlint.config.ts\"));\r\n checks.push({\r\n name: \"Commitlint 配置\",\r\n status: hasConfig ? \"pass\" : \"fail\",\r\n message: hasConfig ? undefined : \"缺少 commitlint.config.*\",\r\n });\r\n } else {\r\n checks.push({\r\n name: \"Commitlint\",\r\n status: \"fail\",\r\n message: \"未安装 @commitlint/cli\",\r\n });\r\n }\r\n\r\n // Commitizen\r\n if (hasCommitizen) {\r\n const hasCzConfig =\r\n fileExists(resolve(cwd, \".cz-config.cjs\")) ||\r\n fileExists(resolve(cwd, \".cz-config.js\"));\r\n checks.push({\r\n name: \"Commitizen 配置\",\r\n status: hasCzConfig ? \"pass\" : \"fail\",\r\n message: hasCzConfig ? undefined : \"缺少 .cz-config.*\",\r\n });\r\n\r\n const hasCommitizenPath = !!packageJson.config?.commitizen;\r\n checks.push({\r\n name: \"commitizen path 配置\",\r\n status: hasCommitizenPath ? \"pass\" : \"fail\",\r\n message: hasCommitizenPath\r\n ? undefined\r\n : \"package.json 中缺少 config.commitizen\",\r\n });\r\n } else {\r\n checks.push({\r\n name: \"Commitizen\",\r\n status: \"fail\",\r\n message: \"未安装 commitizen\",\r\n });\r\n }\r\n\r\n // cz 脚本\r\n const hasCzScript = !!packageJson.scripts?.cz;\r\n checks.push({\r\n name: \"cz 脚本\",\r\n status: hasCzScript ? \"pass\" : \"fail\",\r\n message: hasCzScript ? undefined : \"缺少 cz 脚本 (bun run cz)\",\r\n });\r\n\r\n // ── 输出核心检查结果 ──\r\n const coreChecks = checks.slice();\r\n for (const check of coreChecks) {\r\n printCheck(check);\r\n }\r\n\r\n // ── 4. 附加功能检查(仅检测已安装的) ──\r\n const extraChecks: CheckResult[] = [];\r\n\r\n if (hasEslint) {\r\n console.log();\r\n console.log(` ${chalk.bold(\"代码检查\")}`);\r\n console.log();\r\n\r\n const hasEslintConfig =\r\n fileExists(resolve(cwd, \"eslint.config.ts\")) ||\r\n fileExists(resolve(cwd, \"eslint.config.js\")) ||\r\n fileExists(resolve(cwd, \"eslint.config.mjs\"));\r\n const eslintCheck: CheckResult = {\r\n name: \"ESLint 配置\",\r\n status: hasEslintConfig ? \"pass\" : \"fail\",\r\n message: hasEslintConfig ? undefined : \"缺少 eslint.config.*\",\r\n };\r\n extraChecks.push(eslintCheck);\r\n printCheck(eslintCheck);\r\n\r\n if (hasHusky) {\r\n const preCommit = fileExists(resolve(cwd, \".husky/pre-commit\"));\r\n const preCommitCheck: CheckResult = {\r\n name: \"pre-commit hook\",\r\n status: preCommit ? \"pass\" : \"fail\",\r\n message: preCommit ? undefined : \"缺少 pre-commit hook\",\r\n };\r\n extraChecks.push(preCommitCheck);\r\n printCheck(preCommitCheck);\r\n }\r\n }\r\n\r\n if (hasLintStaged) {\r\n const lintStagedConfig = !!packageJson[\"lint-staged\"];\r\n const lintStagedCheck: CheckResult = {\r\n name: \"lint-staged 配置\",\r\n status: lintStagedConfig ? \"pass\" : \"fail\",\r\n message: lintStagedConfig\r\n ? undefined\r\n : \"package.json 中缺少 lint-staged 配置\",\r\n };\r\n extraChecks.push(lintStagedCheck);\r\n printCheck(lintStagedCheck);\r\n }\r\n\r\n if (hasPrettier) {\r\n console.log();\r\n console.log(` ${chalk.bold(\"代码格式化\")}`);\r\n console.log();\r\n\r\n const hasPrettierConfig =\r\n fileExists(resolve(cwd, \".prettierrc.cjs\")) ||\r\n fileExists(resolve(cwd, \".prettierrc.js\")) ||\r\n fileExists(resolve(cwd, \".prettierrc.json\")) ||\r\n fileExists(resolve(cwd, \".prettierrc\"));\r\n const prettierCheck: CheckResult = {\r\n name: \"Prettier 配置\",\r\n status: hasPrettierConfig ? \"pass\" : \"fail\",\r\n message: hasPrettierConfig ? undefined : \"缺少 .prettierrc.*\",\r\n };\r\n extraChecks.push(prettierCheck);\r\n printCheck(prettierCheck);\r\n }\r\n\r\n // EditorConfig(不依赖 package.json,直接检测文件)\r\n const hasEditorConfig = fileExists(resolve(cwd, \".editorconfig\"));\r\n if (hasEditorConfig) {\r\n // 只在存在时标记 pass,不存在不报错(属于可选功能)\r\n }\r\n\r\n // ── 5. 未安装功能提示 ──\r\n const notInstalled: string[] = [];\r\n if (!hasEslint) notInstalled.push(\"ESLint\");\r\n if (!hasLintStaged) notInstalled.push(\"lint-staged\");\r\n if (!hasPrettier) notInstalled.push(\"Prettier\");\r\n if (!hasEditorConfig) notInstalled.push(\"EditorConfig\");\r\n\r\n if (notInstalled.length > 0) {\r\n console.log();\r\n console.log(` ${chalk.bold(\"未启用的功能\")}`);\r\n console.log();\r\n for (const name of notInstalled) {\r\n console.log(` ${S.SKIP} ${chalk.gray(name)}`);\r\n }\r\n }\r\n\r\n // ── 6. 汇总 ──\r\n const allChecks = [...coreChecks, ...extraChecks];\r\n const passedCount = allChecks.filter((c) => c.status === \"pass\").length;\r\n const failedCount = allChecks.filter((c) => c.status === \"fail\").length;\r\n\r\n console.log();\r\n console.log(S.LINE);\r\n console.log(\r\n ` ${chalk.bold(\"总计:\")} ${chalk.green(`${passedCount} 通过`)} ${\r\n failedCount > 0 ? chalk.red(`${failedCount} 失败`) : chalk.gray(\"0 失败\")\r\n }`,\r\n );\r\n\r\n if (failedCount > 0) {\r\n console.log();\r\n console.log(\r\n ` ${S.WARN} ${chalk.yellow(\"运行\")} ${chalk.cyan(\r\n \"robot-standards init\",\r\n )} ${chalk.yellow(\"修复问题\")}`,\r\n );\r\n } else {\r\n console.log();\r\n console.log(` ${S.OK} ${chalk.green(\"所有检查通过!\")}`);\r\n }\r\n\r\n console.log(S.LINE);\r\n console.log();\r\n\r\n return failedCount === 0;\r\n}\r\n\r\nfunction printCheck(check: CheckResult) {\r\n const icon =\r\n check.status === \"pass\" ? S.OK : check.status === \"fail\" ? S.FAIL : S.SKIP;\r\n console.log(` ${icon} ${check.name}`);\r\n if (check.message && check.status === \"fail\") {\r\n console.log(` ${chalk.gray(check.message)}`);\r\n }\r\n}\r\n","/*\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-13\r\n * @Description: 文件操作工具\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { existsSync } from \"node:fs\";\r\nimport { readFile, writeFile, mkdir } from \"node:fs/promises\";\r\nimport { resolve, dirname } from \"node:path\";\r\n\r\n/**\r\n * 检查文件是否存在\r\n */\r\nexport function fileExists(filePath: string): boolean {\r\n return existsSync(filePath);\r\n}\r\n\r\n/**\r\n * 读取文件内容\r\n */\r\nexport async function readFileContent(filePath: string): Promise<string> {\r\n return await readFile(filePath, \"utf-8\");\r\n}\r\n\r\n/**\r\n * 写入文件内容\r\n */\r\nexport async function writeFileContent(\r\n filePath: string,\r\n content: string,\r\n): Promise<void> {\r\n const dir = dirname(filePath);\r\n if (!existsSync(dir)) {\r\n await mkdir(dir, { recursive: true });\r\n }\r\n await writeFile(filePath, content, \"utf-8\");\r\n}\r\n\r\n/**\r\n * 读取并解析 JSON 文件\r\n */\r\nexport async function readJsonFile<T = any>(filePath: string): Promise<T> {\r\n const content = await readFileContent(filePath);\r\n return JSON.parse(content) as T;\r\n}\r\n\r\n/**\r\n * 写入 JSON 文件\r\n */\r\nexport async function writeJsonFile(\r\n filePath: string,\r\n data: any,\r\n pretty: boolean = true,\r\n): Promise<void> {\r\n const content = pretty\r\n ? JSON.stringify(data, null, 2) + \"\\n\"\r\n : JSON.stringify(data);\r\n await writeFileContent(filePath, content);\r\n}\r\n\r\n/**\r\n * 更新 package.json\r\n */\r\nexport async function updatePackageJson(\r\n updates: Record<string, any>,\r\n cwd: string = process.cwd(),\r\n): Promise<void> {\r\n const packageJsonPath = resolve(cwd, \"package.json\");\r\n const packageJson = await readJsonFile(packageJsonPath);\r\n const updated = { ...packageJson, ...updates };\r\n await writeJsonFile(packageJsonPath, updated);\r\n}\r\n","/*\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-13\r\n * @Description: Git 工具函数\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { existsSync } from \"node:fs\";\r\nimport { resolve } from \"node:path\";\r\nimport { execa } from \"execa\";\r\n\r\n/**\r\n * 检查是否在 Git 仓库中\r\n */\r\nexport function isGitRepository(cwd: string = process.cwd()): boolean {\r\n return existsSync(resolve(cwd, \".git\"));\r\n}\r\n\r\n/**\r\n * 初始化 Git 仓库\r\n */\r\nexport async function initGitRepository(\r\n cwd: string = process.cwd(),\r\n): Promise<void> {\r\n if (!isGitRepository(cwd)) {\r\n await execa(\"git\", [\"init\"], { cwd });\r\n }\r\n}\r\n"],"mappings":"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","names":["resolve","existsSync","resolve","resolve"]}
1
+ {"version":3,"sources":["../../src/cli/doctor.ts","../../src/utils/file.ts","../../src/utils/git.ts"],"sourcesContent":["/*\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-13\r\n * @Description: Doctor 命令 - 智能诊断 Git 标准化配置\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { resolve } from \"node:path\";\r\nimport chalk from \"chalk\";\r\nimport { fileExists, readJsonFile } from \"../utils/file\";\r\nimport { isGitRepository } from \"../utils/git\";\r\n\r\n// ─── 品牌 & 符号 ──────────────────────────────────────────────────\r\nconst BRAND = \"#7C3AED\";\r\nconst S = {\r\n LOGO: chalk.hex(BRAND).bold(\"[RS]\"),\r\n OK: chalk.green(\"✔\"),\r\n FAIL: chalk.red(\"✖\"),\r\n WARN: chalk.yellow(\"▲\"),\r\n SKIP: chalk.gray(\"○\"),\r\n LINE: chalk.gray(\"─\".repeat(48)),\r\n};\r\n\r\nexport interface DoctorOptions {\r\n cwd?: string;\r\n}\r\n\r\ninterface CheckResult {\r\n name: string;\r\n status: \"pass\" | \"fail\" | \"skip\";\r\n message?: string;\r\n}\r\n\r\n/**\r\n * Doctor 命令主函数\r\n * 根据已安装的功能智能检测,未安装的功能只做提示不标记为失败\r\n */\r\nexport async function doctor(options: DoctorOptions = {}) {\r\n const cwd = options.cwd || process.cwd();\r\n\r\n // ── Banner ──\r\n console.log();\r\n console.log(S.LINE);\r\n console.log(\r\n ` ${S.LOGO} ${chalk.bold(\"Robot Standards\")} ${chalk.gray(\"Doctor\")}`,\r\n );\r\n console.log(S.LINE);\r\n console.log();\r\n\r\n const checks: CheckResult[] = [];\r\n\r\n // ── 1. 基础环境 ──\r\n const isGit = isGitRepository(cwd);\r\n checks.push({\r\n name: \"Git 仓库\",\r\n status: isGit ? \"pass\" : \"fail\",\r\n message: isGit ? undefined : \"未初始化 Git 仓库,运行 git init\",\r\n });\r\n\r\n // ── 2. 读取 package.json 探测已安装功能 ──\r\n let packageJson: any = {};\r\n const packageJsonPath = resolve(cwd, \"package.json\");\r\n const hasPackageJson = fileExists(packageJsonPath);\r\n\r\n if (hasPackageJson) {\r\n try {\r\n packageJson = await readJsonFile(packageJsonPath);\r\n } catch {\r\n // ignore\r\n }\r\n } else {\r\n checks.push({\r\n name: \"package.json\",\r\n status: \"fail\",\r\n message: \"缺少 package.json\",\r\n });\r\n }\r\n\r\n const allDeps = {\r\n ...packageJson.dependencies,\r\n ...packageJson.devDependencies,\r\n };\r\n\r\n // 检测已安装的功能模块\r\n const hasCommitizen = !!allDeps[\"commitizen\"];\r\n const hasCommitlint = !!allDeps[\"@commitlint/cli\"];\r\n const hasHusky = !!allDeps[\"husky\"];\r\n const hasEslint = !!allDeps[\"eslint\"];\r\n const hasLintStaged = !!allDeps[\"lint-staged\"];\r\n const hasPrettier = !!allDeps[\"prettier\"];\r\n\r\n // ── 3. 核心功能检查(始终检查) ──\r\n console.log(` ${chalk.bold(\"核心功能\")}`);\r\n console.log();\r\n\r\n // Husky\r\n if (hasHusky) {\r\n const huskyDir = fileExists(resolve(cwd, \".husky\"));\r\n checks.push({\r\n name: \"Husky 目录\",\r\n status: huskyDir ? \"pass\" : \"fail\",\r\n message: huskyDir ? undefined : \"缺少 .husky 目录\",\r\n });\r\n\r\n const commitMsg = fileExists(resolve(cwd, \".husky/commit-msg\"));\r\n checks.push({\r\n name: \"commit-msg hook\",\r\n status: commitMsg ? \"pass\" : \"fail\",\r\n message: commitMsg ? undefined : \"缺少 commit-msg hook\",\r\n });\r\n } else {\r\n checks.push({\r\n name: \"Husky\",\r\n status: \"fail\",\r\n message: \"未安装 husky\",\r\n });\r\n }\r\n\r\n // Commitlint\r\n if (hasCommitlint) {\r\n const hasConfig =\r\n fileExists(resolve(cwd, \"commitlint.config.cjs\")) ||\r\n fileExists(resolve(cwd, \"commitlint.config.js\")) ||\r\n fileExists(resolve(cwd, \"commitlint.config.ts\"));\r\n checks.push({\r\n name: \"Commitlint 配置\",\r\n status: hasConfig ? \"pass\" : \"fail\",\r\n message: hasConfig ? undefined : \"缺少 commitlint.config.*\",\r\n });\r\n } else {\r\n checks.push({\r\n name: \"Commitlint\",\r\n status: \"fail\",\r\n message: \"未安装 @commitlint/cli\",\r\n });\r\n }\r\n\r\n // Commitizen\r\n if (hasCommitizen) {\r\n const hasCzConfig =\r\n fileExists(resolve(cwd, \".cz-config.cjs\")) ||\r\n fileExists(resolve(cwd, \".cz-config.js\"));\r\n checks.push({\r\n name: \"Commitizen 配置\",\r\n status: hasCzConfig ? \"pass\" : \"fail\",\r\n message: hasCzConfig ? undefined : \"缺少 .cz-config.*\",\r\n });\r\n\r\n const hasCommitizenPath = !!packageJson.config?.commitizen;\r\n checks.push({\r\n name: \"commitizen path 配置\",\r\n status: hasCommitizenPath ? \"pass\" : \"fail\",\r\n message: hasCommitizenPath\r\n ? undefined\r\n : \"package.json 中缺少 config.commitizen\",\r\n });\r\n } else {\r\n checks.push({\r\n name: \"Commitizen\",\r\n status: \"fail\",\r\n message: \"未安装 commitizen\",\r\n });\r\n }\r\n\r\n // cz 脚本\r\n const hasCzScript = !!packageJson.scripts?.cz;\r\n checks.push({\r\n name: \"cz 脚本\",\r\n status: hasCzScript ? \"pass\" : \"fail\",\r\n message: hasCzScript ? undefined : \"缺少 cz 脚本 (bun run cz)\",\r\n });\r\n\r\n // ── 输出核心检查结果 ──\r\n const coreChecks = checks.slice();\r\n for (const check of coreChecks) {\r\n printCheck(check);\r\n }\r\n\r\n // ── 4. 附加功能检查(仅检测已安装的) ──\r\n const extraChecks: CheckResult[] = [];\r\n\r\n if (hasEslint) {\r\n console.log();\r\n console.log(` ${chalk.bold(\"代码检查\")}`);\r\n console.log();\r\n\r\n const hasEslintConfig =\r\n fileExists(resolve(cwd, \"eslint.config.ts\")) ||\r\n fileExists(resolve(cwd, \"eslint.config.js\")) ||\r\n fileExists(resolve(cwd, \"eslint.config.mjs\"));\r\n const eslintCheck: CheckResult = {\r\n name: \"ESLint 配置\",\r\n status: hasEslintConfig ? \"pass\" : \"fail\",\r\n message: hasEslintConfig ? undefined : \"缺少 eslint.config.*\",\r\n };\r\n extraChecks.push(eslintCheck);\r\n printCheck(eslintCheck);\r\n\r\n if (hasHusky) {\r\n const preCommit = fileExists(resolve(cwd, \".husky/pre-commit\"));\r\n const preCommitCheck: CheckResult = {\r\n name: \"pre-commit hook\",\r\n status: preCommit ? \"pass\" : \"fail\",\r\n message: preCommit ? undefined : \"缺少 pre-commit hook\",\r\n };\r\n extraChecks.push(preCommitCheck);\r\n printCheck(preCommitCheck);\r\n }\r\n }\r\n\r\n if (hasLintStaged) {\r\n const lintStagedConfig = !!packageJson[\"lint-staged\"];\r\n const lintStagedCheck: CheckResult = {\r\n name: \"lint-staged 配置\",\r\n status: lintStagedConfig ? \"pass\" : \"fail\",\r\n message: lintStagedConfig\r\n ? undefined\r\n : \"package.json 中缺少 lint-staged 配置\",\r\n };\r\n extraChecks.push(lintStagedCheck);\r\n printCheck(lintStagedCheck);\r\n }\r\n\r\n if (hasPrettier) {\r\n console.log();\r\n console.log(` ${chalk.bold(\"代码格式化\")}`);\r\n console.log();\r\n\r\n const hasPrettierConfig =\r\n fileExists(resolve(cwd, \".prettierrc.cjs\")) ||\r\n fileExists(resolve(cwd, \".prettierrc.js\")) ||\r\n fileExists(resolve(cwd, \".prettierrc.json\")) ||\r\n fileExists(resolve(cwd, \".prettierrc\"));\r\n const prettierCheck: CheckResult = {\r\n name: \"Prettier 配置\",\r\n status: hasPrettierConfig ? \"pass\" : \"fail\",\r\n message: hasPrettierConfig ? undefined : \"缺少 .prettierrc.*\",\r\n };\r\n extraChecks.push(prettierCheck);\r\n printCheck(prettierCheck);\r\n }\r\n\r\n // EditorConfig(不依赖 package.json,直接检测文件)\r\n const hasEditorConfig = fileExists(resolve(cwd, \".editorconfig\"));\r\n if (hasEditorConfig) {\r\n // 只在存在时标记 pass,不存在不报错(属于可选功能)\r\n }\r\n\r\n // ── 5. 未安装功能提示 ──\r\n const notInstalled: string[] = [];\r\n if (!hasEslint) notInstalled.push(\"ESLint\");\r\n if (!hasLintStaged) notInstalled.push(\"lint-staged\");\r\n if (!hasPrettier) notInstalled.push(\"Prettier\");\r\n if (!hasEditorConfig) notInstalled.push(\"EditorConfig\");\r\n\r\n if (notInstalled.length > 0) {\r\n console.log();\r\n console.log(` ${chalk.bold(\"未启用的功能\")}`);\r\n console.log();\r\n for (const name of notInstalled) {\r\n console.log(` ${S.SKIP} ${chalk.gray(name)}`);\r\n }\r\n }\r\n\r\n // ── 6. 汇总 ──\r\n const allChecks = [...coreChecks, ...extraChecks];\r\n const passedCount = allChecks.filter((c) => c.status === \"pass\").length;\r\n const failedCount = allChecks.filter((c) => c.status === \"fail\").length;\r\n\r\n console.log();\r\n console.log(S.LINE);\r\n console.log(\r\n ` ${chalk.bold(\"总计:\")} ${chalk.green(`${passedCount} 通过`)} ${\r\n failedCount > 0 ? chalk.red(`${failedCount} 失败`) : chalk.gray(\"0 失败\")\r\n }`,\r\n );\r\n\r\n if (failedCount > 0) {\r\n console.log();\r\n console.log(\r\n ` ${S.WARN} ${chalk.yellow(\"运行\")} ${chalk.cyan(\r\n \"robot-standards init\",\r\n )} ${chalk.yellow(\"修复问题\")}`,\r\n );\r\n } else {\r\n console.log();\r\n console.log(` ${S.OK} ${chalk.green(\"所有检查通过!\")}`);\r\n }\r\n\r\n console.log(S.LINE);\r\n console.log();\r\n\r\n return failedCount === 0;\r\n}\r\n\r\nfunction printCheck(check: CheckResult) {\r\n const icon =\r\n check.status === \"pass\" ? S.OK : check.status === \"fail\" ? S.FAIL : S.SKIP;\r\n console.log(` ${icon} ${check.name}`);\r\n if (check.message && check.status === \"fail\") {\r\n console.log(` ${chalk.gray(check.message)}`);\r\n }\r\n}\r\n","/*\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-13\r\n * @Description: 文件操作工具\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { existsSync, chmodSync } from \"node:fs\";\r\nimport { readFile, writeFile, mkdir } from \"node:fs/promises\";\r\nimport { resolve, dirname } from \"node:path\";\r\n\r\n/**\r\n * 检查文件是否存在\r\n */\r\nexport function fileExists(filePath: string): boolean {\r\n return existsSync(filePath);\r\n}\r\n\r\n/**\r\n * 读取文件内容\r\n */\r\nexport async function readFileContent(filePath: string): Promise<string> {\r\n return await readFile(filePath, \"utf-8\");\r\n}\r\n\r\n/**\r\n * 写入文件内容\r\n */\r\nexport async function writeFileContent(\r\n filePath: string,\r\n content: string,\r\n): Promise<void> {\r\n const dir = dirname(filePath);\r\n if (!existsSync(dir)) {\r\n await mkdir(dir, { recursive: true });\r\n }\r\n await writeFile(filePath, content, \"utf-8\");\r\n}\r\n\r\n/**\r\n * 写入可执行文件(自动设置 chmod 755 执行权限)\r\n * 用于 Husky Hook 等需要执行权限的文件\r\n */\r\nexport async function writeExecutableFile(\r\n filePath: string,\r\n content: string,\r\n): Promise<void> {\r\n await writeFileContent(filePath, content);\r\n try {\r\n chmodSync(filePath, 0o755);\r\n } catch {\r\n // Windows 环境下 chmod 可能不生效,忽略错误\r\n }\r\n}\r\n\r\n/**\r\n * 读取并解析 JSON 文件\r\n */\r\nexport async function readJsonFile<T = any>(filePath: string): Promise<T> {\r\n const content = await readFileContent(filePath);\r\n return JSON.parse(content) as T;\r\n}\r\n\r\n/**\r\n * 写入 JSON 文件\r\n */\r\nexport async function writeJsonFile(\r\n filePath: string,\r\n data: any,\r\n pretty: boolean = true,\r\n): Promise<void> {\r\n const content = pretty\r\n ? JSON.stringify(data, null, 2) + \"\\n\"\r\n : JSON.stringify(data);\r\n await writeFileContent(filePath, content);\r\n}\r\n\r\n/**\r\n * 更新 package.json\r\n */\r\nexport async function updatePackageJson(\r\n updates: Record<string, any>,\r\n cwd: string = process.cwd(),\r\n): Promise<void> {\r\n const packageJsonPath = resolve(cwd, \"package.json\");\r\n const packageJson = await readJsonFile(packageJsonPath);\r\n const updated = { ...packageJson, ...updates };\r\n await writeJsonFile(packageJsonPath, updated);\r\n}\r\n","/*\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-13\r\n * @Description: Git 工具函数\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { existsSync } from \"node:fs\";\r\nimport { resolve } from \"node:path\";\r\nimport { execa } from \"execa\";\r\n\r\n/**\r\n * 检查是否在 Git 仓库中\r\n */\r\nexport function isGitRepository(cwd: string = process.cwd()): boolean {\r\n return existsSync(resolve(cwd, \".git\"));\r\n}\r\n\r\n/**\r\n * 初始化 Git 仓库\r\n */\r\nexport async function initGitRepository(\r\n cwd: string = process.cwd(),\r\n): Promise<void> {\r\n if (!isGitRepository(cwd)) {\r\n await execa(\"git\", [\"init\"], { cwd });\r\n }\r\n}\r\n"],"mappings":"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","names":["resolve","existsSync","resolve","resolve"]}
package/dist/cli/init.cjs CHANGED
@@ -112,6 +112,13 @@ async function writeFileContent(filePath, content) {
112
112
  }
113
113
  await (0, import_promises.writeFile)(filePath, content, "utf-8");
114
114
  }
115
+ async function writeExecutableFile(filePath, content) {
116
+ await writeFileContent(filePath, content);
117
+ try {
118
+ (0, import_node_fs3.chmodSync)(filePath, 493);
119
+ } catch {
120
+ }
121
+ }
115
122
  async function readJsonFile(filePath) {
116
123
  const content = await readFileContent(filePath);
117
124
  return JSON.parse(content);
@@ -601,6 +608,7 @@ module.exports = {
601
608
  'perf', 'test', 'chore', 'revert', 'build', 'deps',
602
609
  ],
603
610
  ],
611
+ 'scope-empty': [2, 'never'],
604
612
  'subject-case': [0],
605
613
  },
606
614
  }
@@ -921,13 +929,9 @@ async function setupHusky(cwd, pm, features) {
921
929
  try {
922
930
  const execCmd = getExecCommand(pm);
923
931
  await (0, import_execa2.execa)(execCmd, ["husky", "init"], { cwd, stdio: "pipe" });
924
- const legacyDir = (0, import_node_path4.resolve)(cwd, ".husky/_");
925
- if ((0, import_node_fs4.existsSync)(legacyDir)) {
926
- (0, import_node_fs4.rmSync)(legacyDir, { recursive: true, force: true });
927
- }
928
932
  const commitMsg = `${execCmd} --no-install commitlint --edit "$1"
929
933
  `;
930
- await writeFileContent((0, import_node_path4.resolve)(cwd, ".husky/commit-msg"), commitMsg);
934
+ await writeExecutableFile((0, import_node_path4.resolve)(cwd, ".husky/commit-msg"), commitMsg);
931
935
  const hooks = ["commit-msg"];
932
936
  const needsPreCommit = features.eslint || features.lintStaged || features.oxlint;
933
937
  if (needsPreCommit) {
@@ -940,7 +944,7 @@ async function setupHusky(cwd, pm, features) {
940
944
  } else if (features.eslint) {
941
945
  cmds.push(`${execCmd} eslint . --fix`);
942
946
  }
943
- await writeFileContent(
947
+ await writeExecutableFile(
944
948
  (0, import_node_path4.resolve)(cwd, ".husky/pre-commit"),
945
949
  cmds.join("\n") + "\n"
946
950
  );