@tyyyho/treg 0.1.5 → 0.1.6
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 +18 -10
- package/dist/init-project/cli.js +89 -24
- package/dist/init-project/frameworks/react/index.js +2 -2
- package/dist/init-project/mrm-rules/ai-skills.js +22 -10
- package/dist/init-project/mrm-rules/husky.js +12 -9
- package/dist/init-project/types.js +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,9 +6,9 @@ It applies infra setup such as lint, format, TypeScript, test, husky, and AI ski
|
|
|
6
6
|
## Quick Start
|
|
7
7
|
|
|
8
8
|
```bash
|
|
9
|
-
pnpm dlx @tyyyho/treg init
|
|
9
|
+
pnpm dlx @tyyyho/treg init --framework react
|
|
10
10
|
# or
|
|
11
|
-
npx @tyyyho/treg init
|
|
11
|
+
npx @tyyyho/treg init --framework react
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
`init` requires `--framework`.
|
|
@@ -16,7 +16,7 @@ npx @tyyyho/treg init <project-dir> --framework react
|
|
|
16
16
|
## Commands
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
npx @tyyyho/treg <command> [
|
|
19
|
+
npx @tyyyho/treg <command> [options]
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
- `init`: Initialize infra rules (requires `--framework`)
|
|
@@ -28,12 +28,14 @@ npx @tyyyho/treg <command> [projectDir] [options]
|
|
|
28
28
|
- `--framework <node|react|next|vue|svelte|nuxt>`: Target framework
|
|
29
29
|
- `--framework-version <major>`: Optional major version hint (react only)
|
|
30
30
|
- `--features <lint,format,typescript,test,husky>`: Features to install (defaults to all)
|
|
31
|
+
- `--dir <path>`: Target directory (defaults to current directory)
|
|
31
32
|
- `--test-runner <jest|vitest>`: Test runner when test feature is enabled
|
|
32
33
|
- `--pm <pnpm|npm|yarn|auto>`: Package manager (auto-detected by default)
|
|
33
34
|
- `--force`: Overwrite existing config files
|
|
34
35
|
- `--dry-run`: Print full plan without writing files
|
|
35
36
|
- `--skip-husky-install`: Skip husky install command
|
|
36
|
-
- `--skills`: Update existing `AGENTS.md`/`CLAUDE.md` with skill guidance
|
|
37
|
+
- `--skills`: Update existing `AGENTS.md`/`CLAUDE.md` with skill guidance (enabled by default)
|
|
38
|
+
- `--no-skills`: Disable skill guidance updates
|
|
37
39
|
- `--help`: Show help
|
|
38
40
|
|
|
39
41
|
## Features
|
|
@@ -51,37 +53,43 @@ Default feature set:
|
|
|
51
53
|
Initialize a React project:
|
|
52
54
|
|
|
53
55
|
```bash
|
|
54
|
-
npx @tyyyho/treg init
|
|
56
|
+
npx @tyyyho/treg init --framework react
|
|
55
57
|
```
|
|
56
58
|
|
|
57
59
|
Add only lint + format:
|
|
58
60
|
|
|
59
61
|
```bash
|
|
60
|
-
npx @tyyyho/treg add
|
|
62
|
+
npx @tyyyho/treg add --features lint,format
|
|
61
63
|
```
|
|
62
64
|
|
|
63
65
|
Use Vitest for test feature:
|
|
64
66
|
|
|
65
67
|
```bash
|
|
66
|
-
npx @tyyyho/treg init
|
|
68
|
+
npx @tyyyho/treg init --framework node --features test --test-runner vitest
|
|
67
69
|
```
|
|
68
70
|
|
|
69
71
|
Use react major version variant:
|
|
70
72
|
|
|
71
73
|
```bash
|
|
72
|
-
npx @tyyyho/treg init
|
|
74
|
+
npx @tyyyho/treg init --framework react --framework-version 18
|
|
73
75
|
```
|
|
74
76
|
|
|
75
77
|
Preview changes only:
|
|
76
78
|
|
|
77
79
|
```bash
|
|
78
|
-
npx @tyyyho/treg init
|
|
80
|
+
npx @tyyyho/treg init --framework react --dry-run
|
|
79
81
|
```
|
|
80
82
|
|
|
81
83
|
Update AI skill guidance:
|
|
82
84
|
|
|
83
85
|
```bash
|
|
84
|
-
npx @tyyyho/treg add
|
|
86
|
+
npx @tyyyho/treg add --features lint,format,husky
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Target a different directory explicitly:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
npx @tyyyho/treg init --framework react --dir ./packages/web
|
|
85
93
|
```
|
|
86
94
|
|
|
87
95
|
## Notes
|
package/dist/init-project/cli.js
CHANGED
|
@@ -1,9 +1,27 @@
|
|
|
1
1
|
const ALLOWED_COMMANDS = ["init", "add", "list"];
|
|
2
|
-
const ALLOWED_PACKAGE_MANAGERS = [
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
const ALLOWED_PACKAGE_MANAGERS = [
|
|
3
|
+
"pnpm",
|
|
4
|
+
"npm",
|
|
5
|
+
"yarn",
|
|
6
|
+
"auto",
|
|
7
|
+
];
|
|
8
|
+
const ALLOWED_FRAMEWORKS = [
|
|
9
|
+
"node",
|
|
10
|
+
"react",
|
|
11
|
+
"next",
|
|
12
|
+
"vue",
|
|
13
|
+
"svelte",
|
|
14
|
+
"nuxt",
|
|
15
|
+
];
|
|
16
|
+
const ALLOWED_FEATURES = [
|
|
17
|
+
"lint",
|
|
18
|
+
"format",
|
|
19
|
+
"typescript",
|
|
20
|
+
"test",
|
|
21
|
+
"husky",
|
|
22
|
+
];
|
|
5
23
|
const ALLOWED_TEST_RUNNERS = ["jest", "vitest"];
|
|
6
|
-
export const USAGE = `Usage: treg <command> [
|
|
24
|
+
export const USAGE = `Usage: treg <command> [options]
|
|
7
25
|
|
|
8
26
|
Commands:
|
|
9
27
|
init Initialize infra rules in a project (requires --framework)
|
|
@@ -16,74 +34,104 @@ Options:
|
|
|
16
34
|
--framework-version <major> Optional framework major version hint
|
|
17
35
|
--features <lint,format,typescript,test,husky>
|
|
18
36
|
Features to install (all selected by default)
|
|
37
|
+
--dir <path> Target directory (defaults to current directory)
|
|
19
38
|
--test-runner <jest|vitest> Test runner when test feature is enabled
|
|
20
39
|
--pm <pnpm|npm|yarn|auto> Package manager (auto-detected if omitted)
|
|
21
40
|
--force Overwrite existing config files
|
|
22
41
|
--dry-run Print planned changes without writing files
|
|
23
42
|
--skip-husky-install Do not run husky install
|
|
24
|
-
--skills Update AGENTS.md/CLAUDE.md with feature skill guidance
|
|
43
|
+
--skills Update AGENTS.md/CLAUDE.md with feature skill guidance (default: enabled)
|
|
44
|
+
--no-skills Disable AGENTS.md/CLAUDE.md skill guidance updates
|
|
25
45
|
-h, --help Show help
|
|
26
46
|
`;
|
|
47
|
+
function includes(allowed, value) {
|
|
48
|
+
return allowed.includes(value);
|
|
49
|
+
}
|
|
50
|
+
function isCommandName(value) {
|
|
51
|
+
return includes(ALLOWED_COMMANDS, value);
|
|
52
|
+
}
|
|
53
|
+
function isPackageManagerOption(value) {
|
|
54
|
+
return includes(ALLOWED_PACKAGE_MANAGERS, value);
|
|
55
|
+
}
|
|
56
|
+
function isFrameworkId(value) {
|
|
57
|
+
return includes(ALLOWED_FRAMEWORKS, value);
|
|
58
|
+
}
|
|
59
|
+
function isFeatureName(value) {
|
|
60
|
+
return includes(ALLOWED_FEATURES, value);
|
|
61
|
+
}
|
|
62
|
+
function isTestRunner(value) {
|
|
63
|
+
return includes(ALLOWED_TEST_RUNNERS, value);
|
|
64
|
+
}
|
|
27
65
|
export function parseArgs(argv) {
|
|
28
66
|
const options = {
|
|
29
67
|
command: "init",
|
|
30
68
|
projectDir: null,
|
|
31
69
|
framework: null,
|
|
32
70
|
frameworkVersion: null,
|
|
33
|
-
features:
|
|
71
|
+
features: [],
|
|
34
72
|
testRunner: "jest",
|
|
35
73
|
pm: null,
|
|
36
74
|
force: false,
|
|
37
75
|
dryRun: false,
|
|
38
76
|
skipHuskyInstall: false,
|
|
39
|
-
skills:
|
|
77
|
+
skills: true,
|
|
40
78
|
help: false,
|
|
41
79
|
};
|
|
42
80
|
let cursor = 0;
|
|
43
81
|
const firstArg = argv[0];
|
|
44
|
-
if (firstArg &&
|
|
82
|
+
if (firstArg && isCommandName(firstArg)) {
|
|
45
83
|
options.command = firstArg;
|
|
46
84
|
cursor = 1;
|
|
47
85
|
}
|
|
48
86
|
for (let i = cursor; i < argv.length; i += 1) {
|
|
49
87
|
const arg = argv[i];
|
|
88
|
+
if (!arg) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
50
91
|
if (arg === "-h" || arg === "--help") {
|
|
51
92
|
options.help = true;
|
|
52
93
|
}
|
|
53
94
|
else if (arg === "--framework") {
|
|
54
|
-
options.framework = argv
|
|
95
|
+
options.framework = readFlagValue(argv, i, "--framework");
|
|
55
96
|
i += 1;
|
|
56
97
|
}
|
|
57
98
|
else if (arg.startsWith("--framework=")) {
|
|
58
|
-
options.framework = arg
|
|
99
|
+
options.framework = readInlineFlagValue(arg, "--framework");
|
|
59
100
|
}
|
|
60
101
|
else if (arg === "--framework-version") {
|
|
61
|
-
options.frameworkVersion = argv
|
|
102
|
+
options.frameworkVersion = readFlagValue(argv, i, "--framework-version");
|
|
62
103
|
i += 1;
|
|
63
104
|
}
|
|
64
105
|
else if (arg.startsWith("--framework-version=")) {
|
|
65
|
-
options.frameworkVersion = arg
|
|
106
|
+
options.frameworkVersion = readInlineFlagValue(arg, "--framework-version");
|
|
66
107
|
}
|
|
67
108
|
else if (arg === "--features") {
|
|
68
109
|
options.features.push(...parseCsvValue(argv[i + 1], "--features"));
|
|
69
110
|
i += 1;
|
|
70
111
|
}
|
|
71
112
|
else if (arg.startsWith("--features=")) {
|
|
72
|
-
options.features.push(...parseCsvValue(arg
|
|
113
|
+
options.features.push(...parseCsvValue(readInlineFlagValue(arg, "--features"), "--features"));
|
|
114
|
+
}
|
|
115
|
+
else if (arg === "--dir") {
|
|
116
|
+
options.projectDir = readFlagValue(argv, i, "--dir");
|
|
117
|
+
i += 1;
|
|
118
|
+
}
|
|
119
|
+
else if (arg.startsWith("--dir=")) {
|
|
120
|
+
options.projectDir = readInlineFlagValue(arg, "--dir");
|
|
73
121
|
}
|
|
74
122
|
else if (arg === "--test-runner") {
|
|
75
|
-
options.testRunner = argv
|
|
123
|
+
options.testRunner = readFlagValue(argv, i, "--test-runner");
|
|
76
124
|
i += 1;
|
|
77
125
|
}
|
|
78
126
|
else if (arg.startsWith("--test-runner=")) {
|
|
79
|
-
options.testRunner = arg
|
|
127
|
+
options.testRunner = readInlineFlagValue(arg, "--test-runner");
|
|
80
128
|
}
|
|
81
129
|
else if (arg === "--pm") {
|
|
82
|
-
options.pm = argv
|
|
130
|
+
options.pm = readFlagValue(argv, i, "--pm");
|
|
83
131
|
i += 1;
|
|
84
132
|
}
|
|
85
133
|
else if (arg.startsWith("--pm=")) {
|
|
86
|
-
options.pm = arg
|
|
134
|
+
options.pm = readInlineFlagValue(arg, "--pm");
|
|
87
135
|
}
|
|
88
136
|
else if (arg === "--force") {
|
|
89
137
|
options.force = true;
|
|
@@ -97,8 +145,8 @@ export function parseArgs(argv) {
|
|
|
97
145
|
else if (arg === "--skills") {
|
|
98
146
|
options.skills = true;
|
|
99
147
|
}
|
|
100
|
-
else if (
|
|
101
|
-
options.
|
|
148
|
+
else if (arg === "--no-skills") {
|
|
149
|
+
options.skills = false;
|
|
102
150
|
}
|
|
103
151
|
else {
|
|
104
152
|
throw new Error(`Unknown argument: ${arg}`);
|
|
@@ -107,6 +155,20 @@ export function parseArgs(argv) {
|
|
|
107
155
|
validateParsedOptions(options);
|
|
108
156
|
return options;
|
|
109
157
|
}
|
|
158
|
+
function readFlagValue(argv, index, flagName) {
|
|
159
|
+
const value = argv[index + 1];
|
|
160
|
+
if (!value) {
|
|
161
|
+
throw new Error(`Missing value for ${flagName}`);
|
|
162
|
+
}
|
|
163
|
+
return value;
|
|
164
|
+
}
|
|
165
|
+
function readInlineFlagValue(arg, flagName) {
|
|
166
|
+
const [, rawValue] = arg.split("=", 2);
|
|
167
|
+
if (!rawValue) {
|
|
168
|
+
throw new Error(`Missing value for ${flagName}`);
|
|
169
|
+
}
|
|
170
|
+
return rawValue;
|
|
171
|
+
}
|
|
110
172
|
function parseCsvValue(rawValue, flagName) {
|
|
111
173
|
if (!rawValue) {
|
|
112
174
|
throw new Error(`Missing value for ${flagName}`);
|
|
@@ -117,13 +179,16 @@ function parseCsvValue(rawValue, flagName) {
|
|
|
117
179
|
.filter(Boolean);
|
|
118
180
|
}
|
|
119
181
|
function validateParsedOptions(options) {
|
|
120
|
-
if (!
|
|
182
|
+
if (!isCommandName(options.command)) {
|
|
121
183
|
throw new Error(`Unsupported command: ${options.command}`);
|
|
122
184
|
}
|
|
123
|
-
if (options.
|
|
185
|
+
if (options.projectDir === "") {
|
|
186
|
+
throw new Error("Missing value for --dir");
|
|
187
|
+
}
|
|
188
|
+
if (options.pm && !isPackageManagerOption(options.pm)) {
|
|
124
189
|
throw new Error(`Unsupported package manager: ${options.pm}`);
|
|
125
190
|
}
|
|
126
|
-
if (options.framework && !
|
|
191
|
+
if (options.framework && !isFrameworkId(options.framework)) {
|
|
127
192
|
throw new Error(`Unsupported framework: ${options.framework}`);
|
|
128
193
|
}
|
|
129
194
|
if (options.frameworkVersion && !/^\d+$/.test(options.frameworkVersion)) {
|
|
@@ -134,11 +199,11 @@ function validateParsedOptions(options) {
|
|
|
134
199
|
options.framework !== "react") {
|
|
135
200
|
throw new Error(`Unsupported --framework-version for framework: ${options.framework}`);
|
|
136
201
|
}
|
|
137
|
-
if (!
|
|
202
|
+
if (!isTestRunner(options.testRunner)) {
|
|
138
203
|
throw new Error(`Unsupported test runner: ${options.testRunner}`);
|
|
139
204
|
}
|
|
140
205
|
for (const feature of options.features) {
|
|
141
|
-
if (!
|
|
206
|
+
if (!isFeatureName(feature)) {
|
|
142
207
|
throw new Error(`Unsupported feature in --features: ${feature}`);
|
|
143
208
|
}
|
|
144
209
|
}
|
|
@@ -11,8 +11,8 @@ export const reactFramework = {
|
|
|
11
11
|
},
|
|
12
12
|
};
|
|
13
13
|
const REACT_VARIANTS = {
|
|
14
|
-
18: reactV18Framework,
|
|
15
|
-
19: reactV19Framework,
|
|
14
|
+
"18": reactV18Framework,
|
|
15
|
+
"19": reactV19Framework,
|
|
16
16
|
};
|
|
17
17
|
export function resolveReactFramework(packageJson, frameworkVersion) {
|
|
18
18
|
if (frameworkVersion && REACT_VARIANTS[frameworkVersion]) {
|
|
@@ -48,6 +48,13 @@ const FEATURE_SKILLS = {
|
|
|
48
48
|
],
|
|
49
49
|
},
|
|
50
50
|
};
|
|
51
|
+
const FEATURE_STEP_LABELS = {
|
|
52
|
+
format: "格式處理",
|
|
53
|
+
husky: "Git hook 維護",
|
|
54
|
+
lint: "Lint 規則檢查",
|
|
55
|
+
test: "測試規則調整",
|
|
56
|
+
typescript: "TypeScript 型別與設定",
|
|
57
|
+
};
|
|
51
58
|
function resolveSkillsDoc(projectDir) {
|
|
52
59
|
const agentsPath = path.join(projectDir, "AGENTS.md");
|
|
53
60
|
if (existsSync(agentsPath)) {
|
|
@@ -118,23 +125,28 @@ function buildSkillSection(context) {
|
|
|
118
125
|
START_MARKER,
|
|
119
126
|
"## treg AI Skills",
|
|
120
127
|
"",
|
|
121
|
-
"
|
|
128
|
+
"### 執行步驟與 Skill 對應",
|
|
122
129
|
"",
|
|
123
130
|
];
|
|
124
|
-
|
|
131
|
+
if (enabled.length === 0) {
|
|
132
|
+
lines.push("1. 本次未啟用任何 feature,無需呼叫 skill。");
|
|
133
|
+
lines.push("");
|
|
134
|
+
lines.push(END_MARKER);
|
|
135
|
+
lines.push("");
|
|
136
|
+
return lines.join("\n");
|
|
137
|
+
}
|
|
138
|
+
enabled.forEach((feature, index) => {
|
|
125
139
|
const skill = FEATURE_SKILLS[feature];
|
|
126
140
|
if (!skill)
|
|
127
|
-
|
|
141
|
+
return;
|
|
128
142
|
const skillRelativePath = getSkillRelativePath(feature);
|
|
129
|
-
|
|
130
|
-
lines.push(
|
|
131
|
-
lines.push(`- 使用時機: ${skill.when}`);
|
|
143
|
+
const stepLabel = FEATURE_STEP_LABELS[feature] ?? feature;
|
|
144
|
+
lines.push(`${index + 1}. ${stepLabel}:呼叫 [${skill.name}](${skillRelativePath})`);
|
|
132
145
|
if (feature === "test") {
|
|
133
|
-
lines.push(
|
|
146
|
+
lines.push(` - 目前測試工具:\`${testRunner}\``);
|
|
134
147
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
148
|
+
});
|
|
149
|
+
lines.push("");
|
|
138
150
|
lines.push(END_MARKER);
|
|
139
151
|
lines.push("");
|
|
140
152
|
return lines.join("\n");
|
|
@@ -45,14 +45,17 @@ export async function runHuskyRule(context) {
|
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
47
|
if (!dryRun) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
48
|
+
runHuskyInstallCommand(pm, projectDir);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function runHuskyInstallCommand(pm, projectDir) {
|
|
52
|
+
if (pm === "pnpm") {
|
|
53
|
+
runCommand("pnpm exec husky", projectDir, false);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (pm === "yarn") {
|
|
57
|
+
runCommand("yarn husky", projectDir, false);
|
|
58
|
+
return;
|
|
57
59
|
}
|
|
60
|
+
runCommand("npx husky", projectDir, false);
|
|
58
61
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|