@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 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 <project-dir> --framework react
9
+ pnpm dlx @tyyyho/treg init --framework react
10
10
  # or
11
- npx @tyyyho/treg init <project-dir> --framework react
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> [projectDir] [options]
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 . --framework react
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 . --features lint,format
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 . --framework node --features test --test-runner vitest
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 . --framework react --framework-version 18
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 . --framework react --dry-run
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 . --features lint,format,husky --skills
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
@@ -1,9 +1,27 @@
1
1
  const ALLOWED_COMMANDS = ["init", "add", "list"];
2
- const ALLOWED_PACKAGE_MANAGERS = ["pnpm", "npm", "yarn", "auto"];
3
- const ALLOWED_FRAMEWORKS = ["node", "react", "next", "vue", "svelte", "nuxt"];
4
- const ALLOWED_FEATURES = ["lint", "format", "typescript", "test", "husky"];
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> [projectDir] [options]
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: new Array(),
71
+ features: [],
34
72
  testRunner: "jest",
35
73
  pm: null,
36
74
  force: false,
37
75
  dryRun: false,
38
76
  skipHuskyInstall: false,
39
- skills: false,
77
+ skills: true,
40
78
  help: false,
41
79
  };
42
80
  let cursor = 0;
43
81
  const firstArg = argv[0];
44
- if (firstArg && ALLOWED_COMMANDS.includes(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[i + 1];
95
+ options.framework = readFlagValue(argv, i, "--framework");
55
96
  i += 1;
56
97
  }
57
98
  else if (arg.startsWith("--framework=")) {
58
- options.framework = arg.split("=")[1];
99
+ options.framework = readInlineFlagValue(arg, "--framework");
59
100
  }
60
101
  else if (arg === "--framework-version") {
61
- options.frameworkVersion = argv[i + 1];
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.split("=")[1];
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.split("=")[1], "--features"));
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[i + 1];
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.split("=")[1];
127
+ options.testRunner = readInlineFlagValue(arg, "--test-runner");
80
128
  }
81
129
  else if (arg === "--pm") {
82
- options.pm = argv[i + 1];
130
+ options.pm = readFlagValue(argv, i, "--pm");
83
131
  i += 1;
84
132
  }
85
133
  else if (arg.startsWith("--pm=")) {
86
- options.pm = arg.split("=")[1];
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 (!arg.startsWith("-") && !options.projectDir) {
101
- options.projectDir = arg;
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 (!ALLOWED_COMMANDS.includes(options.command)) {
182
+ if (!isCommandName(options.command)) {
121
183
  throw new Error(`Unsupported command: ${options.command}`);
122
184
  }
123
- if (options.pm && !ALLOWED_PACKAGE_MANAGERS.includes(options.pm)) {
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 && !ALLOWED_FRAMEWORKS.includes(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 (!ALLOWED_TEST_RUNNERS.includes(options.testRunner)) {
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 (!ALLOWED_FEATURES.includes(feature)) {
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
- "以下 skill 可在 agent 任務完成前引用,確保基礎建設規則正確且可重跑:",
128
+ "### 執行步驟與 Skill 對應",
122
129
  "",
123
130
  ];
124
- for (const feature of enabled) {
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
- continue;
141
+ return;
128
142
  const skillRelativePath = getSkillRelativePath(feature);
129
- lines.push(`### ${feature}`);
130
- lines.push(`- Skill: [${skill.name}](${skillRelativePath})`);
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(`- 目前測試工具: \`${testRunner}\``);
146
+ lines.push(` - 目前測試工具:\`${testRunner}\``);
134
147
  }
135
- lines.push(`- 驗證清單: ${skill.checklist.join("、")}`);
136
- lines.push("");
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
- if (pm === "pnpm") {
49
- runCommand("pnpm exec husky", projectDir, false);
50
- }
51
- else if (pm === "yarn") {
52
- runCommand("yarn husky", projectDir, false);
53
- }
54
- else {
55
- runCommand("npx husky", projectDir, false);
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 {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tyyyho/treg",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "CLI tool for initializing development conventions in existing projects.",
5
5
  "license": "MIT",
6
6
  "repository": {