pm-workflow-studio 0.1.2 → 0.1.3

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.
Files changed (3) hide show
  1. package/README.md +16 -0
  2. package/bin/pmflow.js +147 -4
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -17,6 +17,20 @@ pm-workflow --help
17
17
 
18
18
  ## 快速开始
19
19
 
20
+ 交互式:
21
+
22
+ ```bash
23
+ pmflow
24
+ ```
25
+
26
+ 或:
27
+
28
+ ```bash
29
+ pmflow init
30
+ ```
31
+
32
+ CLI 会逐步询问产品名称、项目目录和 AI 工作区结构。适合不想记参数的日常使用。
33
+
20
34
  Codex:
21
35
 
22
36
  ```bash
@@ -49,6 +63,7 @@ claude
49
63
 
50
64
  ```bash
51
65
  pmflow init --ai auto --name "习惯打卡"
66
+ pmflow init --interactive
52
67
  pmflow init --ai codex --root ./pm-workflow-demo --name "习惯打卡"
53
68
  pmflow init --ai claude --root ./pm-workflow-claude-demo --name "习惯打卡"
54
69
  ```
@@ -56,6 +71,7 @@ pmflow init --ai claude --root ./pm-workflow-claude-demo --name "习惯打卡"
56
71
  - `--ai auto|codex|claude`:选择生成结构。默认 `auto`;空目录默认 Codex;目录已有 `.claude/` 时选择 Claude Code。
57
72
  - `--root <dir>`:目标项目目录,默认当前目录。
58
73
  - `--name <product name>`:产品名称,默认 `My Product`。
74
+ - `--interactive` / `-i`:进入交互式创建向导。
59
75
  - `--cli` 是 `--ai` 的别名。
60
76
 
61
77
  当前支持 Codex 和 Claude Code;`kiro` 暂未支持。
package/bin/pmflow.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  const fs = require("node:fs");
4
4
  const path = require("node:path");
5
+ const readline = require("node:readline");
5
6
 
6
7
  const PACKAGE_ROOT = path.resolve(__dirname, "..");
7
8
  const CODEX_MIRROR = path.join(PACKAGE_ROOT, ".codex");
@@ -26,6 +27,8 @@ function printHelp() {
26
27
  console.log(`PM Workflow Studio
27
28
 
28
29
  Usage:
30
+ pmflow
31
+ pmflow init
29
32
  pmflow init [--ai auto|codex|claude] [--root <dir>] [--name <product name>]
30
33
  pm-workflow init [--ai auto|codex|claude] [--root <dir>] [--name <product name>]
31
34
 
@@ -36,9 +39,13 @@ Options:
36
39
  claude: generate .claude structure for Claude Code.
37
40
  --root Target workspace directory. Defaults to current directory.
38
41
  --name Product name for generated templates. Defaults to "My Product".
42
+ -i, --interactive
43
+ Start the interactive setup wizard.
39
44
  -h, --help Show help.
40
45
 
41
46
  Examples:
47
+ pmflow
48
+ pmflow init
42
49
  pmflow init --ai codex --root ./pm-workflow-demo --name "习惯打卡"
43
50
  pmflow init --ai claude --root ./pm-workflow-claude-demo --name "习惯打卡"
44
51
  pmflow init --ai auto --name "习惯打卡"
@@ -54,6 +61,14 @@ function fail(message) {
54
61
  process.exit(1);
55
62
  }
56
63
 
64
+ const useColor = process.stdout.isTTY && !process.env.NO_COLOR;
65
+ const color = {
66
+ cyan: (value) => (useColor ? `\u001b[36m${value}\u001b[0m` : value),
67
+ dim: (value) => (useColor ? `\u001b[2m${value}\u001b[0m` : value),
68
+ green: (value) => (useColor ? `\u001b[32m${value}\u001b[0m` : value),
69
+ bold: (value) => (useColor ? `\u001b[1m${value}\u001b[0m` : value),
70
+ };
71
+
57
72
  function exists(target) {
58
73
  return fs.existsSync(target);
59
74
  }
@@ -89,6 +104,7 @@ function parseInitArgs(argv) {
89
104
  ai: "auto",
90
105
  root: ".",
91
106
  name: "My Product",
107
+ interactive: false,
92
108
  };
93
109
 
94
110
  for (let index = 0; index < argv.length; index += 1) {
@@ -97,6 +113,10 @@ function parseInitArgs(argv) {
97
113
  printHelp();
98
114
  process.exit(0);
99
115
  }
116
+ if (arg === "-i" || arg === "--interactive") {
117
+ options.interactive = true;
118
+ continue;
119
+ }
100
120
  if (arg === "--ai" || arg === "--cli") {
101
121
  const value = argv[index + 1];
102
122
  if (!value) fail(`${arg} requires a value.`);
@@ -140,6 +160,110 @@ function parseInitArgs(argv) {
140
160
  return options;
141
161
  }
142
162
 
163
+ function createPrompt() {
164
+ const rl = readline.createInterface({
165
+ input: process.stdin,
166
+ output: process.stdout,
167
+ });
168
+ return {
169
+ ask(question) {
170
+ return new Promise((resolve) => {
171
+ rl.question(question, (answer) => resolve(answer.trim()));
172
+ });
173
+ },
174
+ close() {
175
+ rl.close();
176
+ },
177
+ };
178
+ }
179
+
180
+ function printInteractiveHeader() {
181
+ console.log("");
182
+ console.log(color.cyan("┌────────────────────────────────────────────┐"));
183
+ console.log(color.cyan("│") + ` ${color.bold("PM Workflow Studio")} ` + color.cyan("│"));
184
+ console.log(color.cyan("│") + " 交互式创建产品工作室,命令参数仍然可用 " + color.cyan("│"));
185
+ console.log(color.cyan("└────────────────────────────────────────────┘"));
186
+ console.log("");
187
+ }
188
+
189
+ function printStep(index, title) {
190
+ console.log(color.dim(`\nStep ${index}`));
191
+ console.log(color.bold(title));
192
+ }
193
+
194
+ function formatDefault(value) {
195
+ return color.dim(`(${value})`);
196
+ }
197
+
198
+ async function askText(prompt, label, defaultValue) {
199
+ const answer = await prompt.ask(`${label} ${formatDefault(defaultValue)}: `);
200
+ return answer || defaultValue;
201
+ }
202
+
203
+ async function askAi(prompt, defaultValue) {
204
+ const choices = [
205
+ ["1", "auto", "自动判断,空目录默认 Codex"],
206
+ ["2", "codex", "生成 .codex + .agents 结构"],
207
+ ["3", "claude", "生成 .claude 结构"],
208
+ ];
209
+
210
+ while (true) {
211
+ for (const [key, value, description] of choices) {
212
+ const marker = value === defaultValue ? color.green(" *") : " ";
213
+ console.log(` ${key}. ${value.padEnd(6)} ${color.dim(description)}${marker}`);
214
+ }
215
+ const answer = await prompt.ask(`选择 AI CLI ${formatDefault(defaultValue)}: `);
216
+ const normalized = answer ? answer.toLowerCase() : defaultValue;
217
+ const matched = choices.find(([key, value]) => normalized === key || normalized === value);
218
+ if (matched) return matched[1];
219
+ console.log("请输入 1/2/3,或 auto/codex/claude。");
220
+ }
221
+ }
222
+
223
+ async function askConfirm(prompt, label, defaultValue = true) {
224
+ const suffix = defaultValue ? "Y/n" : "y/N";
225
+ while (true) {
226
+ const answer = (await prompt.ask(`${label} ${formatDefault(suffix)}: `)).toLowerCase();
227
+ if (!answer) return defaultValue;
228
+ if (["y", "yes", "是", "确认"].includes(answer)) return true;
229
+ if (["n", "no", "否", "取消"].includes(answer)) return false;
230
+ console.log("请输入 y 或 n。");
231
+ }
232
+ }
233
+
234
+ async function runInteractiveInit(seedOptions = {}) {
235
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
236
+ fail("interactive mode requires a TTY. Use `pmflow init --ai auto --root . --name \"My Product\"` in non-interactive environments.");
237
+ }
238
+
239
+ const prompt = createPrompt();
240
+ try {
241
+ printInteractiveHeader();
242
+
243
+ printStep(1, "产品信息");
244
+ const name = await askText(prompt, "产品名称", seedOptions.name || "My Product");
245
+ const root = await askText(prompt, "项目目录", seedOptions.root || ".");
246
+
247
+ printStep(2, "AI 工作区结构");
248
+ const ai = await askAi(prompt, seedOptions.ai || "auto");
249
+
250
+ printStep(3, "确认创建");
251
+ console.log(` 产品名称: ${color.green(name)}`);
252
+ console.log(` 项目目录: ${color.green(path.resolve(root))}`);
253
+ console.log(` AI 结构: ${color.green(ai)}`);
254
+ const confirmed = await askConfirm(prompt, "开始创建?", true);
255
+ if (!confirmed) {
256
+ console.log("已取消。");
257
+ return;
258
+ }
259
+
260
+ console.log("");
261
+ createStructure(root, name, normalizeAi(ai));
262
+ } finally {
263
+ prompt.close();
264
+ }
265
+ }
266
+
143
267
  function templatePath(name) {
144
268
  const centralPath = path.join(CODEX_TEMPLATES, name);
145
269
  if (exists(centralPath)) return centralPath;
@@ -442,22 +566,41 @@ function createStructure(rootInput, productName, cli) {
442
566
  console.log(nextStep);
443
567
  }
444
568
 
445
- function runInit(argv) {
569
+ async function runInit(argv) {
446
570
  const options = parseInitArgs(argv);
571
+ if (options.interactive || (argv.length === 0 && process.stdin.isTTY && process.stdout.isTTY)) {
572
+ await runInteractiveInit(options);
573
+ return;
574
+ }
447
575
  createStructure(options.root, options.name, options.ai);
448
576
  }
449
577
 
450
- function main() {
578
+ async function main() {
451
579
  const [command, ...rest] = process.argv.slice(2);
452
580
  if (!command || command === "-h" || command === "--help") {
581
+ if (!command) {
582
+ if (process.stdin.isTTY && process.stdout.isTTY) {
583
+ await runInteractiveInit();
584
+ } else {
585
+ printHelp();
586
+ }
587
+ return;
588
+ }
453
589
  printHelp();
454
590
  return;
455
591
  }
456
592
  if (command === "init") {
457
- runInit(rest);
593
+ await runInit(rest);
458
594
  return;
459
595
  }
460
596
  fail(`unknown command "${command}".`);
461
597
  }
462
598
 
463
- main();
599
+ main().catch((error) => {
600
+ if (error && error.message) {
601
+ console.error(`pmflow: ${error.message}`);
602
+ } else {
603
+ console.error(error);
604
+ }
605
+ process.exit(1);
606
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pm-workflow-studio",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "AI product development studio CLI for Codex and Claude Code.",
5
5
  "type": "commonjs",
6
6
  "bin": {