@tyyyho/treg 0.1.10 → 0.1.12

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,17 +1,19 @@
1
1
  # @tyyyho/treg
2
2
 
3
+ [繁體中文 README](./README.zh-hant.md)
4
+
3
5
  `treg` is a CLI for quickly setting up project tooling conventions in an existing repository.
4
6
  It applies infra setup such as lint, format, TypeScript, test, husky, and AI skill guidance.
5
7
 
6
8
  ## Quick Start
7
9
 
8
10
  ```bash
9
- pnpm dlx @tyyyho/treg init --framework react
11
+ pnpm dlx @tyyyho/treg init
10
12
  # or
11
- npx @tyyyho/treg init --framework react
13
+ npx @tyyyho/treg init
12
14
  ```
13
15
 
14
- `init` requires `--framework`.
16
+ `init` auto-detects framework from dependencies.
15
17
 
16
18
  ## Commands
17
19
 
@@ -19,22 +21,21 @@ npx @tyyyho/treg init --framework react
19
21
  npx @tyyyho/treg <command> [options]
20
22
  ```
21
23
 
22
- - `init`: Initialize infra rules (requires `--framework`)
24
+ - `init`: Initialize infra rules (framework auto-detected from dependencies)
23
25
  - `add`: Add selected infra features to an existing project
24
26
  - `list`: List supported frameworks, features, and test runners
25
27
 
26
28
  ## Options
27
29
 
28
- - `--framework <node|react|next|vue|svelte|nuxt>`: Target framework
29
- - `--framework-version <major>`: Optional major version hint (react only)
30
+ - `--framework <node|react|next|vue|svelte|nuxt>`: Optional framework override
30
31
  - `--features <lint,format,typescript,test,husky>`: Features to install (defaults to all)
31
32
  - `--dir <path>`: Target directory (defaults to current directory)
32
- - `--test-runner <jest|vitest>`: Test runner when test feature is enabled
33
+ - `--test-runner <jest|vitest>`: Optional test runner override when test feature is enabled
33
34
  - `--pm <pnpm|npm|yarn|auto>`: Package manager (auto-detected by default)
34
35
  - `--force`: Overwrite existing config files
35
36
  - `--dry-run`: Print full plan without writing files
36
37
  - `--skip-husky-install`: Skip husky install command
37
- - `--skills`: Update existing `AGENTS.md`/`CLAUDE.md` with skill guidance (enabled by default)
38
+ - `--skills`: Update existing `CLAUDE.md`/`AGENTS.md`/`GEMINI.md` with skill guidance (enabled by default)
38
39
  - `--no-skills`: Disable skill guidance updates
39
40
  - `--help`: Show help
40
41
 
@@ -50,7 +51,13 @@ Default feature set:
50
51
 
51
52
  ## Examples
52
53
 
53
- Initialize a React project:
54
+ Initialize with auto-detected framework:
55
+
56
+ ```bash
57
+ npx @tyyyho/treg init
58
+ ```
59
+
60
+ Initialize with explicit framework override:
54
61
 
55
62
  ```bash
56
63
  npx @tyyyho/treg init --framework react
@@ -68,12 +75,6 @@ Use Vitest for test feature:
68
75
  npx @tyyyho/treg init --framework node --features test --test-runner vitest
69
76
  ```
70
77
 
71
- Use react major version variant:
72
-
73
- ```bash
74
- npx @tyyyho/treg init --framework react --framework-version 18
75
- ```
76
-
77
78
  Preview changes only:
78
79
 
79
80
  ```bash
@@ -94,6 +95,9 @@ npx @tyyyho/treg init --framework react --dir ./packages/web
94
95
 
95
96
  ## Notes
96
97
 
97
- - `init` requires `--framework`.
98
+ - `init` auto-detects framework from repo dependencies.
99
+ - Detection order is `nuxt -> next -> react -> vue -> svelte -> node`.
100
+ - Default test runner is `vitest` for `vue`/`nuxt`, and `jest` for other frameworks.
98
101
  - `add` lets you install only the features you specify.
102
+ - Framework setup uses one stable config per framework (no `--framework-version` variants).
99
103
  - `--dry-run` prints the full plan and does not write files.
@@ -0,0 +1,103 @@
1
+ # @tyyyho/treg(繁體中文)
2
+
3
+ [English README](./README.md)
4
+
5
+ `treg` 是一個用於既有專案的 CLI 工具,可快速建立與套用工具鏈規範。
6
+ 預設搭建基礎設定,包含 lint、format、TypeScript、test、husky、skills。
7
+
8
+ ## 快速開始
9
+
10
+ ```bash
11
+ pnpm dlx @tyyyho/treg init
12
+ # 或
13
+ npx @tyyyho/treg init
14
+ ```
15
+
16
+ `init` 會依照依賴自動偵測 framework。
17
+
18
+ ## 指令
19
+
20
+ ```bash
21
+ npx @tyyyho/treg <command> [options]
22
+ ```
23
+
24
+ - `init`:初始化基礎規範(依賴自動偵測 framework)
25
+ - `add`:在既有專案中新增指定 feature
26
+ - `list`:列出支援的 framework、feature 與 test runner
27
+
28
+ ## 參數
29
+
30
+ - `--framework <node|react|next|vue|svelte|nuxt>`:可選,手動覆寫 framework
31
+ - `--features <lint,format,typescript,test,husky>`:指定要安裝的 feature(預設全部)
32
+ - `--dir <path>`:指定目標目錄(預設為目前目錄)
33
+ - `--test-runner <jest|vitest>`:可選,啟用 test feature 時覆寫測試框架
34
+ - `--pm <pnpm|npm|yarn|auto>`:套件管理器(預設自動偵測)
35
+ - `--force`:覆寫既有設定檔
36
+ - `--dry-run`:輸出完整執行計畫,但不寫入檔案
37
+ - `--skip-husky-install`:略過 `husky install`
38
+ - `--skills`:更新既有 `CLAUDE.md`/`AGENTS.md`/`GEMINI.md` 的 skill 指引(預設啟用)
39
+ - `--no-skills`:停用 skill 指引更新
40
+ - `--help`:顯示說明
41
+
42
+ ## Features
43
+
44
+ 預設 feature 組合:
45
+
46
+ - `husky`
47
+ - `typescript`
48
+ - `lint`
49
+ - `format`
50
+ - `test`
51
+
52
+ ## 使用範例
53
+
54
+ 依賴自動偵測 framework 初始化:
55
+
56
+ ```bash
57
+ npx @tyyyho/treg init
58
+ ```
59
+
60
+ 手動指定 framework 初始化:
61
+
62
+ ```bash
63
+ npx @tyyyho/treg init --framework react
64
+ ```
65
+
66
+ 只安裝 lint + format:
67
+
68
+ ```bash
69
+ npx @tyyyho/treg add --features lint,format
70
+ ```
71
+
72
+ test feature 使用 Vitest:
73
+
74
+ ```bash
75
+ npx @tyyyho/treg init --framework node --features test --test-runner vitest
76
+ ```
77
+
78
+ 僅預覽變更(不寫檔):
79
+
80
+ ```bash
81
+ npx @tyyyho/treg init --framework react --dry-run
82
+ ```
83
+
84
+ 更新 AI skills 指引:
85
+
86
+ ```bash
87
+ npx @tyyyho/treg add --features lint,format,husky
88
+ ```
89
+
90
+ 明確指定其他目錄:
91
+
92
+ ```bash
93
+ npx @tyyyho/treg init --framework react --dir ./packages/web
94
+ ```
95
+
96
+ ## 注意事項
97
+
98
+ - `init` 會依 repo 依賴自動偵測 framework。
99
+ - 偵測順序:`nuxt -> next -> react -> vue -> svelte -> node`。
100
+ - 預設測試工具為:`vue`/`nuxt` 使用 `vitest`,其他 framework 使用 `jest`。
101
+ - `add` 可只安裝你指定的 features。
102
+ - 每個 framework 僅提供單一穩定設定,不支援 `--framework-version` 版本變體。
103
+ - `--dry-run` 會輸出完整計畫且不寫入任何檔案。
@@ -24,24 +24,23 @@ const ALLOWED_TEST_RUNNERS = ["jest", "vitest"];
24
24
  export const USAGE = `Usage: treg <command> [options]
25
25
 
26
26
  Commands:
27
- init Initialize infra rules in a project (requires --framework)
27
+ init Initialize infra rules in a project (framework auto-detected from dependencies)
28
28
  add Add selected infra features to an existing project
29
29
  list List supported frameworks, features, and test runners
30
30
 
31
31
  Options:
32
32
  --framework <node|react|next|vue|svelte|nuxt>
33
- Target framework
34
- --framework-version <major> Optional framework major version hint
33
+ Optional framework override (default: auto-detected)
35
34
  --features <lint,format,typescript,test,husky>
36
35
  Features to install (all selected by default)
37
36
  --dir <path> Target directory (defaults to current directory)
38
- --test-runner <jest|vitest> Test runner when test feature is enabled
37
+ --test-runner <jest|vitest> Optional test runner override (default: vue/nuxt=vitest, others=jest)
39
38
  --pm <pnpm|npm|yarn|auto> Package manager (auto-detected if omitted)
40
39
  --force Overwrite existing config files
41
40
  --dry-run Print planned changes without writing files
42
41
  --skip-husky-install Do not run husky install
43
- --skills Update AGENTS.md/CLAUDE.md with feature skill guidance (default: enabled)
44
- --no-skills Disable AGENTS.md/CLAUDE.md skill guidance updates
42
+ --skills Update CLAUDE.md/AGENTS.md/GEMINI.md with feature skill guidance (default: enabled)
43
+ --no-skills Disable CLAUDE.md/AGENTS.md/GEMINI.md skill guidance updates
45
44
  -h, --help Show help
46
45
  `;
47
46
  function includes(allowed, value) {
@@ -67,9 +66,8 @@ export function parseArgs(argv) {
67
66
  command: "init",
68
67
  projectDir: null,
69
68
  framework: null,
70
- frameworkVersion: null,
71
69
  features: [],
72
- testRunner: "jest",
70
+ testRunner: null,
73
71
  pm: null,
74
72
  force: false,
75
73
  dryRun: false,
@@ -98,13 +96,6 @@ export function parseArgs(argv) {
98
96
  else if (arg.startsWith("--framework=")) {
99
97
  options.framework = readInlineFlagValue(arg, "--framework");
100
98
  }
101
- else if (arg === "--framework-version") {
102
- options.frameworkVersion = readFlagValue(argv, i, "--framework-version");
103
- i += 1;
104
- }
105
- else if (arg.startsWith("--framework-version=")) {
106
- options.frameworkVersion = readInlineFlagValue(arg, "--framework-version");
107
- }
108
99
  else if (arg === "--features") {
109
100
  options.features.push(...parseCsvValue(argv[i + 1], "--features"));
110
101
  i += 1;
@@ -191,15 +182,7 @@ function validateParsedOptions(options) {
191
182
  if (options.framework && !isFrameworkId(options.framework)) {
192
183
  throw new Error(`Unsupported framework: ${options.framework}`);
193
184
  }
194
- if (options.frameworkVersion && !/^\d+$/.test(options.frameworkVersion)) {
195
- throw new Error("Invalid --framework-version: major version must be numeric");
196
- }
197
- if (options.frameworkVersion &&
198
- options.framework &&
199
- options.framework !== "react") {
200
- throw new Error(`Unsupported --framework-version for framework: ${options.framework}`);
201
- }
202
- if (!isTestRunner(options.testRunner)) {
185
+ if (options.testRunner && !isTestRunner(options.testRunner)) {
203
186
  throw new Error(`Unsupported test runner: ${options.testRunner}`);
204
187
  }
205
188
  for (const feature of options.features) {
@@ -207,9 +190,6 @@ function validateParsedOptions(options) {
207
190
  throw new Error(`Unsupported feature in --features: ${feature}`);
208
191
  }
209
192
  }
210
- if (options.command === "init" && !options.help && !options.framework) {
211
- throw new Error("Missing required option: --framework");
212
- }
213
193
  }
214
194
  export function resolveFeatures(options) {
215
195
  const selected = new Set(options.features.length > 0 ? options.features : ALLOWED_FEATURES);
@@ -226,3 +206,12 @@ export function printSupportedTargets() {
226
206
  console.log("Features: lint, format, typescript, test, husky");
227
207
  console.log("Test runners: jest, vitest");
228
208
  }
209
+ export function resolveTestRunner(frameworkId, testRunner) {
210
+ if (testRunner) {
211
+ return testRunner;
212
+ }
213
+ if (frameworkId === "vue" || frameworkId === "nuxt") {
214
+ return "vitest";
215
+ }
216
+ return "jest";
217
+ }
@@ -1,7 +1,7 @@
1
1
  import { nextFramework } from "./next/index.js";
2
2
  import { nodeFramework } from "./node/index.js";
3
3
  import { nuxtFramework } from "./nuxt/index.js";
4
- import { reactFramework, resolveReactFramework } from "./react/index.js";
4
+ import { reactFramework } from "./react/index.js";
5
5
  import { svelteFramework } from "./svelte/index.js";
6
6
  import { vueFramework } from "./vue/index.js";
7
7
  const FRAMEWORK_REGISTRY = {
@@ -20,18 +20,11 @@ const FRAMEWORK_DETECT_ORDER = [
20
20
  svelteFramework,
21
21
  nodeFramework,
22
22
  ];
23
- export function resolveFramework(frameworkArg, frameworkVersion, packageJson) {
24
- if (frameworkArg === "react") {
25
- return resolveReactFramework(packageJson, frameworkVersion);
26
- }
23
+ export function resolveFramework(frameworkArg, packageJson) {
27
24
  if (frameworkArg) {
28
25
  return FRAMEWORK_REGISTRY[frameworkArg];
29
26
  }
30
- const detected = detectFramework(packageJson);
31
- if (detected.id === "react") {
32
- return resolveReactFramework(packageJson, frameworkVersion);
33
- }
34
- return detected;
27
+ return detectFramework(packageJson);
35
28
  }
36
29
  export function detectFramework(packageJson) {
37
30
  const matched = FRAMEWORK_DETECT_ORDER.find(framework => framework.matches(packageJson));
@@ -1,27 +1,9 @@
1
1
  import { hasPackage } from "../../utils.js";
2
- import { reactV18Framework } from "./v18/index.js";
3
- import { reactV19Framework } from "./v19/index.js";
4
2
  export const reactFramework = {
5
3
  id: "react",
6
- variant: "v19",
7
4
  testEnvironment: "jsdom",
8
5
  tsRequiredExcludes: ["dist", "coverage", "jest.config.js", "public"],
9
6
  matches(packageJson) {
10
7
  return (hasPackage(packageJson, "react") || hasPackage(packageJson, "react-dom"));
11
8
  },
12
9
  };
13
- const REACT_VARIANTS = {
14
- "18": reactV18Framework,
15
- "19": reactV19Framework,
16
- };
17
- export function resolveReactFramework(packageJson, frameworkVersion) {
18
- if (frameworkVersion && REACT_VARIANTS[frameworkVersion]) {
19
- return REACT_VARIANTS[frameworkVersion];
20
- }
21
- const detected = packageJson?.dependencies?.react ?? packageJson?.devDependencies?.react;
22
- const major = typeof detected === "string" ? detected.match(/\d+/)?.[0] : null;
23
- if (major && REACT_VARIANTS[major]) {
24
- return REACT_VARIANTS[major];
25
- }
26
- return reactFramework;
27
- }
@@ -1,7 +1,7 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { promises as fs } from "node:fs";
3
3
  import path from "node:path";
4
- import { parseArgs, printSupportedTargets, resolveFeatures, USAGE, } from "./cli.js";
4
+ import { parseArgs, printSupportedTargets, resolveFeatures, resolveTestRunner, USAGE, } from "./cli.js";
5
5
  import { resolveFramework } from "./frameworks/index.js";
6
6
  import { runFeatureRules } from "./mrm-rules/index.js";
7
7
  import { detectPackageManager, runScript } from "./package-manager.js";
@@ -37,25 +37,22 @@ export async function main(argv = process.argv.slice(2)) {
37
37
  const pm = !options.pm || options.pm === "auto"
38
38
  ? detectPackageManager(projectDir)
39
39
  : options.pm;
40
- const framework = resolveFramework(options.framework, options.frameworkVersion, packageJson);
41
- if (options.frameworkVersion && framework.id !== "react") {
42
- console.error(`Unsupported --framework-version for framework: ${framework.id}`);
43
- process.exitCode = 1;
44
- return;
45
- }
40
+ const framework = resolveFramework(options.framework, packageJson);
41
+ const testRunner = resolveTestRunner(framework.id, options.testRunner);
46
42
  const enabledFeatures = resolveFeatures(options);
47
43
  const context = {
48
44
  ...options,
45
+ testRunner,
49
46
  projectDir,
50
47
  pm,
51
48
  framework,
52
49
  enabledFeatures,
53
50
  };
54
51
  console.log(formatStep(1, TOTAL_STEPS, "Resolve plan", options.dryRun));
55
- console.log(`${options.dryRun ? "[dry-run] " : ""}Framework=${framework.id}${framework.variant ? `/${framework.variant}` : ""}, features=${Object.entries(enabledFeatures)
52
+ console.log(`${options.dryRun ? "[dry-run] " : ""}Framework=${framework.id}, features=${Object.entries(enabledFeatures)
56
53
  .filter(([, enabled]) => enabled)
57
54
  .map(([name]) => name)
58
- .join(", ")}, testRunner=${options.testRunner}`);
55
+ .join(", ")}, testRunner=${testRunner}`);
59
56
  console.log(formatStep(2, TOTAL_STEPS, "Run mrm rules", options.dryRun));
60
57
  await runFeatureRules(context);
61
58
  console.log(formatStep(3, TOTAL_STEPS, "Finalize", options.dryRun));
@@ -3,68 +3,70 @@ import { promises as fs } from "node:fs";
3
3
  import path from "node:path";
4
4
  const START_MARKER = "<!-- treg:skills:start -->";
5
5
  const END_MARKER = "<!-- treg:skills:end -->";
6
+ const SKILL_SECTION_HEADING = "## treg AI Skills";
6
7
  const SKILLS_BASE_DIR = "skills";
8
+ const SKILLS_DOC_CANDIDATES = ["CLAUDE.md", "AGENTS.md", "GEMINI.md"];
7
9
  const FEATURE_SKILLS = {
8
10
  format: {
9
11
  name: "treg/format",
10
12
  description: "Run and verify formatting rules.",
11
- when: "在提交前或大範圍改動後,統一格式化程式碼。",
12
- checklist: ["執行 format", "執行 format:check", "確認未變動非目標檔案"],
13
+ when: "Before committing or after broad edits, normalize formatting across the codebase.",
14
+ checklist: [
15
+ "Run `format`.",
16
+ "Run `format:check`.",
17
+ "Confirm only intended files were changed.",
18
+ ],
13
19
  },
14
20
  husky: {
15
21
  name: "treg/husky",
16
22
  description: "Verify and maintain git hook automation.",
17
- when: "需要保證 pre-commit / pre-push 自動檢查時。",
23
+ when: "When pre-commit and pre-push checks must stay enforced and consistent.",
18
24
  checklist: [
19
- "確認 hooks 可執行",
20
- "確認含 format:check lint:check",
21
- "若啟用型別/測試,也要納入 hooks",
25
+ "Ensure hooks are executable.",
26
+ "Ensure hooks include `format:check` and `lint:check`.",
27
+ "If type-checking or tests are enabled, ensure those checks are included.",
22
28
  ],
23
29
  },
24
30
  lint: {
25
31
  name: "treg/lint",
26
32
  description: "Run and validate lint rules.",
27
- when: "新增規則或調整工具鏈後,驗證 lint 一致性。",
28
- checklist: ["執行 lint", "執行 lint:check", "修正 max-warnings 問題"],
33
+ when: "After adding rules or changing tooling, verify lint consistency.",
34
+ checklist: [
35
+ "Run `lint`.",
36
+ "Run `lint:check`.",
37
+ "Fix max-warnings and remaining lint violations.",
38
+ ],
29
39
  },
30
40
  test: {
31
41
  name: "treg/test",
32
42
  description: "Validate test runner setup and execution.",
33
- when: "新增測試規則或調整測試設定時。",
43
+ when: "When test rules are added or test configuration changes.",
34
44
  checklist: [
35
- "確認 test runner 與專案一致",
36
- "執行 test",
37
- "視需要執行 test:coverage",
45
+ "Confirm the selected test runner matches the project setup.",
46
+ "Run `test`.",
47
+ "Run `test:coverage` when coverage validation is needed.",
38
48
  ],
39
49
  },
40
50
  typescript: {
41
51
  name: "treg/typescript",
42
52
  description: "Validate TypeScript strictness and config.",
43
- when: "調整 tsconfig 或型別嚴格度規則時。",
53
+ when: "When tsconfig or strict typing rules are changed.",
44
54
  checklist: [
45
- "執行 type-check",
46
- "確認 strict 相關選項仍生效",
47
- "檢查 exclude 不含產品邏輯路徑",
55
+ "Run `type:check`.",
56
+ "Confirm strict compiler options remain enabled.",
57
+ "Ensure `exclude` does not hide product-logic paths.",
48
58
  ],
49
59
  },
50
60
  };
51
61
  const FEATURE_STEP_LABELS = {
52
- format: "格式處理",
53
- husky: "Git hook 維護",
54
- lint: "Lint 規則檢查",
55
- test: "測試規則調整",
56
- typescript: "TypeScript 型別與設定",
62
+ format: "Formatting",
63
+ husky: "Git Hook Maintenance",
64
+ lint: "Lint Validation",
65
+ test: "Test Configuration",
66
+ typescript: "TypeScript Settings",
57
67
  };
58
- function resolveSkillsDoc(projectDir) {
59
- const agentsPath = path.join(projectDir, "AGENTS.md");
60
- if (existsSync(agentsPath)) {
61
- return agentsPath;
62
- }
63
- const claudePath = path.join(projectDir, "CLAUDE.md");
64
- if (existsSync(claudePath)) {
65
- return claudePath;
66
- }
67
- return null;
68
+ function resolveSkillsDocs(projectDir) {
69
+ return SKILLS_DOC_CANDIDATES.map(fileName => path.join(projectDir, fileName)).filter(existsSync);
68
70
  }
69
71
  function getEnabledFeatures(enabledFeatures) {
70
72
  return Object.entries(enabledFeatures)
@@ -121,17 +123,9 @@ async function ensureSkillFiles(projectDir, enabled, testRunner, dryRun) {
121
123
  function buildSkillSection(context) {
122
124
  const { enabledFeatures, testRunner } = context;
123
125
  const enabled = getEnabledFeatures(enabledFeatures);
124
- const lines = [
125
- START_MARKER,
126
- "## treg AI Skills",
127
- "",
128
- "### 執行步驟與 Skill 對應",
129
- "",
130
- ];
126
+ const lines = [SKILL_SECTION_HEADING, "", "### Steps and Skill Mapping", ""];
131
127
  if (enabled.length === 0) {
132
- lines.push("1. 本次未啟用任何 feature,無需呼叫 skill");
133
- lines.push("");
134
- lines.push(END_MARKER);
128
+ lines.push("1. No features are enabled in this run, so no skill call is required.");
135
129
  lines.push("");
136
130
  return lines.join("\n");
137
131
  }
@@ -141,25 +135,32 @@ function buildSkillSection(context) {
141
135
  return;
142
136
  const skillRelativePath = getSkillRelativePath(feature);
143
137
  const stepLabel = FEATURE_STEP_LABELS[feature] ?? feature;
144
- lines.push(`${index + 1}. ${stepLabel}:呼叫 [${skill.name}](${skillRelativePath})`);
138
+ lines.push(`${index + 1}. ${stepLabel}: use [${skill.name}](${skillRelativePath})`);
145
139
  if (feature === "test") {
146
- lines.push(` - 目前測試工具:\`${testRunner}\``);
140
+ lines.push(` - Current test runner: \`${testRunner}\``);
147
141
  }
148
142
  });
149
143
  lines.push("");
150
- lines.push(END_MARKER);
151
- lines.push("");
152
144
  return lines.join("\n");
153
145
  }
154
146
  function upsertSkillSection(content, nextSection) {
147
+ const replaceSection = (start, end) => {
148
+ const before = content.slice(0, start).trimEnd();
149
+ const after = content.slice(end).trimStart();
150
+ const rebuilt = `${before}\n\n${nextSection.trim()}\n`;
151
+ return after ? `${rebuilt}\n${after}\n` : `${rebuilt}`;
152
+ };
155
153
  const start = content.indexOf(START_MARKER);
156
154
  const end = content.indexOf(END_MARKER);
157
155
  if (start !== -1 && end !== -1 && end > start) {
158
156
  const suffixStart = end + END_MARKER.length;
159
- const before = content.slice(0, start).trimEnd();
160
- const after = content.slice(suffixStart).trimStart();
161
- const rebuilt = `${before}\n\n${nextSection.trim()}\n`;
162
- return after ? `${rebuilt}\n${after}\n` : `${rebuilt}`;
157
+ return replaceSection(start, suffixStart);
158
+ }
159
+ const headingStart = content.indexOf(SKILL_SECTION_HEADING);
160
+ if (headingStart !== -1) {
161
+ const nextHeading = content.indexOf("\n## ", headingStart + 1);
162
+ const sectionEnd = nextHeading === -1 ? content.length : nextHeading + 1;
163
+ return replaceSection(headingStart, sectionEnd);
163
164
  }
164
165
  if (!content.trim()) {
165
166
  return `${nextSection.trim()}\n`;
@@ -168,26 +169,28 @@ function upsertSkillSection(content, nextSection) {
168
169
  }
169
170
  export async function runAiSkillsRule(context) {
170
171
  const { projectDir, dryRun } = context;
171
- const targetFile = resolveSkillsDoc(projectDir);
172
- if (!targetFile) {
173
- console.log("Skip ai-skills (AGENTS.md/CLAUDE.md not found)");
172
+ const targetFiles = resolveSkillsDocs(projectDir);
173
+ if (targetFiles.length === 0) {
174
+ console.log("Skip ai-skills (CLAUDE.md/AGENTS.md/GEMINI.md not found)");
174
175
  return;
175
176
  }
176
177
  const enabled = getEnabledFeatures(context.enabledFeatures);
177
178
  await ensureSkillFiles(projectDir, enabled, context.testRunner, dryRun);
178
179
  const section = buildSkillSection(context);
179
- const current = await fs.readFile(targetFile, "utf8");
180
- const updated = upsertSkillSection(current, section);
181
- if (dryRun) {
182
- console.log(`[dry-run] Would update ${path.basename(targetFile)} with AI skill guidance`);
183
- return;
184
- }
185
- if (updated !== current) {
186
- await fs.writeFile(targetFile, updated, "utf8");
187
- console.log(`Updated ${path.basename(targetFile)} with AI skill guidance`);
188
- return;
180
+ for (const targetFile of targetFiles) {
181
+ if (dryRun) {
182
+ console.log(`[dry-run] Would update ${path.basename(targetFile)} with AI skill guidance`);
183
+ continue;
184
+ }
185
+ const current = await fs.readFile(targetFile, "utf8");
186
+ const updated = upsertSkillSection(current, section);
187
+ if (updated !== current) {
188
+ await fs.writeFile(targetFile, updated, "utf8");
189
+ console.log(`Updated ${path.basename(targetFile)} with AI skill guidance`);
190
+ continue;
191
+ }
192
+ console.log(`${path.basename(targetFile)} already contains latest AI skill guidance`);
189
193
  }
190
- console.log(`${path.basename(targetFile)} already contains latest AI skill guidance`);
191
194
  }
192
195
  export const __testables__ = {
193
196
  buildSkillSection,
@@ -195,6 +198,6 @@ export const __testables__ = {
195
198
  ensureSkillFiles,
196
199
  getEnabledFeatures,
197
200
  getSkillRelativePath,
198
- resolveSkillsDoc,
201
+ resolveSkillsDocs,
199
202
  upsertSkillSection,
200
203
  };
@@ -10,7 +10,7 @@ function buildHookCommands(runner, enabledFeatures) {
10
10
  `${runner} lint:check || exit 1`,
11
11
  ];
12
12
  if (enabledFeatures.typescript) {
13
- preCommit.push(`${runner} type-check || exit 1`);
13
+ preCommit.push(`${runner} type:check || exit 1`);
14
14
  }
15
15
  const prePush = [...preCommit];
16
16
  if (enabledFeatures.test) {
@@ -16,7 +16,7 @@ export async function runTypescriptRule(context) {
16
16
  withProjectCwd(projectDir, () => {
17
17
  if (dryRun) {
18
18
  console.log("[dry-run] Would update tsconfig.json");
19
- console.log("[dry-run] Would set package script: type-check");
19
+ console.log("[dry-run] Would set package script: type:check");
20
20
  return;
21
21
  }
22
22
  const tsconfig = json("tsconfig.json", {
@@ -35,6 +35,6 @@ export async function runTypescriptRule(context) {
35
35
  .set("compilerOptions", mergedCompilerOptions)
36
36
  .set("exclude", Array.from(exclude))
37
37
  .save();
38
- packageJson().setScript("type-check", "tsc --noEmit").save();
38
+ packageJson().setScript("type:check", "tsc --noEmit").save();
39
39
  });
40
40
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tyyyho/treg",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "CLI tool for initializing development conventions in existing projects.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -27,11 +27,11 @@
27
27
  "lint:check": "eslint . --max-warnings 0",
28
28
  "format": "prettier --write .",
29
29
  "format:check": "prettier --check .",
30
- "type-check": "tsc --noEmit",
30
+ "type:check": "tsc --noEmit",
31
31
  "build": "tsc -p tsconfig.build.json && node scripts/write-dist-package-json.mjs",
32
32
  "release": "node scripts/release.mjs",
33
33
  "prepare": "husky",
34
- "prepublishOnly": "pnpm format:check && pnpm lint:check && pnpm type-check && pnpm test && pnpm build"
34
+ "prepublishOnly": "pnpm format:check && pnpm lint:check && pnpm type:check && pnpm test && pnpm build"
35
35
  },
36
36
  "dependencies": {
37
37
  "mrm-core": "^7.1.22"
@@ -1,6 +0,0 @@
1
- export const reactV18Framework = {
2
- id: "react",
3
- variant: "v18",
4
- testEnvironment: "jsdom",
5
- tsRequiredExcludes: ["dist", "coverage", "jest.config.js", "public"],
6
- };
@@ -1,6 +0,0 @@
1
- export const reactV19Framework = {
2
- id: "react",
3
- variant: "v19",
4
- testEnvironment: "jsdom",
5
- tsRequiredExcludes: ["dist", "coverage", "jest.config.js", "public"],
6
- };