create-yss-spec 3.1.0 → 3.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 (139) hide show
  1. package/README.md +117 -91
  2. package/bin/create-yss-spec.js +11 -3
  3. package/package.json +9 -2
  4. package/src/api/_sync-service.js +191 -0
  5. package/src/api/index.js +22 -0
  6. package/src/api/project-diff.js +9 -0
  7. package/src/api/project-doctor.js +10 -0
  8. package/src/api/template-apply.js +10 -0
  9. package/src/api/template-plan.js +9 -0
  10. package/src/cli/args.js +60 -0
  11. package/src/cli/error-output.js +52 -0
  12. package/src/cli/flags.js +9 -0
  13. package/src/cli/help.js +95 -0
  14. package/src/cli/index.js +82 -0
  15. package/src/cli/plan-output.js +61 -0
  16. package/src/cli/prompts.js +91 -0
  17. package/src/cli/router.js +41 -0
  18. package/src/cli.js +5 -2253
  19. package/src/commands/attach.js +280 -0
  20. package/src/commands/diff.js +23 -0
  21. package/src/commands/doctor.js +417 -0
  22. package/src/commands/init.js +193 -0
  23. package/src/commands/sync.js +146 -0
  24. package/src/commands/update.js +14 -0
  25. package/src/contracts/apply-result-v1.json +45 -0
  26. package/src/contracts/customization-policy-v1.json +28 -0
  27. package/src/contracts/doctor-report-v1.json +29 -0
  28. package/src/contracts/error-envelope-v1.json +24 -0
  29. package/src/contracts/generator-policy-v1.json +24 -0
  30. package/src/contracts/ownership-policy-v1.json +40 -0
  31. package/src/contracts/plan-schema-v1.json +115 -0
  32. package/src/family-identity.js +365 -0
  33. package/src/family-runtime.js +23 -0
  34. package/src/filesystem/apply-plan.js +44 -0
  35. package/src/filesystem/copy-path.js +28 -0
  36. package/src/filesystem/path-utils.js +67 -0
  37. package/src/filesystem/transaction-runner.js +41 -0
  38. package/src/filesystem/transaction.js +153 -0
  39. package/src/git/submodule.js +115 -0
  40. package/src/git/worktree.js +69 -0
  41. package/src/template/attach-planner-runtime.js +51 -0
  42. package/src/template/attach-planner.js +160 -0
  43. package/src/template/instance-runtime.js +463 -0
  44. package/src/template/lifecycle-metadata.js +99 -0
  45. package/src/template/lifecycle-policy.js +129 -0
  46. package/src/template/lifecycle-runtime.js +90 -0
  47. package/src/template/migration-planner.js +95 -0
  48. package/src/template/migration-runtime.js +247 -0
  49. package/src/template/ownership-metadata.js +79 -0
  50. package/src/template/ownership-policy.js +149 -0
  51. package/src/template/ownership-runtime.js +109 -0
  52. package/src/template/plan-schema.js +36 -0
  53. package/src/template/sync-planner-runtime.js +57 -0
  54. package/src/template/sync-planner.js +206 -0
  55. package/src/template/verification-runtime.js +154 -0
  56. package/src/validation/identity.js +41 -0
  57. package/src/validation/metadata.js +105 -0
  58. package/src/validation/security.js +67 -0
  59. package/src/validation/snapshot.js +47 -0
  60. package/template/.agents/skills/.strategic-design-skills-manifest.json +1 -1
  61. package/template/.agents/skills/yss-frontend-scaffold-generator/SKILL.md +2 -0
  62. package/template/.agents/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
  63. package/template/.agents/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +6 -0
  64. package/template/.agents/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +2 -0
  65. package/template/.agents/skills/yss-product-lifecycle/SKILL.md +2 -0
  66. package/template/.agents/skills/yss-ui/SKILL.md +2 -0
  67. package/template/.claude/skills/yss-frontend-scaffold-generator/SKILL.md +2 -0
  68. package/template/.claude/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
  69. package/template/.claude/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +6 -0
  70. package/template/.claude/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +2 -0
  71. package/template/.claude/skills/yss-product-lifecycle/SKILL.md +2 -0
  72. package/template/.claude/skills/yss-ui/SKILL.md +2 -0
  73. package/template/.codex/skills/yss-frontend-scaffold-generator/SKILL.md +2 -0
  74. package/template/.codex/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
  75. package/template/.codex/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +6 -0
  76. package/template/.codex/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +2 -0
  77. package/template/.codex/skills/yss-product-lifecycle/SKILL.md +2 -0
  78. package/template/.codex/skills/yss-ui/SKILL.md +2 -0
  79. package/template/.cursor/skills/yss-frontend-scaffold-generator/SKILL.md +2 -0
  80. package/template/.cursor/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
  81. package/template/.cursor/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +6 -0
  82. package/template/.cursor/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +2 -0
  83. package/template/.cursor/skills/yss-product-lifecycle/SKILL.md +2 -0
  84. package/template/.cursor/skills/yss-ui/SKILL.md +2 -0
  85. package/template/.pi/skills/yss-frontend-scaffold-generator/SKILL.md +2 -0
  86. package/template/.pi/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
  87. package/template/.pi/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +6 -0
  88. package/template/.pi/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +2 -0
  89. package/template/.pi/skills/yss-product-lifecycle/SKILL.md +2 -0
  90. package/template/.pi/skills/yss-ui/SKILL.md +2 -0
  91. package/template/.qoder/skills/yss-frontend-scaffold-generator/SKILL.md +2 -0
  92. package/template/.qoder/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
  93. package/template/.qoder/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +6 -0
  94. package/template/.qoder/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +2 -0
  95. package/template/.qoder/skills/yss-product-lifecycle/SKILL.md +2 -0
  96. package/template/.qoder/skills/yss-ui/SKILL.md +2 -0
  97. package/template/.trae/skills/yss-frontend-scaffold-generator/SKILL.md +2 -0
  98. package/template/.trae/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
  99. package/template/.trae/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +6 -0
  100. package/template/.trae/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +2 -0
  101. package/template/.trae/skills/yss-product-lifecycle/SKILL.md +2 -0
  102. package/template/.trae/skills/yss-ui/SKILL.md +2 -0
  103. package/template/CONTEXT.md +2 -0
  104. package/template/README.md +4 -0
  105. package/template/docs/agents/digital-human-roles.yaml +1 -0
  106. package/template/docs/process/frontend-backend-delivery.md +72 -0
  107. package/template/docs/process/implementation-repo-integration.md +2 -0
  108. package/template/docs/process/lifecycle-artifact-map.md +1 -0
  109. package/template/docs/process/lifecycle-registry-baseline.json +2 -1
  110. package/template/docs/process/lifecycle-registry.yaml +5 -0
  111. package/template/docs/process/schemas/backend-delivery-verification.schema.json +113 -0
  112. package/template/docs/process/schemas/backend-delivery.schema.json +301 -0
  113. package/template/docs/process/schemas/digital-human-task-package.schema.json +8 -0
  114. package/template/docs/process/schemas/frontend-delivery-acceptance.schema.json +200 -0
  115. package/template/docs/process/schemas/frontend-implementation-evidence.schema.json +3 -0
  116. package/template/docs/process/template-verification-profiles.yaml +6 -0
  117. package/template/docs/user-guide//346/210/230/346/234/257/350/256/276/350/256/241/345/255/220/351/241/271/347/233/256/347/224/250/346/210/267/346/211/213/345/206/214.md +20 -206
  118. package/template/docs/user-guide//347/224/250/346/210/267/346/211/213/345/206/214.md +63 -963
  119. package/template/docs/user-guide//347/224/250/346/210/267/346/211/213/345/206/214/347/264/242/345/274/225.md +11 -14
  120. package/template/docs/user-guide//350/256/276/345/244/207/345/200/237/347/224/250/350/264/257/347/251/277/346/241/210/344/276/213.md +127 -0
  121. package/template/scripts/backend-delivery +14 -0
  122. package/template/scripts/fixtures/backend-delivery/revision-server.mjs +11 -0
  123. package/template/scripts/instantiate-harness +69 -0
  124. package/template/scripts/lib/backend-delivery.mjs +164 -0
  125. package/template/scripts/lib/frontend-delivery-boundary.mjs +49 -0
  126. package/template/scripts/lib/frontend-delivery.mjs +73 -0
  127. package/template/scripts/lib/harness-execution-scope.mjs +52 -0
  128. package/template/scripts/lib/implementation-contract-compiler.mjs +12 -2
  129. package/template/scripts/lib/lifecycle-transition.mjs +7 -0
  130. package/template/scripts/lib/strategic-handoff-consumption.mjs +14 -7
  131. package/template/scripts/lib/strategic-handoff.mjs +1 -1
  132. package/template/scripts/lib/task-package.mjs +7 -0
  133. package/template/scripts/sync-strategic-handoff-tools +3 -1
  134. package/template/scripts/verify-frontend-delivery +9 -0
  135. package/template/scripts/verify-frontend-delivery-scenarios +131 -0
  136. package/template/scripts/verify-frontend-implementation-evidence +2 -0
  137. package/template/skills-lock.json +11 -11
  138. package/template.manifest.json +43 -1
  139. package/template.snapshot.json +5 -5
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+
3
+ const ERROR_SCHEMA_VERSION = 1;
4
+
5
+ const ERROR_RULES = [
6
+ [/模板路径越界|中间符号链接|目标父路径不是目录|符号链接或特殊文件/, "YSS_PATH_SAFETY"],
7
+ [/gitlink|git-submodule|detached HEAD|submodule/, "YSS_GIT_PROTECTED"],
8
+ [/模板家族|家族身份|多个模板家族|目标身份属于|profile 身份|身份不一致|身份文件格式非法|身份 metadata schema|未知或非法 profile|不支持跨家族迁移/, "YSS_FAMILY_IDENTITY_INVALID"],
9
+ [/ownership policy|user-owned|被 ownership policy 标记为 protected/, "YSS_OWNERSHIP_PROTECTED"],
10
+ [/模板快照|snapshotHash|templateCommit/, "YSS_SNAPSHOT_INVALID"],
11
+ [/模板元数据|metadataSchemaVersion|managedFilesManifestVersion/, "YSS_METADATA_INVALID"],
12
+ [/yss-project\.yaml|repository_mode|schema_version/, "YSS_IDENTITY_INVALID"],
13
+ [/迁移冲突|迁移目标|旧路径迁移冲突/, "YSS_MIGRATION_CONFLICT"],
14
+ [/unsafe|受管路径阻断|人工整理 Ticket/, "YSS_UNSAFE_PATH"],
15
+ [/不支持的参数|需要 --|需要一个值|--dry-run 与 --apply|必须显式传入/, "YSS_ARGUMENT_INVALID"],
16
+ [/目标目录|目标路径/, "YSS_TARGET_INVALID"],
17
+ ];
18
+
19
+ function classifyErrorCode(error) {
20
+ if (error && typeof error.code === "string" && error.code.startsWith("YSS_")) {
21
+ return error.code;
22
+ }
23
+
24
+ const message = String(error?.message || error || "");
25
+ for (const [pattern, code] of ERROR_RULES) {
26
+ if (pattern.test(message)) return code;
27
+ }
28
+ return "YSS_COMMAND_FAILED";
29
+ }
30
+
31
+ function normalizeError(error) {
32
+ return {
33
+ schemaVersion: ERROR_SCHEMA_VERSION,
34
+ ok: false,
35
+ error: {
36
+ code: classifyErrorCode(error),
37
+ message: String(error?.message || error || "未知错误"),
38
+ },
39
+ };
40
+ }
41
+
42
+ function serializeError(error, space = 2) {
43
+ return `${JSON.stringify(normalizeError(error), null, space)}\n`;
44
+ }
45
+
46
+ module.exports = {
47
+ ERROR_SCHEMA_VERSION,
48
+ ERROR_RULES,
49
+ classifyErrorCode,
50
+ normalizeError,
51
+ serializeError,
52
+ };
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+
3
+ const HELP_FLAGS = new Set(["--help", "-h", "-help"]);
4
+ const VERSION_FLAGS = new Set(["--version", "-v", "-version"]);
5
+
6
+ module.exports = {
7
+ HELP_FLAGS,
8
+ VERSION_FLAGS,
9
+ };
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+
3
+ function versionText(version) {
4
+ return `create-yss-spec ${version}`;
5
+ }
6
+
7
+ function helpText(version) {
8
+ return `create-yss-spec ${version}
9
+
10
+ USAGE
11
+ $ create-yss-spec [COMMAND] [OPTIONS]
12
+
13
+ COMMANDS
14
+ (default) 初始化新的模板实例仓库
15
+ attach 向已有项目补齐受管研发管理资产
16
+ sync 同步已有模板实例的受管资产
17
+ diff 只计算同步差异,不写入文件
18
+ doctor 检查模板实例、身份、Git 和安全状态
19
+ update 检查 npm 最新版本,如有更新则安装
20
+ upgrade update 的别名
21
+
22
+ OPTIONS
23
+ --project-name <name> 项目名称;init 不传则进入交互输入
24
+ --business-domain <domain> 业务领域;init 不传则进入交互输入
25
+ --team-size <size> 团队规模;init 不传则可留空,默认「待补充」
26
+ --target-dir <dir> 目标目录;init 不传则进入交互输入,sync/diff/doctor 默认为当前目录
27
+ --issue-tracker github|gitlab 默认 issue tracker 偏好(默认 github)
28
+ --dry-run 兼容预演模式,不写入文件
29
+ --plan sync:输出结构化文本计划,不写入文件
30
+ --json sync/diff/doctor:输出机器可读 JSON,不写入文件
31
+ --apply attach 确认执行写入;不能与 --dry-run 同时使用
32
+ --force init:允许清空非空目录后重新生成
33
+ attach / sync:覆盖受管冲突文件;unsafe 路径始终阻断
34
+ update:即使已是最新时仍重新安装;npx / 源码目录仍不覆盖
35
+ --git-init 初始化完成后执行 git init
36
+ --include-example-docs 显式保留示例文档(默认开启)
37
+ --no-example-docs 不生成示例文档
38
+ -h, --help 显示本帮助信息
39
+ -v, --version 显示 CLI 版本
40
+
41
+ EXAMPLES
42
+ $ npm create yss-spec@latest
43
+ $ npx create-yss-spec@latest --help
44
+ $ npx create-yss-spec@latest --version
45
+ $ npx create-yss-spec@latest \\
46
+ --project-name "Acme Spec Repo" \\
47
+ --business-domain "Investment Research" \\
48
+ --team-size "12" \\
49
+ --target-dir "./acme-spec-repo" \\
50
+ --issue-tracker github \\
51
+ --git-init
52
+ $ npx create-yss-spec@latest attach \\
53
+ --target-dir . \\
54
+ --project-name "Acme Application" \\
55
+ --business-domain "Data Platform" \\
56
+ --dry-run
57
+ $ npx create-yss-spec@latest attach \\
58
+ --target-dir . \\
59
+ --project-name "Acme Application" \\
60
+ --business-domain "Data Platform" \\
61
+ --apply
62
+ $ npx create-yss-spec@latest sync
63
+ $ npx create-yss-spec@latest sync --dry-run
64
+ $ npx create-yss-spec@latest sync --plan
65
+ $ npx create-yss-spec@latest sync --json
66
+ $ npx create-yss-spec@latest diff
67
+ $ npx create-yss-spec@latest diff --json
68
+ $ npx create-yss-spec@latest doctor
69
+ $ npx create-yss-spec@latest doctor --json
70
+ $ npx create-yss-spec@latest sync --target-dir . --force
71
+ $ npx create-yss-spec update
72
+ $ npx create-yss-spec update --dry-run
73
+ $ npx create-yss-spec upgrade
74
+
75
+ LEARN MORE
76
+ 仓库 README:https://github.com/iloveZzz/create-yss-spec#readme
77
+ 使用手册:https://github.com/iloveZzz/create-yss-spec/blob/main/docs/user-guide/create-yss-spec-cli-guide.md
78
+ 模板仓库:https://github.com/iloveZzz/yss-spec-project-template
79
+ `;
80
+ }
81
+
82
+ function printVersion(version, stdout = process.stdout) {
83
+ stdout.write(`${versionText(version)}\n`);
84
+ }
85
+
86
+ function printHelp(version, stdout = process.stdout) {
87
+ stdout.write(`${helpText(version)}\n`);
88
+ }
89
+
90
+ module.exports = {
91
+ versionText,
92
+ helpText,
93
+ printVersion,
94
+ printHelp,
95
+ };
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+
3
+ const path = require("node:path");
4
+ const packageManifest = require("../../package.json");
5
+ const instanceRuntime = require("../template/instance-runtime");
6
+ const {
7
+ decorateMetadataOwnership,
8
+ } = require("../template/ownership-metadata");
9
+ const {
10
+ decorateMetadataLifecycle,
11
+ } = require("../template/lifecycle-metadata");
12
+
13
+ // Composition-root decorator: all CLI metadata writes persist ownership and
14
+ // lifecycle-policy baselines without duplicating cross-cutting concerns in
15
+ // init/attach/sync command implementations.
16
+ const writeTemplateMetadataBase = instanceRuntime.writeTemplateMetadata;
17
+ instanceRuntime.writeTemplateMetadata = function writePolicyAwareTemplateMetadata(
18
+ targetDir,
19
+ metadata,
20
+ transaction = null,
21
+ ) {
22
+ const ownershipAware = decorateMetadataOwnership(metadata);
23
+ const lifecycleAware = decorateMetadataLifecycle(ownershipAware);
24
+ return writeTemplateMetadataBase(targetDir, lifecycleAware, transaction);
25
+ };
26
+
27
+ const { projectDoctor } = require("../api/project-doctor");
28
+ const { runAttach } = require("../commands/attach");
29
+ const { runDiff } = require("../commands/diff");
30
+ const { renderDoctorText } = require("../commands/doctor");
31
+ const { runInit } = require("../commands/init");
32
+ const { runSync } = require("../commands/sync");
33
+ const { runUpdateCommand } = require("../commands/update");
34
+ const { parseArgs } = require("./args");
35
+ const { printHelp, printVersion } = require("./help");
36
+ const { resolveCommand } = require("./router");
37
+
38
+ const PACKAGE_ROOT = path.resolve(__dirname, "../..");
39
+
40
+ async function runCli(argv = []) {
41
+ const route = resolveCommand(argv);
42
+
43
+ if (route.command === "help") {
44
+ printHelp(packageManifest.version);
45
+ return;
46
+ }
47
+ if (route.command === "version") {
48
+ printVersion(packageManifest.version);
49
+ return;
50
+ }
51
+ if (route.command === "update") {
52
+ return runUpdateCommand(route.args, {
53
+ packageRoot: PACKAGE_ROOT,
54
+ currentVersion: packageManifest.version,
55
+ });
56
+ }
57
+ if (route.command === "attach") {
58
+ return runAttach(route.args);
59
+ }
60
+ if (route.command === "sync") {
61
+ return runSync(route.args);
62
+ }
63
+ if (route.command === "doctor") {
64
+ const options = parseArgs(route.args);
65
+ const report = projectDoctor({ targetDir: options.targetDir || "." });
66
+ if (options.json) {
67
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
68
+ } else {
69
+ process.stdout.write(renderDoctorText(report));
70
+ }
71
+ return report;
72
+ }
73
+ if (route.command === "diff") {
74
+ return runDiff(route.args);
75
+ }
76
+
77
+ return runInit(route.args);
78
+ }
79
+
80
+ module.exports = {
81
+ runCli,
82
+ };
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+
3
+ const { normalizePlan } = require("../template/plan-schema");
4
+
5
+ function ownershipSuffix(item) {
6
+ return item.ownership ? ` [${item.ownership}]` : "";
7
+ }
8
+
9
+ function lifecycleSuffix(item) {
10
+ const parts = [];
11
+ if (item.mergeStrategy) parts.push(`merge=${item.mergeStrategy}`);
12
+ if (item.generatorId) {
13
+ parts.push(
14
+ `generator=${item.generatorId}@${item.generatorVersion || "?"}`,
15
+ );
16
+ }
17
+ return parts.length > 0 ? ` [${parts.join(",")}]` : "";
18
+ }
19
+
20
+ function renderPlanText(input) {
21
+ const plan = normalizePlan(input);
22
+ const lines = [
23
+ `${plan.operation} plan`,
24
+ `目标目录:${plan.targetDir || "-"}`,
25
+ `模板版本:${plan.template?.from || "unknown"} -> ${plan.template?.to || "unknown"}`,
26
+ `blocked: ${plan.blocked ? "yes" : "no"}`,
27
+ ];
28
+
29
+ for (const change of plan.changes) {
30
+ lines.push(
31
+ `${change.action}: ${change.path}${ownershipSuffix(change)}${lifecycleSuffix(change)}`,
32
+ );
33
+ }
34
+ for (const conflict of plan.conflicts) {
35
+ const forceable = conflict.forceable ? " [forceable]" : "";
36
+ lines.push(
37
+ `conflict${forceable}: ${conflict.path}${ownershipSuffix(conflict)}${lifecycleSuffix(conflict)} (${conflict.reason})`,
38
+ );
39
+ }
40
+ for (const unsafe of plan.unsafe) {
41
+ lines.push(
42
+ `unsafe: ${unsafe.path}${ownershipSuffix(unsafe)}${lifecycleSuffix(unsafe)} (${unsafe.reason})`,
43
+ );
44
+ }
45
+ for (const warning of plan.warnings) {
46
+ lines.push(`warning: ${warning}`);
47
+ }
48
+
49
+ const stats = Object.entries(plan.stats || {});
50
+ if (stats.length > 0) {
51
+ lines.push(`统计:${stats.map(([key, value]) => `${key}=${value}`).join(",")}`);
52
+ }
53
+
54
+ return `${lines.join("\n")}\n`;
55
+ }
56
+
57
+ module.exports = {
58
+ ownershipSuffix,
59
+ lifecycleSuffix,
60
+ renderPlanText,
61
+ };
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+
3
+ const readline = require("node:readline/promises");
4
+
5
+ function normalizeInteractiveOptions(options, values) {
6
+ return {
7
+ ...values,
8
+ issueTracker: options.issueTracker || "github",
9
+ dryRun: Boolean(options.dryRun),
10
+ force: Boolean(options.force),
11
+ gitInit: Boolean(options.gitInit),
12
+ includeExampleDocs:
13
+ options.includeExampleDocs === undefined
14
+ ? true
15
+ : Boolean(options.includeExampleDocs),
16
+ };
17
+ }
18
+
19
+ async function promptForMissingOptions(options, deps = {}) {
20
+ const stdin = deps.stdin || process.stdin;
21
+ const stdout = deps.stdout || process.stdout;
22
+
23
+ if (!stdin.isTTY) {
24
+ return promptFromBufferedInput(options, { stdin, stdout });
25
+ }
26
+
27
+ const rl = readline.createInterface({
28
+ input: stdin,
29
+ output: stdout,
30
+ });
31
+
32
+ try {
33
+ const projectName =
34
+ options.projectName || (await rl.question("项目名称: ")).trim();
35
+ const businessDomain =
36
+ options.businessDomain || (await rl.question("业务领域: ")).trim();
37
+ const teamSizeInput =
38
+ options.teamSize !== undefined
39
+ ? options.teamSize
40
+ : await rl.question("团队规模(可留空): ");
41
+ const targetDir =
42
+ options.targetDir || (await rl.question("目标目录: ")).trim();
43
+
44
+ return normalizeInteractiveOptions(options, {
45
+ projectName,
46
+ businessDomain,
47
+ teamSize: (teamSizeInput || "").trim() || "待补充",
48
+ targetDir,
49
+ });
50
+ } finally {
51
+ rl.close();
52
+ }
53
+ }
54
+
55
+ async function promptFromBufferedInput(options, deps = {}) {
56
+ const stdin = deps.stdin || process.stdin;
57
+ const stdout = deps.stdout || process.stdout;
58
+ const chunks = [];
59
+
60
+ for await (const chunk of stdin) {
61
+ chunks.push(chunk);
62
+ }
63
+
64
+ const answers = Buffer.concat(chunks).toString("utf8").split(/\r?\n/);
65
+ let answerIndex = 0;
66
+ const ask = (label) => {
67
+ stdout.write(`${label}: `);
68
+ const value = answers[answerIndex] ?? "";
69
+ answerIndex += 1;
70
+ return value.trim();
71
+ };
72
+
73
+ const projectName = options.projectName || ask("项目名称");
74
+ const businessDomain = options.businessDomain || ask("业务领域");
75
+ const teamSizeInput =
76
+ options.teamSize !== undefined ? options.teamSize : ask("团队规模(可留空)");
77
+ const targetDir = options.targetDir || ask("目标目录");
78
+
79
+ return normalizeInteractiveOptions(options, {
80
+ projectName,
81
+ businessDomain,
82
+ teamSize: teamSizeInput || "待补充",
83
+ targetDir,
84
+ });
85
+ }
86
+
87
+ module.exports = {
88
+ normalizeInteractiveOptions,
89
+ promptForMissingOptions,
90
+ promptFromBufferedInput,
91
+ };
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+
3
+ const { UPDATE_COMMANDS } = require("../self-update");
4
+ const { HELP_FLAGS, VERSION_FLAGS } = require("./flags");
5
+
6
+ function argvIncludesFlag(argv, flags) {
7
+ return argv.some((arg) => flags.has(arg));
8
+ }
9
+
10
+ function resolveCommand(argv = []) {
11
+ if (argvIncludesFlag(argv, HELP_FLAGS)) {
12
+ return { command: "help", args: [] };
13
+ }
14
+ if (argvIncludesFlag(argv, VERSION_FLAGS)) {
15
+ return { command: "version", args: [] };
16
+ }
17
+
18
+ const [head, ...rest] = argv;
19
+ if (head === "sync") {
20
+ return { command: "sync", args: rest };
21
+ }
22
+ if (head === "attach") {
23
+ return { command: "attach", args: rest };
24
+ }
25
+ if (head === "doctor") {
26
+ return { command: "doctor", args: rest };
27
+ }
28
+ if (head === "diff") {
29
+ return { command: "diff", args: rest };
30
+ }
31
+ if (UPDATE_COMMANDS.has(head)) {
32
+ return { command: "update", alias: head, args: rest };
33
+ }
34
+
35
+ return { command: "init", args: argv };
36
+ }
37
+
38
+ module.exports = {
39
+ argvIncludesFlag,
40
+ resolveCommand,
41
+ };