project-auto-wizard 0.13.2 → 0.14.0
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 +1 -1
- package/bin/project-auto-wizard.js +18 -4
- package/package.json +2 -2
- package/payload/config/breaking-changes.json +28 -14
- package/payload/config/wizard-prompts.yml +102 -46
- package/payload/flutter-app/android/fastlane/Fastfile.playstore +44 -32
- package/payload/flutter-app/ios/ExportOptions.plist +8 -8
- package/payload/flutter-app/ios/fastlane/Fastfile +57 -43
- package/payload/scripts/changelog_manager.py +296 -257
- package/payload/scripts/issue_helper.py +54 -50
- package/payload/scripts/messages.py +2279 -0
- package/payload/scripts/truncate_release_notes.py +7 -6
- package/payload/scripts/version_manager.py +64 -61
- package/payload/version.yml.template +2 -1
- package/payload/workflows/common/PROJECT-COMMON-AI-PR-SUMMARY.yaml +17 -12
- package/payload/workflows/common/PROJECT-COMMON-AUTO-CHANGELOG-CONTROL.yaml +59 -34
- package/payload/workflows/common/PROJECT-COMMON-ISSUE-HELPER.yaml +17 -15
- package/payload/workflows/common/PROJECT-COMMON-README-VERSION-UPDATE.yaml +17 -10
- package/payload/workflows/common/PROJECT-COMMON-RELEASE-PUBLISH.yaml +54 -44
- package/payload/workflows/common/PROJECT-COMMON-VERSION-CONTROL.yaml +24 -17
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-FIREBASE-CICD.yaml +194 -184
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-PLAYSTORE-CICD.yaml +226 -213
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-SELFHOSTED-CICD.yaml +46 -42
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-TEST-APK.yaml +334 -241
- package/payload/workflows/flutter/PROJECT-FLUTTER-APP-BUILD-TRIGGER.yaml +152 -128
- package/payload/workflows/flutter/PROJECT-FLUTTER-CI.yaml +246 -188
- package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TEST-TESTFLIGHT.yaml +353 -238
- package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TESTFLIGHT.yaml +96 -85
- package/payload/workflows/go/PROJECT-GO-CI.yaml +47 -36
- package/payload/workflows/go/PROJECT-GO-PR-PREVIEW.yaml +945 -508
- package/payload/workflows/go/PROJECT-GO-SIMPLE-CICD.yaml +155 -133
- package/payload/workflows/next/PROJECT-NEXT-CI.yaml +76 -60
- package/payload/workflows/next/PROJECT-NEXT-CICD.yaml +97 -77
- package/payload/workflows/python/PROJECT-PYTHON-CI.yaml +62 -47
- package/payload/workflows/python/PROJECT-PYTHON-PR-PREVIEW.yaml +942 -505
- package/payload/workflows/python/PROJECT-PYTHON-SIMPLE-CICD.yaml +148 -126
- package/payload/workflows/react/PROJECT-REACT-CI.yaml +86 -70
- package/payload/workflows/react/PROJECT-REACT-CICD.yaml +112 -92
- package/payload/workflows/spring/PROJECT-SPRING-CI.yml +112 -82
- package/payload/workflows/spring/server-deploy/PROJECT-SPRING-NONSTOP-NGINX-CICD.yaml +180 -150
- package/payload/workflows/spring/server-deploy/PROJECT-SPRING-NONSTOP-TRAEFIK-CICD.yaml +161 -131
- package/payload/workflows/spring/server-deploy/PROJECT-SPRING-PR-PREVIEW.yaml +970 -549
- package/payload/workflows/spring/server-deploy/PROJECT-SPRING-SIMPLE-CICD.yaml +193 -162
- package/src/cli/args.js +98 -52
- package/src/cli/help.js +8 -41
- package/src/commands/doctor.js +85 -89
- package/src/commands/dry-run.js +63 -43
- package/src/commands/full.js +79 -77
- package/src/commands/install-settings.js +16 -15
- package/src/commands/interactive-flutter.js +20 -19
- package/src/commands/interactive.js +135 -132
- package/src/commands/purge.js +47 -42
- package/src/commands/status.js +36 -36
- package/src/commands/uninstall.js +43 -41
- package/src/context.js +12 -11
- package/src/core/assets.js +12 -11
- package/src/core/baseline.js +27 -27
- package/src/core/branches.js +29 -27
- package/src/core/branding.js +7 -6
- package/src/core/breaking-check.js +22 -21
- package/src/core/breaking.js +15 -8
- package/src/core/copy/app-files.js +5 -4
- package/src/core/copy/flutter-app.js +8 -8
- package/src/core/copy/gitignore.js +58 -50
- package/src/core/copy/readme.js +45 -35
- package/src/core/copy/simple.js +16 -15
- package/src/core/copy/workflows.js +131 -124
- package/src/core/deploy-style.js +51 -48
- package/src/core/detect-fs.js +57 -57
- package/src/core/detect.js +67 -65
- package/src/core/errors.js +2 -2
- package/src/core/flutter-doctor.js +17 -16
- package/src/core/flutter-hooks.js +33 -32
- package/src/core/flutter-options.js +34 -33
- package/src/core/fsutil.js +12 -12
- package/src/core/installed-stores.js +4 -4
- package/src/core/logger.js +42 -40
- package/src/core/paths-resolve.js +88 -86
- package/src/core/paths.js +10 -10
- package/src/core/release-options.js +8 -8
- package/src/core/removal-exec.js +14 -13
- package/src/core/removal-plan.js +37 -37
- package/src/core/types.js +47 -47
- package/src/core/verify.js +44 -38
- package/src/core/version-yml.js +76 -68
- package/src/core/wizard-env.js +46 -46
- package/src/core/wizard-labels.js +63 -41
- package/src/i18n/catalog/en/cli.js +50 -0
- package/src/i18n/catalog/en/commands.js +187 -0
- package/src/i18n/catalog/en/copy.js +19 -0
- package/src/i18n/catalog/en/core.js +74 -0
- package/src/i18n/catalog/en/core2.js +15 -0
- package/src/i18n/catalog/en/ui.js +221 -0
- package/src/i18n/catalog/en.js +12 -0
- package/src/i18n/catalog/index.js +5 -0
- package/src/i18n/catalog/ko/cli.js +50 -0
- package/src/i18n/catalog/ko/commands.js +187 -0
- package/src/i18n/catalog/ko/copy.js +19 -0
- package/src/i18n/catalog/ko/core.js +74 -0
- package/src/i18n/catalog/ko/core2.js +15 -0
- package/src/i18n/catalog/ko/ui.js +221 -0
- package/src/i18n/catalog/ko.js +10 -0
- package/src/i18n/index.js +44 -0
- package/src/i18n/languages.js +9 -0
- package/src/index.js +128 -94
- package/src/ui/ansi.js +7 -7
- package/src/ui/banner.js +7 -6
- package/src/ui/env-plan.js +82 -81
- package/src/ui/prompts.js +97 -122
- package/src/ui/readline-engine.js +59 -58
- package/src/ui/status-cards.js +37 -31
- package/src/ui/summary.js +89 -88
package/src/ui/prompts.js
CHANGED
|
@@ -1,236 +1,211 @@
|
|
|
1
|
-
//
|
|
2
|
-
// node:readline
|
|
3
|
-
// ESC
|
|
4
|
-
// Ctrl+C
|
|
1
|
+
// Interactive prompt wrappers.
|
|
2
|
+
// Uses the in-house node:readline engine (@clack/prompts was removed because Enter hangs on Windows TTY).
|
|
3
|
+
// On ESC each function returns the CANCEL symbol -> the caller interprets it as default/stay.
|
|
4
|
+
// Ctrl+C / Ctrl+D make the engine reject with PromptAbortError -> run() catches it and exits with code 130.
|
|
5
5
|
import * as engine from "./readline-engine.js";
|
|
6
|
+
import { t } from "../i18n/index.js";
|
|
6
7
|
import { DEPLOY_STYLES, NO_DEPLOY_STYLE } from "../core/deploy-style.js";
|
|
7
8
|
import { TYPE_IDS } from "../core/types.js";
|
|
8
9
|
import { ENV_MODES, DEFAULT_ENV_MODE, STORE_PLATFORMS, DEPLOY_MODES, DEFAULT_DEPLOY_MODE, deployModeWarning } from "../core/flutter-options.js";
|
|
9
10
|
|
|
10
11
|
export const CANCEL = engine.CANCEL;
|
|
11
12
|
|
|
12
|
-
//
|
|
13
|
-
// again=true
|
|
14
|
-
//
|
|
13
|
+
// Mode selection - localized labels, returns the internal key. CANCEL on cancel.
|
|
14
|
+
// again=true is a re-entry after running a read-only mode (status/doctor) and returning to the menu -
|
|
15
|
+
// asking "what should we install?" again would feel odd, so the wording changes.
|
|
15
16
|
export async function selectMode({ again = false } = {}) {
|
|
16
17
|
return engine.select({
|
|
17
|
-
message: again ? "
|
|
18
|
+
message: again ? t("ui.prompts.mode.messageAgain") : t("ui.prompts.mode.message"),
|
|
18
19
|
options: [
|
|
19
|
-
{ value: "full", label: "
|
|
20
|
-
{ value: "uninstall", label: "
|
|
21
|
-
{ value: "status", label: "
|
|
22
|
-
{ value: "doctor", label: "
|
|
20
|
+
{ value: "full", label: t("ui.prompts.mode.full") },
|
|
21
|
+
{ value: "uninstall", label: t("ui.prompts.mode.uninstall") },
|
|
22
|
+
{ value: "status", label: t("ui.prompts.mode.status") },
|
|
23
|
+
{ value: "doctor", label: t("ui.prompts.mode.doctor") },
|
|
23
24
|
],
|
|
24
25
|
});
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
//
|
|
28
|
+
// Project confirmation menu (continue/edit/cancel).
|
|
28
29
|
export async function confirmProjectMenu() {
|
|
29
30
|
return engine.select({
|
|
30
|
-
message: "
|
|
31
|
+
message: t("ui.prompts.confirm.message"),
|
|
31
32
|
options: [
|
|
32
|
-
{ value: "continue", label: "
|
|
33
|
-
{ value: "edit", label: "
|
|
34
|
-
{ value: "cancel", label: "
|
|
33
|
+
{ value: "continue", label: t("ui.prompts.confirm.continue") },
|
|
34
|
+
{ value: "edit", label: t("ui.prompts.confirm.edit") },
|
|
35
|
+
{ value: "cancel", label: t("ui.prompts.confirm.cancel") },
|
|
35
36
|
],
|
|
36
37
|
});
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
// showOptions
|
|
40
|
+
// Edit menu items - showFlutter exposes env mode / store targets / deploy mode only for the Flutter type.
|
|
41
|
+
// Split into a pure function so labels and order can be tested.
|
|
42
|
+
// showOptions exposes the optional-workflow toggles (auto version bump, Copilot) - a saved value skips the first question, so it is changed here.
|
|
42
43
|
export function editMenuOptions({ showFlutter = false, showOptions = false } = {}) {
|
|
43
44
|
const options = [
|
|
44
|
-
{ value: "type", label: "
|
|
45
|
-
{ value: "version", label: "
|
|
46
|
-
{ value: "branch", label: "
|
|
45
|
+
{ value: "type", label: t("ui.prompts.edit.type") },
|
|
46
|
+
{ value: "version", label: t("ui.prompts.edit.version") },
|
|
47
|
+
{ value: "branch", label: t("ui.prompts.edit.branch") },
|
|
47
48
|
];
|
|
48
49
|
if (showOptions) {
|
|
49
|
-
options.push({ value: "semverAuto", label: "
|
|
50
|
-
options.push({ value: "copilotAi", label: "
|
|
50
|
+
options.push({ value: "semverAuto", label: t("ui.prompts.edit.semverAuto") });
|
|
51
|
+
options.push({ value: "copilotAi", label: t("ui.prompts.edit.copilotAi") });
|
|
51
52
|
}
|
|
52
53
|
if (showFlutter) {
|
|
53
|
-
options.push({ value: "envMode", label: "
|
|
54
|
-
options.push({ value: "flutterStore", label: "
|
|
55
|
-
options.push({ value: "deployMode", label: "
|
|
54
|
+
options.push({ value: "envMode", label: t("ui.prompts.edit.envMode") });
|
|
55
|
+
options.push({ value: "flutterStore", label: t("ui.prompts.edit.flutterStore") });
|
|
56
|
+
options.push({ value: "deployMode", label: t("ui.prompts.edit.deployMode") });
|
|
56
57
|
}
|
|
57
|
-
options.push({ value: "done", label: "
|
|
58
|
+
options.push({ value: "done", label: t("ui.prompts.edit.done") });
|
|
58
59
|
return options;
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
//
|
|
62
|
+
// Edit menu - which item to fix.
|
|
62
63
|
export async function editMenu({ showFlutter = false, showOptions = false } = {}) {
|
|
63
|
-
return engine.select({ message: "
|
|
64
|
+
return engine.select({ message: t("ui.prompts.edit.message"), options: editMenuOptions({ showFlutter, showOptions }) });
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
//
|
|
67
|
+
// Interactive type choices - built from the same registry as the CLI validation list (VALID_TYPES).
|
|
67
68
|
export const ALL_TYPES = TYPE_IDS;
|
|
68
69
|
|
|
69
|
-
//
|
|
70
|
+
// Type multi-select.
|
|
70
71
|
export async function selectTypes(current = []) {
|
|
71
72
|
return engine.multiselect({
|
|
72
|
-
message: "
|
|
73
|
-
options: ALL_TYPES.map((
|
|
73
|
+
message: t("ui.prompts.types.select"),
|
|
74
|
+
options: ALL_TYPES.map((type) => ({ value: type, label: type })),
|
|
74
75
|
initialValues: current.length ? current : ["basic"],
|
|
75
76
|
required: true,
|
|
76
77
|
});
|
|
77
78
|
}
|
|
78
79
|
|
|
79
|
-
//
|
|
80
|
-
// "
|
|
81
|
-
//
|
|
80
|
+
// Confirm types right after detection. Unlike selectTypes it appends the evidence file to the label
|
|
81
|
+
// to show "why it was decided this way" - the user can only judge right or wrong when the evidence is visible.
|
|
82
|
+
// If detection is right, a single Enter finishes it.
|
|
82
83
|
export async function confirmTypes({ types = [], markers = null } = {}) {
|
|
83
84
|
const detected = new Set(types);
|
|
84
85
|
engine.note(
|
|
85
|
-
"
|
|
86
|
-
"
|
|
87
|
-
"프로젝트 타입 확정",
|
|
86
|
+
t("ui.prompts.types.confirmNote"),
|
|
87
|
+
t("ui.prompts.types.confirmTitle"),
|
|
88
88
|
);
|
|
89
89
|
return engine.multiselect({
|
|
90
|
-
message: "
|
|
91
|
-
options: ALL_TYPES.map((
|
|
92
|
-
const marker = markers?.get?.(
|
|
93
|
-
//
|
|
94
|
-
return { value:
|
|
90
|
+
message: t("ui.prompts.types.confirmMessage"),
|
|
91
|
+
options: ALL_TYPES.map((type) => {
|
|
92
|
+
const marker = markers?.get?.(type);
|
|
93
|
+
// Only detected types get evidence - the rest are just listed as candidates.
|
|
94
|
+
return { value: type, label: detected.has(type) && marker ? t("ui.prompts.types.withMarker", { type, marker }) : type };
|
|
95
95
|
}),
|
|
96
96
|
initialValues: types.length ? types : ["basic"],
|
|
97
97
|
required: true,
|
|
98
98
|
});
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
// "
|
|
105
|
-
//
|
|
106
|
-
// nonstop=false
|
|
101
|
+
// Deploy style selection. Server-deploy CD workflows are alternatives to each other, so only one is used.
|
|
102
|
+
// Only the chosen one is installed and its push trigger is enabled - previously all four were installed with only SIMPLE on,
|
|
103
|
+
// so anyone wanting zero-downtime had to edit the YAML by hand after install.
|
|
104
|
+
// "No server deploy" excludes server-deploy workflows (CD / PR preview) for every type - a choice for projects
|
|
105
|
+
// that do not deploy to a server (frontend-only, libraries, ...).
|
|
106
|
+
// With nonstop=false (no selected type has a zero-downtime workflow) the unusable zero-downtime choices are left out.
|
|
107
107
|
export async function selectDeployStyle({ nonstop = true } = {}) {
|
|
108
108
|
engine.note(
|
|
109
|
-
"
|
|
110
|
-
"
|
|
111
|
-
"\"서버 배포 안 함\"을 고르면 모든 타입에서 CD와 PR 프리뷰를 함께 제외합니다.\n" +
|
|
112
|
-
"무중단 배포는 spring에만 있습니다 — 다른 타입(python·go·react·next)은 단일 서버 배포로 설치됩니다.",
|
|
113
|
-
"배포 방식",
|
|
109
|
+
t("ui.prompts.deployStyle.note"),
|
|
110
|
+
t("ui.prompts.deployStyle.title"),
|
|
114
111
|
);
|
|
115
112
|
return engine.select({
|
|
116
|
-
message: "
|
|
113
|
+
message: t("ui.prompts.deployStyle.message"),
|
|
117
114
|
options: [
|
|
118
|
-
|
|
119
|
-
{ value:
|
|
115
|
+
// label may be a plain string or a lazy function depending on the registry
|
|
116
|
+
...DEPLOY_STYLES.filter((s) => nonstop || s.value === "simple").map((s) => ({ value: s.value, label: typeof s.label === "function" ? s.label() : s.label })),
|
|
117
|
+
{ value: NO_DEPLOY_STYLE, label: t("ui.prompts.deployStyle.none") },
|
|
120
118
|
],
|
|
121
119
|
});
|
|
122
120
|
}
|
|
123
121
|
|
|
124
|
-
// ── Flutter
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
const STORE_LABELS = {
|
|
133
|
-
android: "Android — Google Play Store (fastlane)",
|
|
134
|
-
ios: "iOS — TestFlight / App Store Connect (fastlane)",
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
const DEPLOY_MODE_LABELS = {
|
|
138
|
-
android: {
|
|
139
|
-
store_only: "store_only — internal 트랙에 업로드 (기본)",
|
|
140
|
-
store_prepare: "store_prepare — production 트랙에 draft로 업로드 (Play Console에서 직접 출시)",
|
|
141
|
-
store_submit: "store_submit — production 심사 제출까지",
|
|
142
|
-
},
|
|
143
|
-
ios: {
|
|
144
|
-
store_only: "store_only — TestFlight 업로드 (기본)",
|
|
145
|
-
store_prepare: "store_prepare — 앱 버전·메타데이터 준비까지 (심사 제출 안 함)",
|
|
146
|
-
store_submit: "store_submit — 심사 제출까지",
|
|
147
|
-
},
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
const PLATFORM_TITLES = { android: "Android (Play Store)", ios: "iOS (TestFlight)" };
|
|
122
|
+
// ── Flutter options ──────────────────────────────────────
|
|
123
|
+
// interactive.js asks these only when the project types include flutter. All three functions return CANCEL
|
|
124
|
+
// as is on cancel (ESC); the "ESC = default" handling is up to the caller (same convention as selectDeployStyle).
|
|
125
|
+
// Labels are resolved lazily via t() so they follow the language chosen at run time.
|
|
126
|
+
const envModeLabel = (value) => t(`ui.prompts.envMode.label.${value}`);
|
|
127
|
+
const storeLabel = (value) => t(`ui.prompts.stores.label.${value}`);
|
|
128
|
+
const deployModeLabel = (platform, value) => t(`ui.prompts.deployMode.label.${platform}.${value}`);
|
|
129
|
+
const platformTitle = (platform) => t(`ui.prompts.deployMode.platform.${platform}`);
|
|
151
130
|
|
|
152
|
-
//
|
|
131
|
+
// Env mode - how the secret ENV_FILE (.env format) is passed to the Flutter build.
|
|
153
132
|
export async function selectEnvMode({ initialValue = DEFAULT_ENV_MODE } = {}) {
|
|
154
133
|
engine.note(
|
|
155
|
-
"
|
|
156
|
-
"
|
|
157
|
-
"나중에 확인 화면의 '수정하기 > 환경변수 방식'에서 바꿀 수 있습니다.",
|
|
158
|
-
"환경변수 방식",
|
|
134
|
+
t("ui.prompts.envMode.note"),
|
|
135
|
+
t("ui.prompts.envMode.title"),
|
|
159
136
|
);
|
|
160
137
|
return engine.select({
|
|
161
|
-
message: "
|
|
162
|
-
options: ENV_MODES.map((value) => ({ value, label:
|
|
138
|
+
message: t("ui.prompts.envMode.message"),
|
|
139
|
+
options: ENV_MODES.map((value) => ({ value, label: envModeLabel(value) })),
|
|
163
140
|
initialIndex: Math.max(0, ENV_MODES.indexOf(initialValue)),
|
|
164
141
|
});
|
|
165
142
|
}
|
|
166
143
|
|
|
167
|
-
//
|
|
144
|
+
// Store deploy targets - installs only the workflows and fastlane templates of the chosen platforms. Choosing none installs without store deployment.
|
|
168
145
|
export async function selectFlutterStores({ initialValues = [] } = {}) {
|
|
169
146
|
engine.note(
|
|
170
|
-
"
|
|
171
|
-
"
|
|
172
|
-
"스토어 배포 대상",
|
|
147
|
+
t("ui.prompts.stores.note"),
|
|
148
|
+
t("ui.prompts.stores.title"),
|
|
173
149
|
);
|
|
174
150
|
return engine.multiselect({
|
|
175
|
-
message: "
|
|
176
|
-
options: STORE_PLATFORMS.map((value) => ({ value, label:
|
|
151
|
+
message: t("ui.prompts.stores.message"),
|
|
152
|
+
options: STORE_PLATFORMS.map((value) => ({ value, label: storeLabel(value) })),
|
|
177
153
|
initialValues,
|
|
178
154
|
required: false,
|
|
179
155
|
});
|
|
180
156
|
}
|
|
181
157
|
|
|
182
|
-
//
|
|
158
|
+
// Deploy mode - asked once per platform. The runtime repository variable and workflow_dispatch input always take precedence over this value.
|
|
183
159
|
export async function selectDeployMode({ platform, initialValue = DEFAULT_DEPLOY_MODE }) {
|
|
184
160
|
return engine.select({
|
|
185
|
-
message:
|
|
186
|
-
options: DEPLOY_MODES.map((value) => ({ value, label:
|
|
161
|
+
message: t("ui.prompts.deployMode.message", { platform: platformTitle(platform) }),
|
|
162
|
+
options: DEPLOY_MODES.map((value) => ({ value, label: deployModeLabel(platform, value) })),
|
|
187
163
|
initialIndex: Math.max(0, DEPLOY_MODES.indexOf(initialValue)),
|
|
188
164
|
});
|
|
189
165
|
}
|
|
190
166
|
|
|
191
|
-
// store_submit
|
|
167
|
+
// store_submit warning text - defined in core; the caller (interactive) prints it via note.
|
|
192
168
|
export { deployModeWarning };
|
|
193
169
|
|
|
194
|
-
//
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
//
|
|
170
|
+
// Branch strategy selection. Previously trunk-based only happened when the same name was entered for both
|
|
171
|
+
// the "release branch" and "development branch" questions; that rule was never announced, so users
|
|
172
|
+
// drifted into pr-flow unintentionally. Choosing the strategy explicitly first removes that.
|
|
173
|
+
// Do not reorder the options (pr-flow first) - the order determines the default in non-TTY environments.
|
|
198
174
|
export async function selectBranchStrategy() {
|
|
199
175
|
engine.note(
|
|
200
|
-
"
|
|
201
|
-
"
|
|
202
|
-
"브랜치 전략",
|
|
176
|
+
t("ui.prompts.branch.note"),
|
|
177
|
+
t("ui.prompts.branch.title"),
|
|
203
178
|
);
|
|
204
179
|
return engine.select({
|
|
205
|
-
message: "
|
|
180
|
+
message: t("ui.prompts.branch.message"),
|
|
206
181
|
options: [
|
|
207
|
-
{ value: "pr-flow", label: "
|
|
208
|
-
{ value: "trunk-based", label: "
|
|
182
|
+
{ value: "pr-flow", label: t("ui.prompts.branch.prFlow") },
|
|
183
|
+
{ value: "trunk-based", label: t("ui.prompts.branch.trunk") },
|
|
209
184
|
],
|
|
210
185
|
});
|
|
211
186
|
}
|
|
212
187
|
|
|
213
|
-
//
|
|
188
|
+
// Text input (empty input keeps the default).
|
|
214
189
|
export async function askText(message, defaultValue = "") {
|
|
215
190
|
const v = await engine.text({ message, defaultValue });
|
|
216
191
|
if (v === CANCEL) return CANCEL;
|
|
217
192
|
return v === "" || v == null ? defaultValue : v;
|
|
218
193
|
}
|
|
219
194
|
|
|
220
|
-
//
|
|
195
|
+
// Yes/no.
|
|
221
196
|
export async function askYesNo(message, initial = true) {
|
|
222
197
|
return engine.confirm({ message, initialValue: initial });
|
|
223
198
|
}
|
|
224
199
|
|
|
225
|
-
//
|
|
200
|
+
// Banner / notice output.
|
|
226
201
|
export function intro(text) { engine.intro(text); }
|
|
227
202
|
export function outro(text) { engine.outro(text); }
|
|
228
203
|
export function note(text, title) { engine.note(text, title); }
|
|
229
|
-
export function cancelMessage(text = "
|
|
204
|
+
export function cancelMessage(text = t("ui.readline-engine.cancelled")) { engine.cancelMessage(text); }
|
|
230
205
|
|
|
231
|
-
// ──
|
|
232
|
-
// runInteractive
|
|
233
|
-
//
|
|
206
|
+
// ── First-screen UI layers + real io for the interactive layer ─────
|
|
207
|
+
// runInteractive calls io.<method>?.() optionally - test stubs omit these methods
|
|
208
|
+
// to skip the visual layers and env questions (the execution contract is unchanged).
|
|
234
209
|
import { printBanner as _printBanner } from "./banner.js";
|
|
235
210
|
import {
|
|
236
211
|
printDetectionLog as _detLog, printAnalysisCard as _card,
|
|
@@ -244,7 +219,7 @@ export function analysisCard(info) { _card(info); }
|
|
|
244
219
|
export function installKind(info) { _installKind(info); }
|
|
245
220
|
export function summary(ctx) { _summary(ctx); }
|
|
246
221
|
|
|
247
|
-
// env
|
|
222
|
+
// Low-level engine io used by the env plan, path resolution and conflict menu (io contract of env-plan/paths-resolve)
|
|
248
223
|
export const engineIo = {
|
|
249
224
|
select: engine.select,
|
|
250
225
|
multiselect: engine.multiselect,
|