@tyyyho/treg 0.1.11 → 0.1.13
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 +23 -4
- package/README.zh-hant.md +23 -4
- package/dist/init-project/cli.js +36 -3
- package/dist/init-project/index.js +1 -1
- package/dist/init-project/mrm-rules/ai-skills.js +67 -64
- package/dist/init-project/mrm-rules/format.js +21 -6
- package/dist/init-project/mrm-rules/husky.js +1 -1
- package/dist/init-project/mrm-rules/typescript.js +2 -2
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -8,11 +8,13 @@ It applies infra setup such as lint, format, TypeScript, test, husky, and AI ski
|
|
|
8
8
|
## Quick Start
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
|
-
pnpm dlx @tyyyho/treg init
|
|
12
|
-
# or
|
|
13
11
|
npx @tyyyho/treg init
|
|
14
12
|
```
|
|
15
13
|
|
|
14
|
+
```bash
|
|
15
|
+
pnpm dlx @tyyyho/treg init
|
|
16
|
+
```
|
|
17
|
+
|
|
16
18
|
`init` auto-detects framework from dependencies.
|
|
17
19
|
|
|
18
20
|
## Commands
|
|
@@ -23,19 +25,22 @@ npx @tyyyho/treg <command> [options]
|
|
|
23
25
|
|
|
24
26
|
- `init`: Initialize infra rules (framework auto-detected from dependencies)
|
|
25
27
|
- `add`: Add selected infra features to an existing project
|
|
26
|
-
- `list`: List supported frameworks, features, and test runners
|
|
28
|
+
- `list`: List supported frameworks, features, formatters, and test runners
|
|
27
29
|
|
|
28
30
|
## Options
|
|
29
31
|
|
|
30
32
|
- `--framework <node|react|next|vue|svelte|nuxt>`: Optional framework override
|
|
31
33
|
- `--features <lint,format,typescript,test,husky>`: Features to install (defaults to all)
|
|
34
|
+
- `--no-format`: Skip format feature setup and avoid changing format configs/scripts
|
|
35
|
+
- `--no-test-runner`: Skip test feature setup and avoid changing test runner/config
|
|
32
36
|
- `--dir <path>`: Target directory (defaults to current directory)
|
|
37
|
+
- `--formatter <prettier|oxfmt>`: Formatter for format feature (default: `prettier`)
|
|
33
38
|
- `--test-runner <jest|vitest>`: Optional test runner override when test feature is enabled
|
|
34
39
|
- `--pm <pnpm|npm|yarn|auto>`: Package manager (auto-detected by default)
|
|
35
40
|
- `--force`: Overwrite existing config files
|
|
36
41
|
- `--dry-run`: Print full plan without writing files
|
|
37
42
|
- `--skip-husky-install`: Skip husky install command
|
|
38
|
-
- `--skills`: Update existing `AGENTS.md`/`
|
|
43
|
+
- `--skills`: Update existing `CLAUDE.md`/`AGENTS.md`/`GEMINI.md` with skill guidance (enabled by default)
|
|
39
44
|
- `--no-skills`: Disable skill guidance updates
|
|
40
45
|
- `--help`: Show help
|
|
41
46
|
|
|
@@ -69,6 +74,18 @@ Add only lint + format:
|
|
|
69
74
|
npx @tyyyho/treg add --features lint,format
|
|
70
75
|
```
|
|
71
76
|
|
|
77
|
+
Use oxfmt instead of prettier:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
npx @tyyyho/treg add --features format --formatter oxfmt
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Skip format/test setup to keep existing project rules untouched:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npx @tyyyho/treg add --no-format --no-test-runner
|
|
87
|
+
```
|
|
88
|
+
|
|
72
89
|
Use Vitest for test feature:
|
|
73
90
|
|
|
74
91
|
```bash
|
|
@@ -98,6 +115,8 @@ npx @tyyyho/treg init --framework react --dir ./packages/web
|
|
|
98
115
|
- `init` auto-detects framework from repo dependencies.
|
|
99
116
|
- Detection order is `nuxt -> next -> react -> vue -> svelte -> node`.
|
|
100
117
|
- Default test runner is `vitest` for `vue`/`nuxt`, and `jest` for other frameworks.
|
|
118
|
+
- Default formatter is `prettier` (`--formatter oxfmt` to override).
|
|
119
|
+
- `--no-format` and `--no-test-runner` let you skip format/test setup to avoid overriding existing project config.
|
|
101
120
|
- `add` lets you install only the features you specify.
|
|
102
121
|
- Framework setup uses one stable config per framework (no `--framework-version` variants).
|
|
103
122
|
- `--dry-run` prints the full plan and does not write files.
|
package/README.zh-hant.md
CHANGED
|
@@ -8,11 +8,13 @@
|
|
|
8
8
|
## 快速開始
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
|
-
pnpm dlx @tyyyho/treg init
|
|
12
|
-
# 或
|
|
13
11
|
npx @tyyyho/treg init
|
|
14
12
|
```
|
|
15
13
|
|
|
14
|
+
```bash
|
|
15
|
+
pnpm dlx @tyyyho/treg init
|
|
16
|
+
```
|
|
17
|
+
|
|
16
18
|
`init` 會依照依賴自動偵測 framework。
|
|
17
19
|
|
|
18
20
|
## 指令
|
|
@@ -23,19 +25,22 @@ npx @tyyyho/treg <command> [options]
|
|
|
23
25
|
|
|
24
26
|
- `init`:初始化基礎規範(依賴自動偵測 framework)
|
|
25
27
|
- `add`:在既有專案中新增指定 feature
|
|
26
|
-
- `list`:列出支援的 framework、feature 與 test runner
|
|
28
|
+
- `list`:列出支援的 framework、feature、formatter 與 test runner
|
|
27
29
|
|
|
28
30
|
## 參數
|
|
29
31
|
|
|
30
32
|
- `--framework <node|react|next|vue|svelte|nuxt>`:可選,手動覆寫 framework
|
|
31
33
|
- `--features <lint,format,typescript,test,husky>`:指定要安裝的 feature(預設全部)
|
|
34
|
+
- `--no-format`:略過 format feature,避免覆寫既有格式化設定與 scripts
|
|
35
|
+
- `--no-test-runner`:略過 test feature,避免覆寫既有測試 runner 與設定
|
|
32
36
|
- `--dir <path>`:指定目標目錄(預設為目前目錄)
|
|
37
|
+
- `--formatter <prettier|oxfmt>`:format feature 使用的 formatter(預設為 `prettier`)
|
|
33
38
|
- `--test-runner <jest|vitest>`:可選,啟用 test feature 時覆寫測試框架
|
|
34
39
|
- `--pm <pnpm|npm|yarn|auto>`:套件管理器(預設自動偵測)
|
|
35
40
|
- `--force`:覆寫既有設定檔
|
|
36
41
|
- `--dry-run`:輸出完整執行計畫,但不寫入檔案
|
|
37
42
|
- `--skip-husky-install`:略過 `husky install`
|
|
38
|
-
- `--skills`:更新既有 `AGENTS.md`/`
|
|
43
|
+
- `--skills`:更新既有 `CLAUDE.md`/`AGENTS.md`/`GEMINI.md` 的 skill 指引(預設啟用)
|
|
39
44
|
- `--no-skills`:停用 skill 指引更新
|
|
40
45
|
- `--help`:顯示說明
|
|
41
46
|
|
|
@@ -69,6 +74,18 @@ npx @tyyyho/treg init --framework react
|
|
|
69
74
|
npx @tyyyho/treg add --features lint,format
|
|
70
75
|
```
|
|
71
76
|
|
|
77
|
+
改用 oxfmt(不使用 prettier):
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
npx @tyyyho/treg add --features format --formatter oxfmt
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
若要保留既有設定,可跳過 format/test 安裝:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npx @tyyyho/treg add --no-format --no-test-runner
|
|
87
|
+
```
|
|
88
|
+
|
|
72
89
|
test feature 使用 Vitest:
|
|
73
90
|
|
|
74
91
|
```bash
|
|
@@ -98,6 +115,8 @@ npx @tyyyho/treg init --framework react --dir ./packages/web
|
|
|
98
115
|
- `init` 會依 repo 依賴自動偵測 framework。
|
|
99
116
|
- 偵測順序:`nuxt -> next -> react -> vue -> svelte -> node`。
|
|
100
117
|
- 預設測試工具為:`vue`/`nuxt` 使用 `vitest`,其他 framework 使用 `jest`。
|
|
118
|
+
- 預設 formatter 為 `prettier`(可用 `--formatter oxfmt` 覆寫)。
|
|
119
|
+
- 可透過 `--no-format` 與 `--no-test-runner` 跳過對 format/test 的設定,避免覆寫既有專案規則。
|
|
101
120
|
- `add` 可只安裝你指定的 features。
|
|
102
121
|
- 每個 framework 僅提供單一穩定設定,不支援 `--framework-version` 版本變體。
|
|
103
122
|
- `--dry-run` 會輸出完整計畫且不寫入任何檔案。
|
package/dist/init-project/cli.js
CHANGED
|
@@ -21,26 +21,30 @@ const ALLOWED_FEATURES = [
|
|
|
21
21
|
"husky",
|
|
22
22
|
];
|
|
23
23
|
const ALLOWED_TEST_RUNNERS = ["jest", "vitest"];
|
|
24
|
+
const ALLOWED_FORMATTERS = ["prettier", "oxfmt"];
|
|
24
25
|
export const USAGE = `Usage: treg <command> [options]
|
|
25
26
|
|
|
26
27
|
Commands:
|
|
27
28
|
init Initialize infra rules in a project (framework auto-detected from dependencies)
|
|
28
29
|
add Add selected infra features to an existing project
|
|
29
|
-
list List supported frameworks, features, and test runners
|
|
30
|
+
list List supported frameworks, features, formatters, and test runners
|
|
30
31
|
|
|
31
32
|
Options:
|
|
32
33
|
--framework <node|react|next|vue|svelte|nuxt>
|
|
33
34
|
Optional framework override (default: auto-detected)
|
|
34
35
|
--features <lint,format,typescript,test,husky>
|
|
35
36
|
Features to install (all selected by default)
|
|
37
|
+
--no-format Skip format feature setup and avoid changing format config/scripts
|
|
38
|
+
--no-test-runner Skip test feature setup and avoid changing test runner/config
|
|
36
39
|
--dir <path> Target directory (defaults to current directory)
|
|
40
|
+
--formatter <prettier|oxfmt> Formatter for format feature (default: prettier)
|
|
37
41
|
--test-runner <jest|vitest> Optional test runner override (default: vue/nuxt=vitest, others=jest)
|
|
38
42
|
--pm <pnpm|npm|yarn|auto> Package manager (auto-detected if omitted)
|
|
39
43
|
--force Overwrite existing config files
|
|
40
44
|
--dry-run Print planned changes without writing files
|
|
41
45
|
--skip-husky-install Do not run husky install
|
|
42
|
-
--skills Update AGENTS.md/
|
|
43
|
-
--no-skills Disable AGENTS.md/
|
|
46
|
+
--skills Update CLAUDE.md/AGENTS.md/GEMINI.md with feature skill guidance (default: enabled)
|
|
47
|
+
--no-skills Disable CLAUDE.md/AGENTS.md/GEMINI.md skill guidance updates
|
|
44
48
|
-h, --help Show help
|
|
45
49
|
`;
|
|
46
50
|
function includes(allowed, value) {
|
|
@@ -61,16 +65,22 @@ function isFeatureName(value) {
|
|
|
61
65
|
function isTestRunner(value) {
|
|
62
66
|
return includes(ALLOWED_TEST_RUNNERS, value);
|
|
63
67
|
}
|
|
68
|
+
function isFormatter(value) {
|
|
69
|
+
return includes(ALLOWED_FORMATTERS, value);
|
|
70
|
+
}
|
|
64
71
|
export function parseArgs(argv) {
|
|
65
72
|
const options = {
|
|
66
73
|
command: "init",
|
|
67
74
|
projectDir: null,
|
|
68
75
|
framework: null,
|
|
76
|
+
formatter: "prettier",
|
|
69
77
|
features: [],
|
|
70
78
|
testRunner: null,
|
|
71
79
|
pm: null,
|
|
72
80
|
force: false,
|
|
73
81
|
dryRun: false,
|
|
82
|
+
noFormat: false,
|
|
83
|
+
noTestRunner: false,
|
|
74
84
|
skipHuskyInstall: false,
|
|
75
85
|
skills: true,
|
|
76
86
|
help: false,
|
|
@@ -110,6 +120,13 @@ export function parseArgs(argv) {
|
|
|
110
120
|
else if (arg.startsWith("--dir=")) {
|
|
111
121
|
options.projectDir = readInlineFlagValue(arg, "--dir");
|
|
112
122
|
}
|
|
123
|
+
else if (arg === "--formatter") {
|
|
124
|
+
options.formatter = readFlagValue(argv, i, "--formatter");
|
|
125
|
+
i += 1;
|
|
126
|
+
}
|
|
127
|
+
else if (arg.startsWith("--formatter=")) {
|
|
128
|
+
options.formatter = readInlineFlagValue(arg, "--formatter");
|
|
129
|
+
}
|
|
113
130
|
else if (arg === "--test-runner") {
|
|
114
131
|
options.testRunner = readFlagValue(argv, i, "--test-runner");
|
|
115
132
|
i += 1;
|
|
@@ -130,6 +147,12 @@ export function parseArgs(argv) {
|
|
|
130
147
|
else if (arg === "--dry-run") {
|
|
131
148
|
options.dryRun = true;
|
|
132
149
|
}
|
|
150
|
+
else if (arg === "--no-format") {
|
|
151
|
+
options.noFormat = true;
|
|
152
|
+
}
|
|
153
|
+
else if (arg === "--no-test-runner") {
|
|
154
|
+
options.noTestRunner = true;
|
|
155
|
+
}
|
|
133
156
|
else if (arg === "--skip-husky-install") {
|
|
134
157
|
options.skipHuskyInstall = true;
|
|
135
158
|
}
|
|
@@ -182,6 +205,9 @@ function validateParsedOptions(options) {
|
|
|
182
205
|
if (options.framework && !isFrameworkId(options.framework)) {
|
|
183
206
|
throw new Error(`Unsupported framework: ${options.framework}`);
|
|
184
207
|
}
|
|
208
|
+
if (!isFormatter(options.formatter)) {
|
|
209
|
+
throw new Error(`Unsupported formatter: ${options.formatter}`);
|
|
210
|
+
}
|
|
185
211
|
if (options.testRunner && !isTestRunner(options.testRunner)) {
|
|
186
212
|
throw new Error(`Unsupported test runner: ${options.testRunner}`);
|
|
187
213
|
}
|
|
@@ -193,6 +219,12 @@ function validateParsedOptions(options) {
|
|
|
193
219
|
}
|
|
194
220
|
export function resolveFeatures(options) {
|
|
195
221
|
const selected = new Set(options.features.length > 0 ? options.features : ALLOWED_FEATURES);
|
|
222
|
+
if (options.noFormat) {
|
|
223
|
+
selected.delete("format");
|
|
224
|
+
}
|
|
225
|
+
if (options.noTestRunner) {
|
|
226
|
+
selected.delete("test");
|
|
227
|
+
}
|
|
196
228
|
return {
|
|
197
229
|
lint: selected.has("lint"),
|
|
198
230
|
format: selected.has("format"),
|
|
@@ -204,6 +236,7 @@ export function resolveFeatures(options) {
|
|
|
204
236
|
export function printSupportedTargets() {
|
|
205
237
|
console.log("Frameworks: node, react, next, vue, svelte, nuxt");
|
|
206
238
|
console.log("Features: lint, format, typescript, test, husky");
|
|
239
|
+
console.log("Formatters: prettier, oxfmt");
|
|
207
240
|
console.log("Test runners: jest, vitest");
|
|
208
241
|
}
|
|
209
242
|
export function resolveTestRunner(frameworkId, testRunner) {
|
|
@@ -52,7 +52,7 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
52
52
|
console.log(`${options.dryRun ? "[dry-run] " : ""}Framework=${framework.id}, features=${Object.entries(enabledFeatures)
|
|
53
53
|
.filter(([, enabled]) => enabled)
|
|
54
54
|
.map(([name]) => name)
|
|
55
|
-
.join(", ")}, testRunner=${testRunner}`);
|
|
55
|
+
.join(", ")}, formatter=${options.formatter}, testRunner=${testRunner}`);
|
|
56
56
|
console.log(formatStep(2, TOTAL_STEPS, "Run mrm rules", options.dryRun));
|
|
57
57
|
await runFeatureRules(context);
|
|
58
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: [
|
|
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: "
|
|
23
|
+
when: "When pre-commit and pre-push checks must stay enforced and consistent.",
|
|
18
24
|
checklist: [
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
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: "
|
|
28
|
-
checklist: [
|
|
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
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
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: "
|
|
53
|
+
when: "When tsconfig or strict typing rules are changed.",
|
|
44
54
|
checklist: [
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
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
|
|
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
|
|
59
|
-
|
|
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.
|
|
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}
|
|
138
|
+
lines.push(`${index + 1}. ${stepLabel}: use [${skill.name}](${skillRelativePath})`);
|
|
145
139
|
if (feature === "test") {
|
|
146
|
-
lines.push(` -
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
|
172
|
-
if (
|
|
173
|
-
console.log("Skip ai-skills (AGENTS.md/
|
|
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
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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
|
-
|
|
201
|
+
resolveSkillsDocs,
|
|
199
202
|
upsertSkillSection,
|
|
200
203
|
};
|
|
@@ -25,20 +25,35 @@ const PRETTIER_IGNORE = [
|
|
|
25
25
|
"package-lock.json",
|
|
26
26
|
"yarn.lock",
|
|
27
27
|
];
|
|
28
|
+
function getFormatterScripts(formatter) {
|
|
29
|
+
if (formatter === "oxfmt") {
|
|
30
|
+
return {
|
|
31
|
+
format: "oxfmt --write .",
|
|
32
|
+
formatCheck: "oxfmt --check .",
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
format: "prettier --write .",
|
|
37
|
+
formatCheck: "prettier --check .",
|
|
38
|
+
};
|
|
39
|
+
}
|
|
28
40
|
export async function runFormatRule(context) {
|
|
29
|
-
const { projectDir, pm, force, dryRun } = context;
|
|
30
|
-
|
|
31
|
-
|
|
41
|
+
const { projectDir, pm, force, dryRun, formatter } = context;
|
|
42
|
+
const { format, formatCheck } = getFormatterScripts(formatter);
|
|
43
|
+
installPackages(projectDir, pm, [formatter], true, dryRun);
|
|
44
|
+
if (formatter === "prettier") {
|
|
45
|
+
await writeFile(projectDir, ".prettierrc.json", PRETTIER_CONFIG, force, dryRun);
|
|
46
|
+
}
|
|
32
47
|
withProjectCwd(projectDir, () => {
|
|
33
48
|
if (dryRun) {
|
|
34
49
|
console.log("[dry-run] Would update .prettierignore");
|
|
35
|
-
console.log(
|
|
50
|
+
console.log(`[dry-run] Would set package scripts: format (${format}), format:check (${formatCheck})`);
|
|
36
51
|
return;
|
|
37
52
|
}
|
|
38
53
|
lines(".prettierignore").add(PRETTIER_IGNORE).save();
|
|
39
54
|
packageJson()
|
|
40
|
-
.setScript("format",
|
|
41
|
-
.setScript("format:check",
|
|
55
|
+
.setScript("format", format)
|
|
56
|
+
.setScript("format:check", formatCheck)
|
|
42
57
|
.save();
|
|
43
58
|
});
|
|
44
59
|
}
|
|
@@ -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
|
|
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
|
|
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
|
|
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.
|
|
3
|
+
"version": "0.1.13",
|
|
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
|
|
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
|
|
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"
|