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.
- package/README.md +117 -91
- package/bin/create-yss-spec.js +11 -3
- package/package.json +9 -2
- package/src/api/_sync-service.js +191 -0
- package/src/api/index.js +22 -0
- package/src/api/project-diff.js +9 -0
- package/src/api/project-doctor.js +10 -0
- package/src/api/template-apply.js +10 -0
- package/src/api/template-plan.js +9 -0
- package/src/cli/args.js +60 -0
- package/src/cli/error-output.js +52 -0
- package/src/cli/flags.js +9 -0
- package/src/cli/help.js +95 -0
- package/src/cli/index.js +82 -0
- package/src/cli/plan-output.js +61 -0
- package/src/cli/prompts.js +91 -0
- package/src/cli/router.js +41 -0
- package/src/cli.js +5 -2253
- package/src/commands/attach.js +280 -0
- package/src/commands/diff.js +23 -0
- package/src/commands/doctor.js +417 -0
- package/src/commands/init.js +193 -0
- package/src/commands/sync.js +146 -0
- package/src/commands/update.js +14 -0
- package/src/contracts/apply-result-v1.json +45 -0
- package/src/contracts/customization-policy-v1.json +28 -0
- package/src/contracts/doctor-report-v1.json +29 -0
- package/src/contracts/error-envelope-v1.json +24 -0
- package/src/contracts/generator-policy-v1.json +24 -0
- package/src/contracts/ownership-policy-v1.json +40 -0
- package/src/contracts/plan-schema-v1.json +115 -0
- package/src/family-identity.js +365 -0
- package/src/family-runtime.js +23 -0
- package/src/filesystem/apply-plan.js +44 -0
- package/src/filesystem/copy-path.js +28 -0
- package/src/filesystem/path-utils.js +67 -0
- package/src/filesystem/transaction-runner.js +41 -0
- package/src/filesystem/transaction.js +153 -0
- package/src/git/submodule.js +115 -0
- package/src/git/worktree.js +69 -0
- package/src/template/attach-planner-runtime.js +51 -0
- package/src/template/attach-planner.js +160 -0
- package/src/template/instance-runtime.js +463 -0
- package/src/template/lifecycle-metadata.js +99 -0
- package/src/template/lifecycle-policy.js +129 -0
- package/src/template/lifecycle-runtime.js +90 -0
- package/src/template/migration-planner.js +95 -0
- package/src/template/migration-runtime.js +247 -0
- package/src/template/ownership-metadata.js +79 -0
- package/src/template/ownership-policy.js +149 -0
- package/src/template/ownership-runtime.js +109 -0
- package/src/template/plan-schema.js +36 -0
- package/src/template/sync-planner-runtime.js +57 -0
- package/src/template/sync-planner.js +206 -0
- package/src/template/verification-runtime.js +154 -0
- package/src/validation/identity.js +41 -0
- package/src/validation/metadata.js +105 -0
- package/src/validation/security.js +67 -0
- package/src/validation/snapshot.js +47 -0
- package/template/.agents/skills/.strategic-design-skills-manifest.json +1 -1
- package/template/.agents/skills/yss-frontend-scaffold-generator/SKILL.md +2 -0
- package/template/.agents/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
- package/template/.agents/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +6 -0
- package/template/.agents/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +2 -0
- package/template/.agents/skills/yss-product-lifecycle/SKILL.md +2 -0
- package/template/.agents/skills/yss-ui/SKILL.md +2 -0
- package/template/.claude/skills/yss-frontend-scaffold-generator/SKILL.md +2 -0
- package/template/.claude/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
- package/template/.claude/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +6 -0
- package/template/.claude/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +2 -0
- package/template/.claude/skills/yss-product-lifecycle/SKILL.md +2 -0
- package/template/.claude/skills/yss-ui/SKILL.md +2 -0
- package/template/.codex/skills/yss-frontend-scaffold-generator/SKILL.md +2 -0
- package/template/.codex/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
- package/template/.codex/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +6 -0
- package/template/.codex/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +2 -0
- package/template/.codex/skills/yss-product-lifecycle/SKILL.md +2 -0
- package/template/.codex/skills/yss-ui/SKILL.md +2 -0
- package/template/.cursor/skills/yss-frontend-scaffold-generator/SKILL.md +2 -0
- package/template/.cursor/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
- package/template/.cursor/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +6 -0
- package/template/.cursor/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +2 -0
- package/template/.cursor/skills/yss-product-lifecycle/SKILL.md +2 -0
- package/template/.cursor/skills/yss-ui/SKILL.md +2 -0
- package/template/.pi/skills/yss-frontend-scaffold-generator/SKILL.md +2 -0
- package/template/.pi/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
- package/template/.pi/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +6 -0
- package/template/.pi/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +2 -0
- package/template/.pi/skills/yss-product-lifecycle/SKILL.md +2 -0
- package/template/.pi/skills/yss-ui/SKILL.md +2 -0
- package/template/.qoder/skills/yss-frontend-scaffold-generator/SKILL.md +2 -0
- package/template/.qoder/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
- package/template/.qoder/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +6 -0
- package/template/.qoder/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +2 -0
- package/template/.qoder/skills/yss-product-lifecycle/SKILL.md +2 -0
- package/template/.qoder/skills/yss-ui/SKILL.md +2 -0
- package/template/.trae/skills/yss-frontend-scaffold-generator/SKILL.md +2 -0
- package/template/.trae/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
- package/template/.trae/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +6 -0
- package/template/.trae/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +2 -0
- package/template/.trae/skills/yss-product-lifecycle/SKILL.md +2 -0
- package/template/.trae/skills/yss-ui/SKILL.md +2 -0
- package/template/CONTEXT.md +2 -0
- package/template/README.md +4 -0
- package/template/docs/agents/digital-human-roles.yaml +1 -0
- package/template/docs/process/frontend-backend-delivery.md +72 -0
- package/template/docs/process/implementation-repo-integration.md +2 -0
- package/template/docs/process/lifecycle-artifact-map.md +1 -0
- package/template/docs/process/lifecycle-registry-baseline.json +2 -1
- package/template/docs/process/lifecycle-registry.yaml +5 -0
- package/template/docs/process/schemas/backend-delivery-verification.schema.json +113 -0
- package/template/docs/process/schemas/backend-delivery.schema.json +301 -0
- package/template/docs/process/schemas/digital-human-task-package.schema.json +8 -0
- package/template/docs/process/schemas/frontend-delivery-acceptance.schema.json +200 -0
- package/template/docs/process/schemas/frontend-implementation-evidence.schema.json +3 -0
- package/template/docs/process/template-verification-profiles.yaml +6 -0
- 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
- package/template/docs/user-guide//347/224/250/346/210/267/346/211/213/345/206/214.md +63 -963
- 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
- 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
- package/template/scripts/backend-delivery +14 -0
- package/template/scripts/fixtures/backend-delivery/revision-server.mjs +11 -0
- package/template/scripts/instantiate-harness +69 -0
- package/template/scripts/lib/backend-delivery.mjs +164 -0
- package/template/scripts/lib/frontend-delivery-boundary.mjs +49 -0
- package/template/scripts/lib/frontend-delivery.mjs +73 -0
- package/template/scripts/lib/harness-execution-scope.mjs +52 -0
- package/template/scripts/lib/implementation-contract-compiler.mjs +12 -2
- package/template/scripts/lib/lifecycle-transition.mjs +7 -0
- package/template/scripts/lib/strategic-handoff-consumption.mjs +14 -7
- package/template/scripts/lib/strategic-handoff.mjs +1 -1
- package/template/scripts/lib/task-package.mjs +7 -0
- package/template/scripts/sync-strategic-handoff-tools +3 -1
- package/template/scripts/verify-frontend-delivery +9 -0
- package/template/scripts/verify-frontend-delivery-scenarios +131 -0
- package/template/scripts/verify-frontend-implementation-evidence +2 -0
- package/template/skills-lock.json +11 -11
- package/template.manifest.json +43 -1
- 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
|
+
};
|
package/src/cli/flags.js
ADDED
package/src/cli/help.js
ADDED
|
@@ -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
|
+
};
|
package/src/cli/index.js
ADDED
|
@@ -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
|
+
};
|