@tyyyho/treg 0.1.15 → 0.1.17
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 +43 -96
- package/README.npm.md +69 -0
- package/README.zh-hant.md +87 -68
- package/dist/init-project/cli.js +18 -52
- package/dist/init-project/index.js +25 -7
- package/dist/init-project/init-prompts.js +221 -0
- package/dist/init-project/mrm-rules/ai-skills.js +13 -6
- package/dist/init-project/mrm-rules/husky.js +4 -0
- package/dist/init-project/package-manager.js +13 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,122 +1,69 @@
|
|
|
1
1
|
# @tyyyho/treg
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`treg` applies tooling standards to existing repositories.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
It applies infra setup such as lint, format, TypeScript, test, husky, and AI skill guidance.
|
|
5
|
+
Scope:
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
- lint
|
|
8
|
+
- format
|
|
9
|
+
- TypeScript
|
|
10
|
+
- test
|
|
11
|
+
- husky
|
|
12
|
+
- AI skill guidance
|
|
9
13
|
|
|
10
|
-
|
|
11
|
-
npx @tyyyho/treg init
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
```bash
|
|
15
|
-
pnpm dlx @tyyyho/treg init
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
`init` auto-detects framework from dependencies.
|
|
19
|
-
|
|
20
|
-
## Commands
|
|
21
|
-
|
|
22
|
-
```bash
|
|
23
|
-
npx @tyyyho/treg <command> [options]
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
- `init`: Initialize infra rules (framework auto-detected from dependencies)
|
|
27
|
-
- `add`: Add selected infra features to an existing project
|
|
28
|
-
- `list`: List supported frameworks, features, formatters, and test runners
|
|
29
|
-
|
|
30
|
-
## Options
|
|
31
|
-
|
|
32
|
-
- `--framework <node|react|next|vue|svelte|nuxt>`: Optional framework override
|
|
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
|
|
36
|
-
- `--dir <path>`: Target directory (defaults to current directory)
|
|
37
|
-
- `--formatter <prettier|oxfmt>`: Formatter for format feature (default: `prettier`)
|
|
38
|
-
- `--test-runner <jest|vitest>`: Optional test runner override when test feature is enabled
|
|
39
|
-
- `--pm <pnpm|npm|yarn|auto>`: Package manager (auto-detected by default)
|
|
40
|
-
- `--force`: Overwrite existing config files
|
|
41
|
-
- `--dry-run`: Print full plan without writing files
|
|
42
|
-
- `--skip-husky-install`: Skip husky install command
|
|
43
|
-
- `--skills`: Update existing `CLAUDE.md`/`AGENTS.md`/`GEMINI.md` with skill guidance (enabled by default)
|
|
44
|
-
- `--no-skills`: Disable skill guidance updates
|
|
45
|
-
- `--help`: Show help
|
|
46
|
-
|
|
47
|
-
## Features
|
|
48
|
-
|
|
49
|
-
Default feature set:
|
|
50
|
-
|
|
51
|
-
- `husky`
|
|
52
|
-
- `typescript`
|
|
53
|
-
- `lint`
|
|
54
|
-
- `format`
|
|
55
|
-
- `test`
|
|
56
|
-
|
|
57
|
-
## Examples
|
|
58
|
-
|
|
59
|
-
Initialize with auto-detected framework:
|
|
14
|
+
Quick start:
|
|
60
15
|
|
|
61
16
|
```bash
|
|
62
17
|
npx @tyyyho/treg init
|
|
63
18
|
```
|
|
64
19
|
|
|
65
|
-
|
|
20
|
+
Commands:
|
|
66
21
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
22
|
+
- `init`
|
|
23
|
+
- `add`
|
|
24
|
+
- `list`
|
|
70
25
|
|
|
71
|
-
|
|
26
|
+
`init` interactive questions:
|
|
72
27
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
28
|
+
1. package manager (`pnpm|npm|yarn|bun`)
|
|
29
|
+
2. features (default: `all`)
|
|
30
|
+
3. test runner (if `test` is selected, supports `skip`)
|
|
31
|
+
4. formatter (if `format` is selected)
|
|
32
|
+
5. ai tools (`Claude|Codex|Gemini`, multi-select, if AI skill guidance is selected)
|
|
76
33
|
|
|
77
|
-
|
|
34
|
+
`add` examples:
|
|
78
35
|
|
|
79
36
|
```bash
|
|
37
|
+
npx @tyyyho/treg add --features lint,format
|
|
80
38
|
npx @tyyyho/treg add --features format --formatter oxfmt
|
|
39
|
+
npx @tyyyho/treg add --features test --test-runner vitest
|
|
81
40
|
```
|
|
82
41
|
|
|
83
|
-
|
|
42
|
+
Options:
|
|
84
43
|
|
|
85
|
-
|
|
86
|
-
npx @tyyyho/treg add --no-format --no-test-runner
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
Use Vitest for test feature:
|
|
44
|
+
`init`:
|
|
90
45
|
|
|
91
|
-
```
|
|
92
|
-
|
|
46
|
+
```text
|
|
47
|
+
--dry-run
|
|
48
|
+
--help
|
|
93
49
|
```
|
|
94
50
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
Target a different directory explicitly:
|
|
108
|
-
|
|
109
|
-
```bash
|
|
110
|
-
npx @tyyyho/treg init --framework react --dir ./packages/web
|
|
51
|
+
`add`:
|
|
52
|
+
|
|
53
|
+
```text
|
|
54
|
+
--framework <node|react|next|vue|svelte|nuxt>
|
|
55
|
+
--features <lint,format,typescript,test,husky>
|
|
56
|
+
--dir <path>
|
|
57
|
+
--formatter <prettier|oxfmt>
|
|
58
|
+
--test-runner <jest|vitest>
|
|
59
|
+
--force
|
|
60
|
+
--dry-run
|
|
61
|
+
--skip-husky-install
|
|
62
|
+
--help
|
|
111
63
|
```
|
|
112
64
|
|
|
113
|
-
|
|
65
|
+
Defaults:
|
|
114
66
|
|
|
115
|
-
- `
|
|
116
|
-
-
|
|
117
|
-
-
|
|
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.
|
|
120
|
-
- `add` lets you install only the features you specify.
|
|
121
|
-
- Framework setup uses one stable config per framework (no `--framework-version` variants).
|
|
122
|
-
- `--dry-run` prints the full plan and does not write files.
|
|
67
|
+
- framework detect order: `nuxt -> next -> react -> vue -> svelte -> node`
|
|
68
|
+
- test runner: `vue/nuxt = vitest`, others = `jest`
|
|
69
|
+
- formatter: `prettier`
|
package/README.npm.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# @tyyyho/treg
|
|
2
|
+
|
|
3
|
+
`treg` applies tooling standards to existing repositories.
|
|
4
|
+
|
|
5
|
+
Scope:
|
|
6
|
+
|
|
7
|
+
- lint
|
|
8
|
+
- format
|
|
9
|
+
- TypeScript
|
|
10
|
+
- test
|
|
11
|
+
- husky
|
|
12
|
+
- AI skill guidance
|
|
13
|
+
|
|
14
|
+
Quick start:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npx @tyyyho/treg init
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Commands:
|
|
21
|
+
|
|
22
|
+
- `init`
|
|
23
|
+
- `add`
|
|
24
|
+
- `list`
|
|
25
|
+
|
|
26
|
+
`init` interactive questions:
|
|
27
|
+
|
|
28
|
+
1. package manager (`pnpm|npm|yarn|bun`)
|
|
29
|
+
2. features (default: `all`)
|
|
30
|
+
3. test runner (if `test` is selected, supports `skip`)
|
|
31
|
+
4. formatter (if `format` is selected)
|
|
32
|
+
5. ai tools (`Claude|Codex|Gemini`, multi-select, if AI skill guidance is selected)
|
|
33
|
+
|
|
34
|
+
`add` examples:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npx @tyyyho/treg add --features lint,format
|
|
38
|
+
npx @tyyyho/treg add --features format --formatter oxfmt
|
|
39
|
+
npx @tyyyho/treg add --features test --test-runner vitest
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Options:
|
|
43
|
+
|
|
44
|
+
`init`:
|
|
45
|
+
|
|
46
|
+
```text
|
|
47
|
+
--dry-run
|
|
48
|
+
--help
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`add`:
|
|
52
|
+
|
|
53
|
+
```text
|
|
54
|
+
--framework <node|react|next|vue|svelte|nuxt>
|
|
55
|
+
--features <lint,format,typescript,test,husky>
|
|
56
|
+
--dir <path>
|
|
57
|
+
--formatter <prettier|oxfmt>
|
|
58
|
+
--test-runner <jest|vitest>
|
|
59
|
+
--force
|
|
60
|
+
--dry-run
|
|
61
|
+
--skip-husky-install
|
|
62
|
+
--help
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Defaults:
|
|
66
|
+
|
|
67
|
+
- framework detect order: `nuxt -> next -> react -> vue -> svelte -> node`
|
|
68
|
+
- test runner: `vue/nuxt = vitest`, others = `jest`
|
|
69
|
+
- formatter: `prettier`
|
package/README.zh-hant.md
CHANGED
|
@@ -2,8 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
[English README](./README.md)
|
|
4
4
|
|
|
5
|
-
`treg` 是一個用於既有專案的 CLI
|
|
6
|
-
|
|
5
|
+
`treg` 是一個用於既有專案的 CLI,可快速套用一致的工具鏈規範。
|
|
6
|
+
它只處理基礎設施設定:
|
|
7
|
+
|
|
8
|
+
- lint
|
|
9
|
+
- format
|
|
10
|
+
- TypeScript
|
|
11
|
+
- test
|
|
12
|
+
- husky
|
|
13
|
+
- AI skill 指引
|
|
7
14
|
|
|
8
15
|
## 快速開始
|
|
9
16
|
|
|
@@ -11,112 +18,124 @@
|
|
|
11
18
|
npx @tyyyho/treg init
|
|
12
19
|
```
|
|
13
20
|
|
|
14
|
-
|
|
15
|
-
pnpm dlx @tyyyho/treg init
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
`init` 會依照依賴自動偵測 framework。
|
|
21
|
+
## 指令總覽
|
|
19
22
|
|
|
20
|
-
|
|
23
|
+
- `init`:初始化功能。
|
|
24
|
+
- `add`:只為既有專案加入指定功能。
|
|
25
|
+
- `list`:列出支援的 framework/feature/formatter/test runner。
|
|
21
26
|
|
|
22
|
-
|
|
23
|
-
npx @tyyyho/treg <command> [options]
|
|
24
|
-
```
|
|
27
|
+
## Init 互動流程
|
|
25
28
|
|
|
26
|
-
|
|
27
|
-
- `add`:在既有專案中新增指定 feature
|
|
28
|
-
- `list`:列出支援的 framework、feature、formatter 與 test runner
|
|
29
|
+
執行 `init` 後,`treg` 會依序詢問:
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
1. 套件管理器(`pnpm|npm|yarn|bun`)
|
|
32
|
+
2. 要加入的功能(預設勾選 `all`)
|
|
33
|
+
3. 測試工具(僅在選到 `test` 時詢問,支援 `skip`)
|
|
34
|
+
4. Formatter(僅在選到 `format` 時詢問)
|
|
35
|
+
5. AI 工具(`Claude|Codex|Gemini` 可複選,僅在選到 AI skill guidance 時詢問)
|
|
31
36
|
|
|
32
|
-
|
|
33
|
-
- `--features <lint,format,typescript,test,husky>`:指定要安裝的 feature(預設全部)
|
|
34
|
-
- `--no-format`:略過 format feature,避免覆寫既有格式化設定與 scripts
|
|
35
|
-
- `--no-test-runner`:略過 test feature,避免覆寫既有測試 runner 與設定
|
|
36
|
-
- `--dir <path>`:指定目標目錄(預設為目前目錄)
|
|
37
|
-
- `--formatter <prettier|oxfmt>`:format feature 使用的 formatter(預設為 `prettier`)
|
|
38
|
-
- `--test-runner <jest|vitest>`:可選,啟用 test feature 時覆寫測試框架
|
|
39
|
-
- `--pm <pnpm|npm|yarn|auto>`:套件管理器(預設自動偵測)
|
|
40
|
-
- `--force`:覆寫既有設定檔
|
|
41
|
-
- `--dry-run`:輸出完整執行計畫,但不寫入檔案
|
|
42
|
-
- `--skip-husky-install`:略過 `husky install`
|
|
43
|
-
- `--skills`:更新既有 `CLAUDE.md`/`AGENTS.md`/`GEMINI.md` 的 skill 指引(預設啟用)
|
|
44
|
-
- `--no-skills`:停用 skill 指引更新
|
|
45
|
-
- `--help`:顯示說明
|
|
37
|
+
預設 `all` 內容:
|
|
46
38
|
|
|
47
|
-
|
|
39
|
+
- lint
|
|
40
|
+
- format
|
|
41
|
+
- TypeScript
|
|
42
|
+
- test
|
|
43
|
+
- husky
|
|
44
|
+
- AI skill guidance
|
|
48
45
|
|
|
49
|
-
|
|
46
|
+
## 常見用法
|
|
50
47
|
|
|
51
|
-
|
|
52
|
-
- `typescript`
|
|
53
|
-
- `lint`
|
|
54
|
-
- `format`
|
|
55
|
-
- `test`
|
|
56
|
-
|
|
57
|
-
## 使用範例
|
|
58
|
-
|
|
59
|
-
依賴自動偵測 framework 初始化:
|
|
48
|
+
初始化:
|
|
60
49
|
|
|
61
50
|
```bash
|
|
62
51
|
npx @tyyyho/treg init
|
|
63
52
|
```
|
|
64
53
|
|
|
65
|
-
|
|
54
|
+
只預覽 init 計畫:
|
|
66
55
|
|
|
67
56
|
```bash
|
|
68
|
-
npx @tyyyho/treg init --
|
|
57
|
+
npx @tyyyho/treg init --dry-run
|
|
69
58
|
```
|
|
70
59
|
|
|
71
|
-
|
|
60
|
+
只加入 lint + format:
|
|
72
61
|
|
|
73
62
|
```bash
|
|
74
63
|
npx @tyyyho/treg add --features lint,format
|
|
75
64
|
```
|
|
76
65
|
|
|
77
|
-
|
|
66
|
+
format 使用 `oxfmt`:
|
|
78
67
|
|
|
79
68
|
```bash
|
|
80
69
|
npx @tyyyho/treg add --features format --formatter oxfmt
|
|
81
70
|
```
|
|
82
71
|
|
|
83
|
-
|
|
72
|
+
test 使用 `vitest`:
|
|
84
73
|
|
|
85
74
|
```bash
|
|
86
|
-
npx @tyyyho/treg add --
|
|
75
|
+
npx @tyyyho/treg add --features test --test-runner vitest
|
|
87
76
|
```
|
|
88
77
|
|
|
89
|
-
|
|
78
|
+
## CLI 參數
|
|
90
79
|
|
|
91
|
-
|
|
92
|
-
npx @tyyyho/treg init --framework node --features test --test-runner vitest
|
|
93
|
-
```
|
|
80
|
+
`init` 可用參數:
|
|
94
81
|
|
|
95
|
-
|
|
82
|
+
```text
|
|
83
|
+
--dry-run
|
|
84
|
+
--help
|
|
85
|
+
```
|
|
96
86
|
|
|
97
|
-
|
|
98
|
-
|
|
87
|
+
`add` 可用參數:
|
|
88
|
+
|
|
89
|
+
```text
|
|
90
|
+
--framework <node|react|next|vue|svelte|nuxt>
|
|
91
|
+
--features <lint,format,typescript,test,husky>
|
|
92
|
+
--dir <path>
|
|
93
|
+
--formatter <prettier|oxfmt>
|
|
94
|
+
--test-runner <jest|vitest>
|
|
95
|
+
--force
|
|
96
|
+
--dry-run
|
|
97
|
+
--skip-husky-install
|
|
98
|
+
--help
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
## 預設行為
|
|
102
102
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
103
|
+
framework 偵測順序:
|
|
104
|
+
|
|
105
|
+
`nuxt -> next -> react -> vue -> svelte -> node`
|
|
106
|
+
|
|
107
|
+
測試工具預設:
|
|
108
|
+
|
|
109
|
+
- `vue` / `nuxt`:`vitest`
|
|
110
|
+
- 其他:`jest`
|
|
111
|
+
|
|
112
|
+
formatter 預設:
|
|
113
|
+
|
|
114
|
+
- `prettier`
|
|
115
|
+
|
|
116
|
+
## AI Skills 行為
|
|
117
|
+
|
|
118
|
+
- 只會更新選擇的 AI 工具對應檔案:
|
|
119
|
+
- `Claude -> CLAUDE.md`
|
|
120
|
+
- `Codex -> AGENTS.md`
|
|
121
|
+
- `Gemini -> GEMINI.md`
|
|
122
|
+
- 僅更新 repo root 已存在的檔案。
|
|
123
|
+
- 不存在的檔案會跳過,不會自動建立。
|
|
124
|
+
- 每個啟用功能的 skill 檔只會建立一次。
|
|
106
125
|
|
|
107
|
-
|
|
126
|
+
## 發布
|
|
108
127
|
|
|
109
128
|
```bash
|
|
110
|
-
|
|
129
|
+
npm run release -- patch
|
|
111
130
|
```
|
|
112
131
|
|
|
113
|
-
|
|
132
|
+
支援目標:
|
|
114
133
|
|
|
115
|
-
- `
|
|
116
|
-
-
|
|
117
|
-
-
|
|
118
|
-
-
|
|
119
|
-
-
|
|
120
|
-
- `
|
|
121
|
-
-
|
|
122
|
-
-
|
|
134
|
+
- `patch`
|
|
135
|
+
- `minor`
|
|
136
|
+
- `major`
|
|
137
|
+
- `prepatch`
|
|
138
|
+
- `preminor`
|
|
139
|
+
- `premajor`
|
|
140
|
+
- `prerelease`
|
|
141
|
+
- 指定版本(`x.y.z`)
|
package/dist/init-project/cli.js
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
1
|
const ALLOWED_COMMANDS = ["init", "add", "list"];
|
|
2
|
-
const ALLOWED_PACKAGE_MANAGERS = [
|
|
3
|
-
"pnpm",
|
|
4
|
-
"npm",
|
|
5
|
-
"yarn",
|
|
6
|
-
"auto",
|
|
7
|
-
];
|
|
8
2
|
const ALLOWED_FRAMEWORKS = [
|
|
9
3
|
"node",
|
|
10
4
|
"react",
|
|
@@ -22,30 +16,28 @@ const ALLOWED_FEATURES = [
|
|
|
22
16
|
];
|
|
23
17
|
const ALLOWED_TEST_RUNNERS = ["jest", "vitest"];
|
|
24
18
|
const ALLOWED_FORMATTERS = ["prettier", "oxfmt"];
|
|
19
|
+
const DEFAULT_AI_TOOLS = ["claude", "codex", "gemini"];
|
|
25
20
|
export const USAGE = `Usage: treg <command> [options]
|
|
26
21
|
|
|
27
22
|
Commands:
|
|
28
|
-
init Initialize infra rules in a project (
|
|
23
|
+
init Initialize infra rules in a project (interactive setup)
|
|
29
24
|
add Add selected infra features to an existing project
|
|
30
25
|
list List supported frameworks, features, formatters, and test runners
|
|
31
26
|
|
|
32
27
|
Options:
|
|
28
|
+
--dry-run Print planned changes without writing files
|
|
29
|
+
-h, --help Show help
|
|
30
|
+
|
|
31
|
+
Add command options:
|
|
33
32
|
--framework <node|react|next|vue|svelte|nuxt>
|
|
34
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
|
-
--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
|
|
39
36
|
--dir <path> Target directory (defaults to current directory)
|
|
40
37
|
--formatter <prettier|oxfmt> Formatter for format feature (default: prettier)
|
|
41
38
|
--test-runner <jest|vitest> Optional test runner override (default: vue/nuxt=vitest, others=jest)
|
|
42
|
-
--pm <pnpm|npm|yarn|auto> Package manager (auto-detected if omitted)
|
|
43
39
|
--force Overwrite existing config files
|
|
44
|
-
--dry-run Print planned changes without writing files
|
|
45
40
|
--skip-husky-install Do not run husky install
|
|
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
|
|
48
|
-
-h, --help Show help
|
|
49
41
|
`;
|
|
50
42
|
function includes(allowed, value) {
|
|
51
43
|
return allowed.includes(value);
|
|
@@ -53,9 +45,6 @@ function includes(allowed, value) {
|
|
|
53
45
|
function isCommandName(value) {
|
|
54
46
|
return includes(ALLOWED_COMMANDS, value);
|
|
55
47
|
}
|
|
56
|
-
function isPackageManagerOption(value) {
|
|
57
|
-
return includes(ALLOWED_PACKAGE_MANAGERS, value);
|
|
58
|
-
}
|
|
59
48
|
function isFrameworkId(value) {
|
|
60
49
|
return includes(ALLOWED_FRAMEWORKS, value);
|
|
61
50
|
}
|
|
@@ -76,13 +65,11 @@ export function parseArgs(argv) {
|
|
|
76
65
|
formatter: "prettier",
|
|
77
66
|
features: [],
|
|
78
67
|
testRunner: null,
|
|
79
|
-
pm: null,
|
|
80
68
|
force: false,
|
|
81
69
|
dryRun: false,
|
|
82
|
-
noFormat: false,
|
|
83
|
-
noTestRunner: false,
|
|
84
70
|
skipHuskyInstall: false,
|
|
85
71
|
skills: true,
|
|
72
|
+
aiTools: [...DEFAULT_AI_TOOLS],
|
|
86
73
|
help: false,
|
|
87
74
|
};
|
|
88
75
|
let cursor = 0;
|
|
@@ -98,8 +85,16 @@ export function parseArgs(argv) {
|
|
|
98
85
|
}
|
|
99
86
|
if (arg === "-h" || arg === "--help") {
|
|
100
87
|
options.help = true;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (arg === "--dry-run") {
|
|
91
|
+
options.dryRun = true;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (options.command !== "add") {
|
|
95
|
+
throw new Error(`Unsupported option for ${options.command}: ${arg}. Only --dry-run and --help are allowed.`);
|
|
101
96
|
}
|
|
102
|
-
|
|
97
|
+
if (arg === "--framework") {
|
|
103
98
|
options.framework = readFlagValue(argv, i, "--framework");
|
|
104
99
|
i += 1;
|
|
105
100
|
}
|
|
@@ -134,34 +129,12 @@ export function parseArgs(argv) {
|
|
|
134
129
|
else if (arg.startsWith("--test-runner=")) {
|
|
135
130
|
options.testRunner = readInlineFlagValue(arg, "--test-runner");
|
|
136
131
|
}
|
|
137
|
-
else if (arg === "--pm") {
|
|
138
|
-
options.pm = readFlagValue(argv, i, "--pm");
|
|
139
|
-
i += 1;
|
|
140
|
-
}
|
|
141
|
-
else if (arg.startsWith("--pm=")) {
|
|
142
|
-
options.pm = readInlineFlagValue(arg, "--pm");
|
|
143
|
-
}
|
|
144
132
|
else if (arg === "--force") {
|
|
145
133
|
options.force = true;
|
|
146
134
|
}
|
|
147
|
-
else if (arg === "--dry-run") {
|
|
148
|
-
options.dryRun = true;
|
|
149
|
-
}
|
|
150
|
-
else if (arg === "--no-format") {
|
|
151
|
-
options.noFormat = true;
|
|
152
|
-
}
|
|
153
|
-
else if (arg === "--no-test-runner") {
|
|
154
|
-
options.noTestRunner = true;
|
|
155
|
-
}
|
|
156
135
|
else if (arg === "--skip-husky-install") {
|
|
157
136
|
options.skipHuskyInstall = true;
|
|
158
137
|
}
|
|
159
|
-
else if (arg === "--skills") {
|
|
160
|
-
options.skills = true;
|
|
161
|
-
}
|
|
162
|
-
else if (arg === "--no-skills") {
|
|
163
|
-
options.skills = false;
|
|
164
|
-
}
|
|
165
138
|
else {
|
|
166
139
|
throw new Error(`Unknown argument: ${arg}`);
|
|
167
140
|
}
|
|
@@ -199,9 +172,6 @@ function validateParsedOptions(options) {
|
|
|
199
172
|
if (options.projectDir === "") {
|
|
200
173
|
throw new Error("Missing value for --dir");
|
|
201
174
|
}
|
|
202
|
-
if (options.pm && !isPackageManagerOption(options.pm)) {
|
|
203
|
-
throw new Error(`Unsupported package manager: ${options.pm}`);
|
|
204
|
-
}
|
|
205
175
|
if (options.framework && !isFrameworkId(options.framework)) {
|
|
206
176
|
throw new Error(`Unsupported framework: ${options.framework}`);
|
|
207
177
|
}
|
|
@@ -219,12 +189,6 @@ function validateParsedOptions(options) {
|
|
|
219
189
|
}
|
|
220
190
|
export function resolveFeatures(options) {
|
|
221
191
|
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
|
-
}
|
|
228
192
|
return {
|
|
229
193
|
lint: selected.has("lint"),
|
|
230
194
|
format: selected.has("format"),
|
|
@@ -238,6 +202,8 @@ export function printSupportedTargets() {
|
|
|
238
202
|
console.log("Features: lint, format, typescript, test, husky");
|
|
239
203
|
console.log("Formatters: prettier, oxfmt");
|
|
240
204
|
console.log("Test runners: jest, vitest");
|
|
205
|
+
console.log("Package managers: pnpm, npm, yarn, bun");
|
|
206
|
+
console.log("AI tools: Claude, Codex, Gemini");
|
|
241
207
|
}
|
|
242
208
|
export function resolveTestRunner(frameworkId, testRunner) {
|
|
243
209
|
if (testRunner) {
|
|
@@ -3,6 +3,7 @@ import { promises as fs } from "node:fs";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { parseArgs, printSupportedTargets, resolveFeatures, resolveTestRunner, USAGE, } from "./cli.js";
|
|
5
5
|
import { resolveFramework } from "./frameworks/index.js";
|
|
6
|
+
import { collectInitPrompts } from "./init-prompts.js";
|
|
6
7
|
import { runFeatureRules } from "./mrm-rules/index.js";
|
|
7
8
|
import { detectPackageManager, runScript } from "./package-manager.js";
|
|
8
9
|
import { formatStep } from "./utils.js";
|
|
@@ -34,25 +35,42 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
34
35
|
return;
|
|
35
36
|
}
|
|
36
37
|
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
|
|
37
|
-
const pm = !options.pm || options.pm === "auto"
|
|
38
|
-
? detectPackageManager(projectDir)
|
|
39
|
-
: options.pm;
|
|
40
38
|
const framework = resolveFramework(options.framework, packageJson);
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
let pm = detectPackageManager(projectDir);
|
|
40
|
+
let formatter = options.formatter;
|
|
41
|
+
let testRunner = resolveTestRunner(framework.id, options.testRunner);
|
|
42
|
+
let enabledFeatures = resolveFeatures(options);
|
|
43
|
+
let skills = options.skills;
|
|
44
|
+
let aiTools = [...options.aiTools];
|
|
45
|
+
if (options.command === "init") {
|
|
46
|
+
const prompted = await collectInitPrompts({
|
|
47
|
+
pm,
|
|
48
|
+
formatter,
|
|
49
|
+
testRunner: resolveTestRunner(framework.id, null),
|
|
50
|
+
});
|
|
51
|
+
pm = prompted.pm;
|
|
52
|
+
formatter = prompted.formatter;
|
|
53
|
+
testRunner = prompted.testRunner;
|
|
54
|
+
enabledFeatures = prompted.enabledFeatures;
|
|
55
|
+
skills = prompted.skills;
|
|
56
|
+
aiTools = prompted.aiTools;
|
|
57
|
+
}
|
|
43
58
|
const context = {
|
|
44
59
|
...options,
|
|
60
|
+
formatter,
|
|
45
61
|
testRunner,
|
|
46
62
|
projectDir,
|
|
47
63
|
pm,
|
|
48
64
|
framework,
|
|
49
65
|
enabledFeatures,
|
|
66
|
+
skills,
|
|
67
|
+
aiTools,
|
|
50
68
|
};
|
|
51
69
|
console.log(formatStep(1, TOTAL_STEPS, "Resolve plan", options.dryRun));
|
|
52
|
-
console.log(`${options.dryRun ? "[dry-run] " : ""}Framework=${framework.id}, features=${Object.entries(enabledFeatures)
|
|
70
|
+
console.log(`${options.dryRun ? "[dry-run] " : ""}Framework=${framework.id}, pm=${pm}, features=${Object.entries(enabledFeatures)
|
|
53
71
|
.filter(([, enabled]) => enabled)
|
|
54
72
|
.map(([name]) => name)
|
|
55
|
-
.join(", ")}, formatter=${
|
|
73
|
+
.join(", ")}, formatter=${formatter}, testRunner=${testRunner}, aiTools=${skills ? aiTools.join(", ") : "disabled"}`);
|
|
56
74
|
console.log(formatStep(2, TOTAL_STEPS, "Run mrm rules", options.dryRun));
|
|
57
75
|
await runFeatureRules(context);
|
|
58
76
|
console.log(formatStep(3, TOTAL_STEPS, "Finalize", options.dryRun));
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
2
|
+
import { createInterface } from "node:readline/promises";
|
|
3
|
+
const DEFAULT_AI_TOOLS = ["claude", "codex", "gemini"];
|
|
4
|
+
const PACKAGE_MANAGER_CHOICES = [
|
|
5
|
+
{ value: "pnpm", label: "pnpm" },
|
|
6
|
+
{ value: "npm", label: "npm" },
|
|
7
|
+
{ value: "yarn", label: "yarn" },
|
|
8
|
+
{ value: "bun", label: "bun" },
|
|
9
|
+
];
|
|
10
|
+
const TEST_RUNNER_CHOICES = [
|
|
11
|
+
{ value: "jest", label: "jest" },
|
|
12
|
+
{ value: "vitest", label: "vitest" },
|
|
13
|
+
{ value: "skip", label: "skip (disable test feature)" },
|
|
14
|
+
];
|
|
15
|
+
const FORMATTER_CHOICES = [
|
|
16
|
+
{ value: "prettier", label: "prettier" },
|
|
17
|
+
{ value: "oxfmt", label: "oxfmt" },
|
|
18
|
+
];
|
|
19
|
+
const AI_TOOL_CHOICES = [
|
|
20
|
+
{ value: "claude", label: "Claude" },
|
|
21
|
+
{ value: "codex", label: "Codex" },
|
|
22
|
+
{ value: "gemini", label: "Gemini" },
|
|
23
|
+
];
|
|
24
|
+
const FEATURE_CHOICES = [
|
|
25
|
+
{
|
|
26
|
+
value: "all",
|
|
27
|
+
label: "all (lint, format, TypeScript, test, husky, AI skill guidance)",
|
|
28
|
+
},
|
|
29
|
+
{ value: "lint", label: "lint" },
|
|
30
|
+
{ value: "format", label: "format" },
|
|
31
|
+
{ value: "typescript", label: "TypeScript" },
|
|
32
|
+
{ value: "test", label: "test" },
|
|
33
|
+
{ value: "husky", label: "husky" },
|
|
34
|
+
{ value: "skills", label: "AI skill guidance" },
|
|
35
|
+
];
|
|
36
|
+
function formatChoices(choices) {
|
|
37
|
+
return choices
|
|
38
|
+
.map((choice, index) => ` ${index + 1}. ${choice.label}`)
|
|
39
|
+
.join("\n");
|
|
40
|
+
}
|
|
41
|
+
function parseSingleChoice(rawInput, choices, defaultValue) {
|
|
42
|
+
const normalized = rawInput.trim().toLowerCase();
|
|
43
|
+
if (!normalized) {
|
|
44
|
+
return { ok: true, value: defaultValue };
|
|
45
|
+
}
|
|
46
|
+
const byIndex = Number.parseInt(normalized, 10);
|
|
47
|
+
if (!Number.isNaN(byIndex) && String(byIndex) === normalized) {
|
|
48
|
+
const selected = choices[byIndex - 1];
|
|
49
|
+
if (selected) {
|
|
50
|
+
return { ok: true, value: selected.value };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const byValue = choices.find(choice => choice.value === normalized);
|
|
54
|
+
if (byValue) {
|
|
55
|
+
return { ok: true, value: byValue.value };
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
ok: false,
|
|
59
|
+
error: `Invalid input: ${rawInput}. Please enter a listed number or option name.`,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function parseMultiChoice(rawInput, choices, defaultValues) {
|
|
63
|
+
const normalized = rawInput.trim().toLowerCase();
|
|
64
|
+
if (!normalized) {
|
|
65
|
+
return { ok: true, value: [...defaultValues] };
|
|
66
|
+
}
|
|
67
|
+
if (normalized === "skip" || normalized === "none") {
|
|
68
|
+
return { ok: true, value: [] };
|
|
69
|
+
}
|
|
70
|
+
const tokens = normalized
|
|
71
|
+
.split(",")
|
|
72
|
+
.map(item => item.trim())
|
|
73
|
+
.filter(Boolean);
|
|
74
|
+
if (tokens.length === 0) {
|
|
75
|
+
return { ok: false, error: "Please enter at least one option." };
|
|
76
|
+
}
|
|
77
|
+
const selected = new Set();
|
|
78
|
+
for (const token of tokens) {
|
|
79
|
+
const byIndex = Number.parseInt(token, 10);
|
|
80
|
+
if (!Number.isNaN(byIndex) && String(byIndex) === token) {
|
|
81
|
+
const item = choices[byIndex - 1];
|
|
82
|
+
if (!item) {
|
|
83
|
+
return {
|
|
84
|
+
ok: false,
|
|
85
|
+
error: `Invalid index: ${token}. Please choose from listed options.`,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
selected.add(item.value);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const byValue = choices.find(choice => choice.value === token);
|
|
92
|
+
if (!byValue) {
|
|
93
|
+
return {
|
|
94
|
+
ok: false,
|
|
95
|
+
error: `Invalid option: ${token}. Please choose from listed options.`,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
selected.add(byValue.value);
|
|
99
|
+
}
|
|
100
|
+
return { ok: true, value: [...selected] };
|
|
101
|
+
}
|
|
102
|
+
function toFeatureSelection(selected) {
|
|
103
|
+
if (selected.includes("all")) {
|
|
104
|
+
return {
|
|
105
|
+
enabledFeatures: {
|
|
106
|
+
lint: true,
|
|
107
|
+
format: true,
|
|
108
|
+
typescript: true,
|
|
109
|
+
test: true,
|
|
110
|
+
husky: true,
|
|
111
|
+
},
|
|
112
|
+
skills: true,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
enabledFeatures: {
|
|
117
|
+
lint: selected.includes("lint"),
|
|
118
|
+
format: selected.includes("format"),
|
|
119
|
+
typescript: selected.includes("typescript"),
|
|
120
|
+
test: selected.includes("test"),
|
|
121
|
+
husky: selected.includes("husky"),
|
|
122
|
+
},
|
|
123
|
+
skills: selected.includes("skills"),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
async function askUntilValid(ask, prompt, parser) {
|
|
127
|
+
while (true) {
|
|
128
|
+
const raw = await ask(prompt);
|
|
129
|
+
const parsed = parser(raw);
|
|
130
|
+
if (parsed.ok && parsed.value !== undefined) {
|
|
131
|
+
return parsed.value;
|
|
132
|
+
}
|
|
133
|
+
console.log(parsed.error ?? "Invalid input");
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
export async function collectInitPrompts(defaults) {
|
|
137
|
+
if (!input.isTTY || !output.isTTY) {
|
|
138
|
+
console.log("Non-interactive shell detected. Use init defaults.");
|
|
139
|
+
return {
|
|
140
|
+
pm: defaults.pm,
|
|
141
|
+
formatter: defaults.formatter,
|
|
142
|
+
testRunner: defaults.testRunner,
|
|
143
|
+
enabledFeatures: {
|
|
144
|
+
lint: true,
|
|
145
|
+
format: true,
|
|
146
|
+
typescript: true,
|
|
147
|
+
test: true,
|
|
148
|
+
husky: true,
|
|
149
|
+
},
|
|
150
|
+
skills: true,
|
|
151
|
+
aiTools: [...DEFAULT_AI_TOOLS],
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
const rl = createInterface({ input, output });
|
|
155
|
+
try {
|
|
156
|
+
console.log("\nInit setup");
|
|
157
|
+
console.log("\n1) Package manager");
|
|
158
|
+
console.log(formatChoices(PACKAGE_MANAGER_CHOICES));
|
|
159
|
+
const pm = await askUntilValid(rl.question.bind(rl), `Select package manager [default: ${defaults.pm}]: `, answer => parseSingleChoice(answer, PACKAGE_MANAGER_CHOICES, defaults.pm));
|
|
160
|
+
console.log("\n2) Features");
|
|
161
|
+
console.log(formatChoices(FEATURE_CHOICES));
|
|
162
|
+
const featureAnswers = await askUntilValid(rl.question.bind(rl), "Select features (comma-separated, default: all): ", answer => parseMultiChoice(answer, FEATURE_CHOICES, ["all"]));
|
|
163
|
+
const featureSelection = toFeatureSelection(featureAnswers);
|
|
164
|
+
let testRunner = defaults.testRunner;
|
|
165
|
+
const enabledFeatures = { ...featureSelection.enabledFeatures };
|
|
166
|
+
if (featureSelection.enabledFeatures.test) {
|
|
167
|
+
console.log("\n3) Test runner");
|
|
168
|
+
console.log(formatChoices(TEST_RUNNER_CHOICES));
|
|
169
|
+
const selectedTestRunner = await askUntilValid(rl.question.bind(rl), `Select test runner [default: ${defaults.testRunner}, or skip]: `, answer => parseSingleChoice(answer, TEST_RUNNER_CHOICES, defaults.testRunner));
|
|
170
|
+
if (selectedTestRunner === "skip") {
|
|
171
|
+
enabledFeatures.test = false;
|
|
172
|
+
console.log("Test feature disabled by selection: skip");
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
testRunner = selectedTestRunner;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
console.log("\n3) Test runner skipped (test feature not selected)");
|
|
180
|
+
}
|
|
181
|
+
let formatter = defaults.formatter;
|
|
182
|
+
if (featureSelection.enabledFeatures.format) {
|
|
183
|
+
console.log("\n4) Formatter");
|
|
184
|
+
console.log(formatChoices(FORMATTER_CHOICES));
|
|
185
|
+
formatter = await askUntilValid(rl.question.bind(rl), `Select formatter [default: ${defaults.formatter}]: `, answer => parseSingleChoice(answer, FORMATTER_CHOICES, defaults.formatter));
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
console.log("\n4) Formatter skipped (format feature not selected)");
|
|
189
|
+
}
|
|
190
|
+
let aiTools = [];
|
|
191
|
+
let skills = featureSelection.skills;
|
|
192
|
+
if (skills) {
|
|
193
|
+
console.log("\n5) AI tools");
|
|
194
|
+
console.log(formatChoices(AI_TOOL_CHOICES));
|
|
195
|
+
console.log("Type 'skip' to disable AI skill guidance.");
|
|
196
|
+
aiTools = await askUntilValid(rl.question.bind(rl), "Select AI tools (comma-separated, default: all): ", answer => parseMultiChoice(answer, AI_TOOL_CHOICES, DEFAULT_AI_TOOLS));
|
|
197
|
+
if (aiTools.length === 0) {
|
|
198
|
+
skills = false;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
console.log("\n5) AI tools skipped (AI skill guidance not selected)");
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
pm,
|
|
206
|
+
formatter,
|
|
207
|
+
testRunner,
|
|
208
|
+
enabledFeatures,
|
|
209
|
+
skills,
|
|
210
|
+
aiTools,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
finally {
|
|
214
|
+
rl.close();
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
export const __testables__ = {
|
|
218
|
+
parseSingleChoice,
|
|
219
|
+
parseMultiChoice,
|
|
220
|
+
toFeatureSelection,
|
|
221
|
+
};
|
|
@@ -5,7 +5,11 @@ const START_MARKER = "<!-- treg:skills:start -->";
|
|
|
5
5
|
const END_MARKER = "<!-- treg:skills:end -->";
|
|
6
6
|
const SKILL_SECTION_HEADING = "## treg AI Skills";
|
|
7
7
|
const SKILLS_BASE_DIR = "skills";
|
|
8
|
-
const
|
|
8
|
+
const AI_TOOL_DOCS = {
|
|
9
|
+
claude: "CLAUDE.md",
|
|
10
|
+
codex: "AGENTS.md",
|
|
11
|
+
gemini: "GEMINI.md",
|
|
12
|
+
};
|
|
9
13
|
const FEATURE_SKILLS = {
|
|
10
14
|
format: {
|
|
11
15
|
name: "treg/format",
|
|
@@ -65,8 +69,11 @@ const FEATURE_STEP_LABELS = {
|
|
|
65
69
|
test: "Test Configuration",
|
|
66
70
|
typescript: "TypeScript Settings",
|
|
67
71
|
};
|
|
68
|
-
function resolveSkillsDocs(projectDir) {
|
|
69
|
-
|
|
72
|
+
function resolveSkillsDocs(projectDir, aiTools) {
|
|
73
|
+
const docFiles = [...new Set(aiTools.map(tool => AI_TOOL_DOCS[tool]))];
|
|
74
|
+
return docFiles
|
|
75
|
+
.map(fileName => path.join(projectDir, fileName))
|
|
76
|
+
.filter(existsSync);
|
|
70
77
|
}
|
|
71
78
|
function getEnabledFeatures(enabledFeatures) {
|
|
72
79
|
return Object.entries(enabledFeatures)
|
|
@@ -168,10 +175,10 @@ function upsertSkillSection(content, nextSection) {
|
|
|
168
175
|
return `${content.trimEnd()}\n\n${nextSection.trim()}\n`;
|
|
169
176
|
}
|
|
170
177
|
export async function runAiSkillsRule(context) {
|
|
171
|
-
const { projectDir, dryRun } = context;
|
|
172
|
-
const targetFiles = resolveSkillsDocs(projectDir);
|
|
178
|
+
const { projectDir, dryRun, aiTools } = context;
|
|
179
|
+
const targetFiles = resolveSkillsDocs(projectDir, aiTools);
|
|
173
180
|
if (targetFiles.length === 0) {
|
|
174
|
-
console.log("Skip ai-skills (
|
|
181
|
+
console.log("Skip ai-skills (selected AI docs not found)");
|
|
175
182
|
return;
|
|
176
183
|
}
|
|
177
184
|
const enabled = getEnabledFeatures(context.enabledFeatures);
|
|
@@ -53,6 +53,10 @@ function runHuskyInstallCommand(pm, projectDir) {
|
|
|
53
53
|
runCommand("pnpm exec husky", projectDir, false);
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
|
+
if (pm === "bun") {
|
|
57
|
+
runCommand("bunx husky", projectDir, false);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
56
60
|
if (pm === "yarn") {
|
|
57
61
|
runCommand("yarn husky", projectDir, false);
|
|
58
62
|
return;
|
|
@@ -5,6 +5,10 @@ export function detectPackageManager(projectDir) {
|
|
|
5
5
|
if (existsSync(path.join(projectDir, "pnpm-lock.yaml"))) {
|
|
6
6
|
return "pnpm";
|
|
7
7
|
}
|
|
8
|
+
if (existsSync(path.join(projectDir, "bun.lockb")) ||
|
|
9
|
+
existsSync(path.join(projectDir, "bun.lock"))) {
|
|
10
|
+
return "bun";
|
|
11
|
+
}
|
|
8
12
|
if (existsSync(path.join(projectDir, "yarn.lock"))) {
|
|
9
13
|
return "yarn";
|
|
10
14
|
}
|
|
@@ -16,6 +20,8 @@ export function detectPackageManager(projectDir) {
|
|
|
16
20
|
export function getRunCommand(pm) {
|
|
17
21
|
if (pm === "pnpm")
|
|
18
22
|
return "pnpm";
|
|
23
|
+
if (pm === "bun")
|
|
24
|
+
return "bun run";
|
|
19
25
|
if (pm === "yarn")
|
|
20
26
|
return "yarn";
|
|
21
27
|
return "npm run";
|
|
@@ -32,6 +38,10 @@ export function runScript(pm, scriptName, cwd, dryRun = false) {
|
|
|
32
38
|
runCommand(`pnpm ${scriptName}`, cwd, dryRun);
|
|
33
39
|
return;
|
|
34
40
|
}
|
|
41
|
+
if (pm === "bun") {
|
|
42
|
+
runCommand(`bun run ${scriptName}`, cwd, dryRun);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
35
45
|
if (pm === "yarn") {
|
|
36
46
|
runCommand(`yarn ${scriptName}`, cwd, dryRun);
|
|
37
47
|
return;
|
|
@@ -46,6 +56,9 @@ export function installPackages(pm, projectDir, packages, isDev, dryRun = false)
|
|
|
46
56
|
if (pm === "pnpm") {
|
|
47
57
|
command = `pnpm add ${isDev ? "-D " : ""}${list}`;
|
|
48
58
|
}
|
|
59
|
+
else if (pm === "bun") {
|
|
60
|
+
command = `bun add ${isDev ? "-d " : ""}${list}`;
|
|
61
|
+
}
|
|
49
62
|
else if (pm === "yarn") {
|
|
50
63
|
command = `yarn add ${isDev ? "-D " : ""}${list}`;
|
|
51
64
|
}
|