projectops 4.31.5 → 4.33.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 +144 -145
- package/package.json +2 -2
- package/src/cli/args.js +65 -29
- package/src/cli/errors.js +9 -7
- package/src/cli/help.js +51 -3
- package/src/commands/doctor.js +80 -78
- package/src/commands/full.js +11 -2
- package/src/commands/interactive.js +71 -49
- package/src/commands/issues.js +4 -0
- package/src/commands/skills.js +8 -7
- package/src/commands/version.js +4 -2
- package/src/commands/workflows.js +3 -1
- package/src/context.js +4 -0
- package/src/core/breaking-check.js +6 -5
- package/src/core/copy/simple.js +4 -0
- package/src/core/copy/workflows.js +10 -5
- package/src/core/exclusions.js +8 -0
- package/src/core/ide/adapters/claude.js +18 -17
- package/src/core/ide/adapters/codex.js +12 -11
- package/src/core/ide/adapters/cursor.js +11 -10
- package/src/core/ide/adapters/gemini.js +11 -10
- package/src/core/ide/adapters/pi-common.js +2 -1
- package/src/core/ide/adapters/pi-harness.js +13 -16
- package/src/core/ide/adapters/pi.js +12 -11
- package/src/core/label-style.js +78 -0
- package/src/core/migration-guide.js +38 -38
- package/src/core/migrations/index.js +19 -15
- package/src/core/migrations/registry.js +70 -55
- package/src/core/options-ask.js +65 -64
- package/src/core/orphan-workflows.js +2 -1
- package/src/core/paths-resolve.js +31 -30
- package/src/core/repo-language.js +47 -0
- package/src/core/run-trace.js +10 -0
- package/src/core/version-yml.js +68 -36
- package/src/i18n/core.en.js +31 -0
- package/src/i18n/core.ko.js +31 -0
- package/src/i18n/doctor.en.js +89 -0
- package/src/i18n/doctor.ko.js +89 -0
- package/src/i18n/en.js +141 -0
- package/src/i18n/ide.en.js +102 -0
- package/src/i18n/ide.ko.js +102 -0
- package/src/i18n/index.js +73 -0
- package/src/i18n/ko.js +134 -0
- package/src/i18n/migrate.en.js +66 -0
- package/src/i18n/migrate.ko.js +66 -0
- package/src/i18n/options.en.js +104 -0
- package/src/i18n/options.ko.js +104 -0
- package/src/i18n/summary.en.js +70 -0
- package/src/i18n/summary.ko.js +70 -0
- package/src/index.js +46 -28
- package/src/ui/banner.js +8 -6
- package/src/ui/env-plan.js +19 -18
- package/src/ui/prompts.js +25 -24
- package/src/ui/readline-engine.js +18 -10
- package/src/ui/skills-prompts.js +8 -7
- package/src/ui/status-cards.js +38 -38
- package/src/ui/summary.js +103 -111
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// options 영역 메시지 (en). 키는 <영역>.<의미> 형식.
|
|
2
|
+
export default {
|
|
3
|
+
"options.branch.remoteOnly": "Branch '{branch}' exists on the remote but not locally. Run 'git switch {branch}' if you need it.",
|
|
4
|
+
"options.branch.missing": "Development (release source) branch '{branch}' does not exist. It is required for releases ({branch} to {base} PR) to work.",
|
|
5
|
+
"options.branch.baseFallback": "default branch",
|
|
6
|
+
"options.branch.currentFallback": "current",
|
|
7
|
+
"options.branch.createPrompt": "Create branch '{branch}' from the {base} branch?",
|
|
8
|
+
"options.branch.skipped": "Skipped. Run it yourself later: git checkout -b {branch} && git push -u origin {branch}",
|
|
9
|
+
"options.branch.createFailed": "Branch creation failed. Run it yourself: git branch {branch}{from}",
|
|
10
|
+
"options.branch.created": "Created local branch '{branch}' (no checkout was done)",
|
|
11
|
+
"options.branch.pushPrompt": "Also push branch '{branch}' to origin?",
|
|
12
|
+
"options.branch.pushed": "Pushed origin/{branch}",
|
|
13
|
+
"options.branch.pushFailed": "Push failed (credentials or network). Run it yourself: git push -u origin {branch}",
|
|
14
|
+
"options.optional.found": "{icon} Found the {name} workflow ({count} files).",
|
|
15
|
+
"options.optional.included": "Included workflows:",
|
|
16
|
+
"options.optional.prompt": "Include the {name} workflow?",
|
|
17
|
+
"options.optional.yes": "Including the {name} workflow. It will be added to GitHub Actions.",
|
|
18
|
+
"options.optional.no": "Excluding the {name} workflow (you can add it later with an option).",
|
|
19
|
+
"options.saved.deploy": "Deployment: keeping the value saved in version.yml ({value}), skipping the question",
|
|
20
|
+
"options.saved.none": "none",
|
|
21
|
+
"options.saved.publish": "Publish targets: keeping the value saved in version.yml ({value}), skipping the question",
|
|
22
|
+
"options.saved.secret": "Secret backup option: keeping the value saved in version.yml ({value}), skipping the question",
|
|
23
|
+
"options.intent.intro": "What kind of project is this? (deployment questions will be narrowed to match)",
|
|
24
|
+
"options.intent.prompt": "Choose the project kind",
|
|
25
|
+
"options.intent.app": "An app or service that runs on a server or hosting (deploy only)",
|
|
26
|
+
"options.intent.library": "A library or package that others consume (publish only)",
|
|
27
|
+
"options.intent.both": "Both (runs on a server and is also published as a library)",
|
|
28
|
+
"options.intent.none": "No deployment (CI and build checks only)",
|
|
29
|
+
"options.intent.manual": "I will pick each option myself (ask every option separately)",
|
|
30
|
+
"options.intent.chosen": "Project kind: {value}",
|
|
31
|
+
"options.axes.intro": "There are two separate kinds of deployment. They are independent, so answer each one:",
|
|
32
|
+
"options.axes.one": " 1) Deploying a runnable: running on a server or hosting (Docker, Vercel ...)",
|
|
33
|
+
"options.axes.two": " 2) Library publish: releasing to a registry for others to consume (Nexus, npm ...)",
|
|
34
|
+
"options.deploy.intro": "(1) Where do you deploy the runnable (server or app)?",
|
|
35
|
+
"options.deploy.hint": " Pick one if you plan to host it on a server. Otherwise keep 'Do not deploy to a server'.",
|
|
36
|
+
"options.deploy.dockerSsh": "Docker + SSH server deployment (default)",
|
|
37
|
+
"options.deploy.prompt": "Choose how to deploy the runnable",
|
|
38
|
+
"options.deploy.none": "Do not deploy to a server (build checks only; choose library publish next)",
|
|
39
|
+
"options.deploy.chosen": "Runnable deployment: {value}",
|
|
40
|
+
"options.publish.intro": "(2) Do you also publish this project as a library that others consume?",
|
|
41
|
+
"options.publish.hint": " Separate from (1). Pick what applies. If you do not publish a library, select nothing and press Enter.",
|
|
42
|
+
"options.publish.nexus": "Internal Maven (Nexus) library publish",
|
|
43
|
+
"options.publish.npm": "Public npmjs package publish (NPM_TOKEN)",
|
|
44
|
+
"options.publish.githubPackages": "GitHub Packages library publish",
|
|
45
|
+
"options.publish.prompt": "Library publish targets (select nothing and press Enter = no publish)",
|
|
46
|
+
"options.publish.chosen": "Library publish: {value}",
|
|
47
|
+
"options.publish.chosenNone": "Library publish: none",
|
|
48
|
+
"options.pr.intro": "Which comments do you want on pull requests?",
|
|
49
|
+
"options.pr.hint1": " The two do different jobs and have separate limits, so enabling both does not make them interfere.",
|
|
50
|
+
"options.pr.hint2": " - Change summary: what changed (works with no extra setup)",
|
|
51
|
+
"options.pr.hint3": " - Code review: points out bugs and improvements (needs the CodeRabbit app, free for public repos)",
|
|
52
|
+
"options.pr.prompt": "Choose the PR comment mode",
|
|
53
|
+
"options.pr.both": "Both (recommended, free to use)",
|
|
54
|
+
"options.pr.summary": "Change summary only (no extra setup)",
|
|
55
|
+
"options.pr.coderabbit": "Code review only (CodeRabbit)",
|
|
56
|
+
"options.pr.none": "Disabled",
|
|
57
|
+
"options.pr.chosen": "PR comments: {value}",
|
|
58
|
+
"options.pr.label.summary": "AI change summary",
|
|
59
|
+
"options.pr.label.coderabbit": "CodeRabbit",
|
|
60
|
+
"options.pr.label.both": "Both",
|
|
61
|
+
"options.pr.label.none": "Disabled",
|
|
62
|
+
"options.pr.crWarn": " CodeRabbit needs extra setup:",
|
|
63
|
+
"options.pr.crStep1": " 1) Go to https://coderabbit.ai and sign in with GitHub",
|
|
64
|
+
"options.pr.crStep2": " 2) Install the CodeRabbit GitHub app and grant it access to this repository",
|
|
65
|
+
"options.pr.crStep3": " (Without this the workflow runs but no review comments appear on PRs)",
|
|
66
|
+
"options.ollama.prompt": "Ollama server base_url (e.g. https://ai.suhsaechan.kr/v1)",
|
|
67
|
+
"options.ollama.chosen": "Ollama base_url: {value}",
|
|
68
|
+
"options.ollama.unset": "(not set)",
|
|
69
|
+
"options.devBranch.intro": "What is the 'development branch' where you collect code before releasing to deployment?",
|
|
70
|
+
"options.devBranch.detected": " The detected default (deploy) branch is '{base}'. Development usually collects on a branch in front of it (e.g. develop).",
|
|
71
|
+
"options.devBranch.hint": " Unless you have a reason, keep develop.",
|
|
72
|
+
"options.devBranch.prompt": "Development (release source) branch",
|
|
73
|
+
"options.devBranch.chosen": "Development (release source) branch: {value}",
|
|
74
|
+
"options.secret.short": "Secret server backup",
|
|
75
|
+
"options.secret.desc": "A workflow that uploads config files stored in GitHub Secrets to a server over SSH and keeps their history.",
|
|
76
|
+
"paths.multi": "Multi-type project detected: {total} types in total",
|
|
77
|
+
"paths.single": "{type} project detected: 1 type in total",
|
|
78
|
+
"paths.rootDef": "Project root = the folder holding that type's version file (path relative to the repo root)",
|
|
79
|
+
"paths.byFlag": " {type} -> {path} (set by --paths)",
|
|
80
|
+
"paths.atRoot": " {type} -> . (found {marker} at the root)",
|
|
81
|
+
"paths.keptSaved": " {type} -> {path} (kept project_paths from version.yml)",
|
|
82
|
+
"paths.autoDetected": " {type} -> {path} (auto-detected)",
|
|
83
|
+
"paths.ambiguous": " Warning: {type} -> {count} candidates, cannot decide automatically; recording root (.) (set it with --paths \"{type}=path\")",
|
|
84
|
+
"paths.found": " {prog} {type}: found {marker}",
|
|
85
|
+
"paths.location": " Location: <repo root>/{file}",
|
|
86
|
+
"paths.confirmPrompt": " Set the {type} project root to '{cand}'?",
|
|
87
|
+
"paths.confirmYes": "Yes, use '{cand}' (based on {file})",
|
|
88
|
+
"paths.confirmManual": "No, enter a path manually",
|
|
89
|
+
"paths.exclude": "Not this type, exclude {type} from installation",
|
|
90
|
+
"paths.manual": "Enter manually",
|
|
91
|
+
"paths.multiFound": " {prog} {type}: found {count} path candidates",
|
|
92
|
+
"paths.selectPrompt": " Choose the {type} project root",
|
|
93
|
+
"paths.notFound": " Warning: {prog} {type}: no project found (maxdepth 3).",
|
|
94
|
+
"paths.inputPrompt": " Enter the {type} project root path (folder containing {marker}, e.g. server, app; press Enter for the root",
|
|
95
|
+
"paths.inputCurrent": ", current value: {value}",
|
|
96
|
+
"paths.missingFile": " Warning: {path}/{marker} does not exist.",
|
|
97
|
+
"paths.actionPrompt": " What would you like to do?",
|
|
98
|
+
"paths.retry": "Enter again",
|
|
99
|
+
"paths.force": "Use '{path}' anyway",
|
|
100
|
+
"paths.excluded": " {type} excluded: this type is left out of version sync and workflow installation",
|
|
101
|
+
"paths.summary": "Confirmed version file path per type:",
|
|
102
|
+
"paths.duplicate": " Warning: several types ({types}) point at the same file ({file}).",
|
|
103
|
+
"paths.duplicateNote": " The same version is written to all of them on sync. It works, but check that this is intended.",
|
|
104
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// options 영역 메시지 (ko). 키는 <영역>.<의미> 형식.
|
|
2
|
+
export default {
|
|
3
|
+
"options.branch.remoteOnly": "'{branch}' 브랜치가 원격에는 있고 로컬에 없습니다. 필요 시 'git switch {branch}'로 가져오세요.",
|
|
4
|
+
"options.branch.missing": "개발(릴리스 소스) 브랜치 '{branch}'가 없습니다. 릴리스({branch}→{base} PR)가 동작하려면 필요합니다.",
|
|
5
|
+
"options.branch.baseFallback": "기본 브랜치",
|
|
6
|
+
"options.branch.currentFallback": "현재",
|
|
7
|
+
"options.branch.createPrompt": "{base} 브랜치에서 '{branch}' 브랜치를 만들까요?",
|
|
8
|
+
"options.branch.skipped": "건너뜁니다. 나중에 직접: git checkout -b {branch} && git push -u origin {branch}",
|
|
9
|
+
"options.branch.createFailed": "브랜치 생성 실패. 직접 실행해주세요: git branch {branch}{from}",
|
|
10
|
+
"options.branch.created": "로컬 브랜치 '{branch}' 생성 완료 (checkout은 하지 않았습니다)",
|
|
11
|
+
"options.branch.pushPrompt": "원격(origin)에 '{branch}' 브랜치도 push할까요?",
|
|
12
|
+
"options.branch.pushed": "origin/{branch} push 완료",
|
|
13
|
+
"options.branch.pushFailed": "push 실패(자격/네트워크). 직접 실행해주세요: git push -u origin {branch}",
|
|
14
|
+
"options.optional.found": "{icon} {name} 워크플로우를 발견했습니다. ({count}개 파일)",
|
|
15
|
+
"options.optional.included": "포함되는 워크플로우:",
|
|
16
|
+
"options.optional.prompt": "{name} 워크플로우를 포함할까요?",
|
|
17
|
+
"options.optional.yes": "{name} 워크플로우를 포함합니다. GitHub Actions에 추가됩니다",
|
|
18
|
+
"options.optional.no": "{name} 워크플로우를 제외합니다 (나중에 옵션으로 추가 가능)",
|
|
19
|
+
"options.saved.deploy": "배포 방식: version.yml 저장값({value}) 유지. 재질문 생략",
|
|
20
|
+
"options.saved.none": "없음",
|
|
21
|
+
"options.saved.publish": "Publish 타겟: version.yml 저장값({value}) 유지. 재질문 생략",
|
|
22
|
+
"options.saved.secret": "Secret 백업 옵션: version.yml 저장값({value}) 유지. 재질문 생략",
|
|
23
|
+
"options.intent.intro": "이 프로젝트는 어떤 성격인가요? (배포 관련 질문을 여기에 맞춰 좁혀드립니다)",
|
|
24
|
+
"options.intent.prompt": "프로젝트 성격을 선택하세요",
|
|
25
|
+
"options.intent.app": "서버/호스팅에 올려 돌리는 앱·서비스 (배포만)",
|
|
26
|
+
"options.intent.library": "남이 가져다 쓰는 라이브러리/패키지 (publish만)",
|
|
27
|
+
"options.intent.both": "둘 다 (서버로도 돌리고 라이브러리로도 냄)",
|
|
28
|
+
"options.intent.none": "배포 안 함 (CI·빌드 검증만)",
|
|
29
|
+
"options.intent.manual": "직접 하나씩 고를게요 (모든 옵션 개별 질문)",
|
|
30
|
+
"options.intent.chosen": "프로젝트 성격: {value}",
|
|
31
|
+
"options.axes.intro": "배포는 두 가지가 따로 있습니다. 서로 독립이라 각각 답하시면 됩니다:",
|
|
32
|
+
"options.axes.one": " 1) 실행물 배포: 서버/호스팅에 올려 돌리는 것 (Docker, Vercel …)",
|
|
33
|
+
"options.axes.two": " 2) 라이브러리 배포(publish): 남이 가져다 쓰게 레지스트리에 내는 것 (Nexus, npm …)",
|
|
34
|
+
"options.deploy.intro": "(1) 실행물(서버/앱)을 어디에 올리나요?",
|
|
35
|
+
"options.deploy.hint": " 서버·호스팅에 올릴 계획이 있으면 고르고, 없으면 '서버에 올리지 않음'으로 두세요.",
|
|
36
|
+
"options.deploy.dockerSsh": "Docker + SSH 서버 배포 (기본)",
|
|
37
|
+
"options.deploy.prompt": "실행물 배포 방식을 선택하세요",
|
|
38
|
+
"options.deploy.none": "서버에 올리지 않음 (빌드 검증만, 라이브러리는 다음에서 선택)",
|
|
39
|
+
"options.deploy.chosen": "실행물 배포: {value}",
|
|
40
|
+
"options.publish.intro": "(2) 이 프로젝트를 남이 가져다 쓰는 라이브러리로도 배포(publish)하나요?",
|
|
41
|
+
"options.publish.hint": " (1) 실행물 배포와 별개입니다. 해당되는 걸 고르고, 라이브러리 배포를 안 하면 아무것도 고르지 말고 Enter.",
|
|
42
|
+
"options.publish.nexus": "사내 Maven(Nexus) 라이브러리 배포",
|
|
43
|
+
"options.publish.npm": "공개 npmjs 패키지 배포 (NPM_TOKEN)",
|
|
44
|
+
"options.publish.githubPackages": "GitHub Packages 라이브러리 배포",
|
|
45
|
+
"options.publish.prompt": "라이브러리 배포 타겟 (없으면 선택 없이 Enter = 배포 안 함)",
|
|
46
|
+
"options.publish.chosen": "라이브러리 배포: {value}",
|
|
47
|
+
"options.publish.chosenNone": "라이브러리 배포: 안 함",
|
|
48
|
+
"options.pr.intro": "PR에 어떤 댓글을 받으시겠어요?",
|
|
49
|
+
"options.pr.hint1": " 두 가지는 하는 일이 다르고 한도도 따로라, 같이 켜도 서로 방해하지 않습니다.",
|
|
50
|
+
"options.pr.hint2": " · 변경 요약: 무엇이 바뀌었는지 (추가 설정 없이 바로 동작)",
|
|
51
|
+
"options.pr.hint3": " · 코드 리뷰: 버그·개선점 지적 (CodeRabbit 앱 설치 필요, 공개 저장소 무료)",
|
|
52
|
+
"options.pr.prompt": "PR 댓글 방식을 선택하세요",
|
|
53
|
+
"options.pr.both": "둘 다 (추천, 무료로 쓸 수 있습니다)",
|
|
54
|
+
"options.pr.summary": "변경 요약만 (추가 설정 불필요)",
|
|
55
|
+
"options.pr.coderabbit": "코드 리뷰만 (CodeRabbit)",
|
|
56
|
+
"options.pr.none": "사용 안 함",
|
|
57
|
+
"options.pr.chosen": "PR 댓글: {value}",
|
|
58
|
+
"options.pr.label.summary": "AI 변경 요약",
|
|
59
|
+
"options.pr.label.coderabbit": "CodeRabbit",
|
|
60
|
+
"options.pr.label.both": "둘 다",
|
|
61
|
+
"options.pr.label.none": "사용 안 함",
|
|
62
|
+
"options.pr.crWarn": " ⚠️ CodeRabbit은 추가 설정이 필요합니다:",
|
|
63
|
+
"options.pr.crStep1": " 1) https://coderabbit.ai 접속 → GitHub으로 로그인",
|
|
64
|
+
"options.pr.crStep2": " 2) CodeRabbit GitHub 앱 설치 → 이 저장소에 접근 권한(grant access) 부여",
|
|
65
|
+
"options.pr.crStep3": " (이 단계를 안 하면 워크플로우는 켜져도 PR에 리뷰 댓글이 달리지 않습니다)",
|
|
66
|
+
"options.ollama.prompt": "Ollama 서버 base_url (예: https://ai.suhsaechan.kr/v1)",
|
|
67
|
+
"options.ollama.chosen": "Ollama base_url: {value}",
|
|
68
|
+
"options.ollama.unset": "(미지정)",
|
|
69
|
+
"options.devBranch.intro": "개발한 코드를 모아서 배포로 올리는 '개발 브랜치'는 무엇인가요?",
|
|
70
|
+
"options.devBranch.detected": " 감지된 기본(배포) 브랜치는 '{base}'입니다. 개발은 보통 그 앞단 브랜치(예: develop)에서 모아 올립니다.",
|
|
71
|
+
"options.devBranch.hint": " 특별한 이유가 없으면 develop 그대로 두세요.",
|
|
72
|
+
"options.devBranch.prompt": "개발(릴리스 소스) 브랜치",
|
|
73
|
+
"options.devBranch.chosen": "개발(릴리스 소스) 브랜치: {value}",
|
|
74
|
+
"options.secret.short": "Secret 서버 백업",
|
|
75
|
+
"options.secret.desc": "GitHub Secret에 저장한 설정 파일을 SSH로 서버에 업로드·이력관리하는 워크플로우입니다.",
|
|
76
|
+
"paths.multi": "멀티타입 프로젝트가 감지되었습니다. 총 {total}개 타입",
|
|
77
|
+
"paths.single": "{type} 프로젝트가 감지되었습니다. 총 1개 타입",
|
|
78
|
+
"paths.rootDef": "'프로젝트 루트' = 그 타입의 버전 파일이 있는 폴더 (레포 루트 기준 상대경로)",
|
|
79
|
+
"paths.byFlag": " {type} → {path} (--paths 지정)",
|
|
80
|
+
"paths.atRoot": " {type} → . (루트의 {marker})",
|
|
81
|
+
"paths.keptSaved": " {type} → {path} (기존 project_paths 유지)",
|
|
82
|
+
"paths.autoDetected": " {type} → {path} (자동 감지)",
|
|
83
|
+
"paths.ambiguous": " ⚠️ {type} → 후보 {count}개로 자동 확정 불가, 루트(.)로 기록 (--paths \"{type}=경로\"로 지정 가능)",
|
|
84
|
+
"paths.found": " {prog} 🔍 {type} — {marker} 발견",
|
|
85
|
+
"paths.location": " 위치: <레포루트>/{file}",
|
|
86
|
+
"paths.confirmPrompt": " {type} 프로젝트 루트를 '{cand}'(으)로 설정할까요?",
|
|
87
|
+
"paths.confirmYes": "예 — '{cand}' 사용 ({file} 기준)",
|
|
88
|
+
"paths.confirmManual": "아니오 — 경로 직접 입력",
|
|
89
|
+
"paths.exclude": "이 타입 아님 — {type} 설치 대상에서 제외",
|
|
90
|
+
"paths.manual": "직접 입력",
|
|
91
|
+
"paths.multiFound": " {prog} 🔍 {type}: 경로 후보 {count}개 발견",
|
|
92
|
+
"paths.selectPrompt": " {type} 프로젝트 루트를 선택하세요",
|
|
93
|
+
"paths.notFound": " ⚠️ {prog} {type}: 프로젝트를 찾지 못했습니다 (maxdepth 3).",
|
|
94
|
+
"paths.inputPrompt": " {type} 프로젝트 루트 경로 입력 ({marker} 이 있는 폴더, 예: server, app. 루트면 그냥 Enter",
|
|
95
|
+
"paths.inputCurrent": ", 현재값: {value}",
|
|
96
|
+
"paths.missingFile": " ⚠️ {path}/{marker} 파일이 없습니다.",
|
|
97
|
+
"paths.actionPrompt": " 어떻게 할까요?",
|
|
98
|
+
"paths.retry": "다시 입력",
|
|
99
|
+
"paths.force": "그래도 '{path}' 경로 사용",
|
|
100
|
+
"paths.excluded": " ➖ {type} 제외 — 이 타입은 버전 동기화·워크플로우 설치 대상에서 빠집니다",
|
|
101
|
+
"paths.summary": "타입별 버전 파일 경로 확정:",
|
|
102
|
+
"paths.duplicate": " ⚠️ 같은 파일({file})을 여러 타입({types})이 바라봅니다.",
|
|
103
|
+
"paths.duplicateNote": " → sync 때 모두 같은 버전이 기록됩니다. 동작에는 문제없지만 의도한 구성인지 확인하세요.",
|
|
104
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Completion screen messages (English). Merged into the main catalog by en.js.
|
|
2
|
+
export default {
|
|
3
|
+
"done.title": "✨ projectops Setup Complete!",
|
|
4
|
+
"done.integrated": "Installed:",
|
|
5
|
+
"done.feat.version": "Version management (version.yml)",
|
|
6
|
+
"done.feat.readme": "Automatic version section in README.md",
|
|
7
|
+
"done.feat.workflows": "GitHub Actions workflows",
|
|
8
|
+
"done.feat.util": "Utility modules ({count})",
|
|
9
|
+
"done.feat.templates": "Issue, PR and Discussion templates",
|
|
10
|
+
"done.feat.coderabbit": "CodeRabbit AI review settings",
|
|
11
|
+
"done.feat.gitignore": "Required .gitignore entries",
|
|
12
|
+
"done.feat.guide": "Setup guide (PROJECTOPS-SETUP-GUIDE.md)",
|
|
13
|
+
"done.feat.skills": "Agent skills (Claude, Cursor, Gemini, Codex, PI)",
|
|
14
|
+
"done.addedFiles": "Added files:",
|
|
15
|
+
"done.versionYml": "version.yml (version: {version}, types: {types})",
|
|
16
|
+
"done.readmeAdded": "README.md (version section added)",
|
|
17
|
+
"done.readmeMissing": "No README.md: version section skipped",
|
|
18
|
+
"done.addedWorkflows": "Added workflows:",
|
|
19
|
+
"done.installedCount": "Installed or updated ({count}):",
|
|
20
|
+
"done.noneInstalled": "No workflows installed or updated: everything is up to date",
|
|
21
|
+
"done.keptTitle": "{count} workflows you modified were kept as they are",
|
|
22
|
+
"done.keptDiff": "vs. the new template: {added} lines added, {removed} removed",
|
|
23
|
+
"done.keptHint1": "Copies of the new templates are in .github/.projectops/incoming/ (not tracked by git).",
|
|
24
|
+
"done.keptHint2": "Move only the changes you want into your workflows.",
|
|
25
|
+
"done.replacedTitle": "Could not tell whether {count} workflows were modified, so they were replaced with the new template and the originals saved as .bak",
|
|
26
|
+
"done.replacedHint": "If you had edits, copy them over from the .bak files. (The update baseline is recorded by this run.)",
|
|
27
|
+
"done.utilTitle": "Utility modules:",
|
|
28
|
+
"done.spring.title": "Extra setup for Spring projects:",
|
|
29
|
+
"done.spring.1": "The version in build.gradle is synced automatically",
|
|
30
|
+
"done.spring.2": "CI/CD workflows need GitHub Secrets",
|
|
31
|
+
"done.spring.3": "Details: .github/workflows/project-types/spring/README.md",
|
|
32
|
+
"done.flutter.title": "Flutter deployment wizards:",
|
|
33
|
+
"done.flutter.open": "Open them in a browser, fill in the values and generate the files",
|
|
34
|
+
"done.workflowGuide": "Workflow guide",
|
|
35
|
+
"done.verify.ok": "Install check: no unreplaced values",
|
|
36
|
+
"done.verify.bad": "Install check: {count} unreplaced values",
|
|
37
|
+
"done.verify.explain1": "Template values are still present at the places below. Left as they are, deployment will fail.",
|
|
38
|
+
"done.verify.explain2": "(The project structure could not be detected automatically, so please fill them in.)",
|
|
39
|
+
"done.verify.more": "... and {count} more",
|
|
40
|
+
"done.secrets.title": "GitHub Secrets to register ({count})",
|
|
41
|
+
"done.secrets.more": "{list} and {count} more",
|
|
42
|
+
"done.secrets.hint": "Until they are registered the workflows fail. Ignore the ones for workflows you do not use.",
|
|
43
|
+
"done.todo.title": "Please check the following:",
|
|
44
|
+
"done.todo.pat": "(Optional) Set up a GitHub Personal Access Token",
|
|
45
|
+
"done.todo.pat1": "Releases work without it. Registering it makes follow-up workflows run more reliably",
|
|
46
|
+
"done.todo.pat2": "and allows automerge even with branch protection rules.",
|
|
47
|
+
"done.todo.branchReady": "Branch {branch} is ready: the wizard checked and created it (nothing to do)",
|
|
48
|
+
"done.todo.branch": "Create the {branch} branch (if it does not exist yet)",
|
|
49
|
+
"done.todo.ai": "(Optional) Get AI-polished release notes",
|
|
50
|
+
"done.todo.ai1": "Release notes already work: they are built by analyzing commits, no setup needed.",
|
|
51
|
+
"done.todo.ai2": "Do the steps below only if you want smoother wording.",
|
|
52
|
+
"done.todo.aiKey": "Get a key from Google AI Studio (free, no credit card, 2 minutes)",
|
|
53
|
+
"done.todo.aiRegister": "Register it in this repository",
|
|
54
|
+
"done.todo.aiSecret": "your key (starts with AIza...)",
|
|
55
|
+
"done.todo.aiOthers": "For another service, just register the key under its name. Whatever is registered is used automatically.",
|
|
56
|
+
"done.todo.coderabbit": "Enable CodeRabbit",
|
|
57
|
+
"done.todo.coderabbit1": "Sign in at https://coderabbit.ai, install the GitHub app and grant access to this repository",
|
|
58
|
+
"done.todo.coderabbit2": "Without this step the workflow is on but no review comments appear on PRs",
|
|
59
|
+
"done.todo.coderabbit3": "Free for public repositories (3 reviews per hour)",
|
|
60
|
+
"done.todo.labels": "(Recommended) Create the template labels",
|
|
61
|
+
"done.todo.labels1": "Actions tab > PROJECT-SYNC-GITHUB-LABELS > Run workflow, once.",
|
|
62
|
+
"done.todo.labels2": "Without it the status labels (todo, in progress) do not exist and label automation is ignored.",
|
|
63
|
+
"done.guide": "For details see:",
|
|
64
|
+
"done.log.title": "Run record",
|
|
65
|
+
"done.log.log": "Log",
|
|
66
|
+
"done.log.events": "Events",
|
|
67
|
+
"done.log.guide": "Guide",
|
|
68
|
+
"done.log.note": "Everything that was decided and why is recorded (not tracked by git).",
|
|
69
|
+
"done.log.ask": "If something goes wrong, ask your AI agent to \"check the run log\".",
|
|
70
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// 완료 화면 문구 (한국어). ko.js 가 메인 카탈로그에 합친다.
|
|
2
|
+
export default {
|
|
3
|
+
"done.title": "✨ projectops Setup Complete!",
|
|
4
|
+
"done.integrated": "통합된 기능:",
|
|
5
|
+
"done.feat.version": "버전 관리 시스템 (version.yml)",
|
|
6
|
+
"done.feat.readme": "README.md 자동 버전 업데이트",
|
|
7
|
+
"done.feat.workflows": "GitHub Actions 워크플로우",
|
|
8
|
+
"done.feat.util": "유틸리티 모듈 ({count} 개)",
|
|
9
|
+
"done.feat.templates": "이슈/PR/Discussion 템플릿",
|
|
10
|
+
"done.feat.coderabbit": "CodeRabbit AI 리뷰 설정",
|
|
11
|
+
"done.feat.gitignore": ".gitignore 필수 항목",
|
|
12
|
+
"done.feat.guide": "템플릿 설정 가이드 (PROJECTOPS-SETUP-GUIDE.md)",
|
|
13
|
+
"done.feat.skills": "Agent Skill 설치 (Claude, Cursor, Gemini, Codex, PI)",
|
|
14
|
+
"done.addedFiles": "추가된 파일:",
|
|
15
|
+
"done.versionYml": "version.yml (버전: {version}, 타입: {types})",
|
|
16
|
+
"done.readmeAdded": "README.md (버전 섹션 추가)",
|
|
17
|
+
"done.readmeMissing": "README.md 없음: 버전 섹션 추가를 건너뜀",
|
|
18
|
+
"done.addedWorkflows": "추가된 워크플로우:",
|
|
19
|
+
"done.installedCount": "새로 설치·갱신됨 ({count}개):",
|
|
20
|
+
"done.noneInstalled": "새로 설치·갱신된 워크플로우 없음: 모두 최신 상태",
|
|
21
|
+
"done.keptTitle": "수정한 워크플로 {count}개는 덮어쓰지 않고 그대로 두었습니다",
|
|
22
|
+
"done.keptDiff": "새 템플릿 기준 추가 {added}줄, 삭제 {removed}줄",
|
|
23
|
+
"done.keptHint1": "새 템플릿 사본은 저장소에 추적되지 않는 .github/.projectops/incoming/ 에 있습니다.",
|
|
24
|
+
"done.keptHint2": "필요한 변경만 워크플로에 직접 옮겨 반영하세요.",
|
|
25
|
+
"done.replacedTitle": "수정 여부를 확인할 수 없어 {count}개 워크플로를 새 템플릿으로 교체하고 원본을 .bak 으로 백업했습니다",
|
|
26
|
+
"done.replacedHint": "직접 수정한 내용이 있었다면 .bak 에서 옮겨 오세요. (업데이트 기준점은 이번 실행으로 새로 기록됩니다)",
|
|
27
|
+
"done.utilTitle": "유틸리티 모듈:",
|
|
28
|
+
"done.spring.title": "Spring 프로젝트 추가 설정:",
|
|
29
|
+
"done.spring.1": "build.gradle의 버전 정보가 자동 동기화됩니다",
|
|
30
|
+
"done.spring.2": "CI/CD 워크플로우에서 GitHub Secrets 설정이 필요합니다",
|
|
31
|
+
"done.spring.3": "자세한 설정 방법: .github/workflows/project-types/spring/README.md",
|
|
32
|
+
"done.flutter.title": "Flutter 배포 마법사 사용법:",
|
|
33
|
+
"done.flutter.open": "브라우저에서 열어 필요한 정보 입력 후 파일 생성",
|
|
34
|
+
"done.workflowGuide": "워크플로우 가이드",
|
|
35
|
+
"done.verify.ok": "설치 검증: 치환되지 않은 값 없음",
|
|
36
|
+
"done.verify.bad": "설치 검증: 치환되지 않은 값 {count}건",
|
|
37
|
+
"done.verify.explain1": "아래 위치에 템플릿 값이 그대로 남아 있습니다. 그대로 두면 배포 시점에 실패합니다.",
|
|
38
|
+
"done.verify.explain2": "(프로젝트 구조를 자동으로 찾지 못한 경우입니다. 직접 값을 채워주세요)",
|
|
39
|
+
"done.verify.more": "… 외 {count}건",
|
|
40
|
+
"done.secrets.title": "등록이 필요한 GitHub Secret ({count}개)",
|
|
41
|
+
"done.secrets.more": "{list} 외 {count}개",
|
|
42
|
+
"done.secrets.hint": "등록 전까지 해당 워크플로우는 실패합니다. 쓰지 않는 워크플로우라면 무시해도 됩니다.",
|
|
43
|
+
"done.todo.title": "다음 작업을 확인해주세요:",
|
|
44
|
+
"done.todo.pat": "(선택) GitHub Personal Access Token 설정",
|
|
45
|
+
"done.todo.pat1": "없어도 릴리스는 정상 동작합니다. 등록하면 릴리스 후 후속 워크플로우가",
|
|
46
|
+
"done.todo.pat2": "더 확실하게 이어지고, 브랜치 보호 규칙이 있어도 자동 머지가 가능합니다.",
|
|
47
|
+
"done.todo.branchReady": "{branch} 브랜치 준비 완료: 마법사가 확인 및 생성했습니다 (추가 작업 불필요)",
|
|
48
|
+
"done.todo.branch": "{branch} 브랜치 생성 (아직 없다면)",
|
|
49
|
+
"done.todo.ai": "(선택) AI가 다듬은 릴리스 노트 받기",
|
|
50
|
+
"done.todo.ai1": "지금 상태로도 릴리스 노트는 나옵니다. 커밋 내용을 분석해 만들기 때문에 아무 설정을 하지 않아도 됩니다.",
|
|
51
|
+
"done.todo.ai2": "아래는 문장을 더 매끄럽게 하고 싶을 때만 하세요.",
|
|
52
|
+
"done.todo.aiKey": "Google AI Studio에서 키 발급 (무료, 신용카드 불필요, 2분)",
|
|
53
|
+
"done.todo.aiRegister": "이 저장소에 등록",
|
|
54
|
+
"done.todo.aiSecret": "발급받은 키(AIza... 로 시작)",
|
|
55
|
+
"done.todo.aiOthers": "다른 서비스를 쓴다면 이름만 바꿔 등록하면 됩니다. 등록한 것이 자동으로 쓰입니다.",
|
|
56
|
+
"done.todo.coderabbit": "CodeRabbit 활성화",
|
|
57
|
+
"done.todo.coderabbit1": "https://coderabbit.ai 로그인 → GitHub 앱 설치 → 이 저장소에 접근 권한(grant access) 부여",
|
|
58
|
+
"done.todo.coderabbit2": "이 단계를 안 하면 워크플로우는 켜져도 PR에 리뷰 댓글이 달리지 않습니다",
|
|
59
|
+
"done.todo.coderabbit3": "공개 저장소는 무료입니다 (시간당 3회 리뷰 제한)",
|
|
60
|
+
"done.todo.labels": "(권장) 템플릿 라벨 만들기",
|
|
61
|
+
"done.todo.labels1": "Actions 탭 > PROJECT-SYNC-GITHUB-LABELS > Run workflow 를 한 번 실행하세요.",
|
|
62
|
+
"done.todo.labels2": "안 하면 작업전·작업중 같은 상태 라벨이 레포에 없어 라벨 자동화가 무시됩니다.",
|
|
63
|
+
"done.guide": "자세한 설정 방법은 다음 파일을 참고하세요:",
|
|
64
|
+
"done.log.title": "이번 실행 기록",
|
|
65
|
+
"done.log.log": "로그",
|
|
66
|
+
"done.log.events": "이벤트",
|
|
67
|
+
"done.log.guide": "가이드",
|
|
68
|
+
"done.log.note": "무엇을 어떤 근거로 정했는지 전부 남아 있습니다 (저장소에 추적되지 않음).",
|
|
69
|
+
"done.log.ask": "문제가 생기면 AI Agent에게 \"실행 로그 확인해줘\"라고 요청하세요.",
|
|
70
|
+
};
|
package/src/index.js
CHANGED
|
@@ -4,7 +4,8 @@ import { join, dirname } from "node:path";
|
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { readFileSync, existsSync } from "node:fs";
|
|
6
6
|
import { parseArgs, parsePathsCsv, validatePathsMap, CliError } from "./cli/args.js";
|
|
7
|
-
import {
|
|
7
|
+
import { helpText } from "./cli/help.js";
|
|
8
|
+
import { resolveLang, setLang, t } from "./i18n/index.js";
|
|
8
9
|
import { reportFatal } from "./cli/errors.js";
|
|
9
10
|
import { createContext } from "./context.js";
|
|
10
11
|
import { PATHS } from "./core/paths.js";
|
|
@@ -25,6 +26,8 @@ import { runFull } from "./commands/full.js";
|
|
|
25
26
|
import { runVersion } from "./commands/version.js";
|
|
26
27
|
import { runWorkflows } from "./commands/workflows.js";
|
|
27
28
|
import { runIssues } from "./commands/issues.js";
|
|
29
|
+
import { resolveLabelStyle } from "./core/label-style.js";
|
|
30
|
+
import { resolveRepoLanguage } from "./core/repo-language.js";
|
|
28
31
|
import { runInteractive } from "./commands/interactive.js";
|
|
29
32
|
import { runSkills } from "./commands/skills.js";
|
|
30
33
|
|
|
@@ -69,8 +72,12 @@ async function runCore(argv, { cwd = process.cwd(), source = { type: "git" }, cl
|
|
|
69
72
|
if (e instanceof CliError) { console.error(e.message); return 1; }
|
|
70
73
|
throw e;
|
|
71
74
|
}
|
|
75
|
+
// Language: --lang > PROJECTOPS_LANG > system. Non-interactive runs are pinned to English
|
|
76
|
+
// so logs do not depend on the runner's locale.
|
|
77
|
+
const langInfo = resolveLang({ flag: opts.lang, pinEnglish: opts.force || !process.stdout.isTTY });
|
|
78
|
+
setLang(langInfo.lang, langInfo.source);
|
|
72
79
|
if (opts.showVersion) { console.log(readPkgVersion()); return 0; }
|
|
73
|
-
if (opts.help) { console.log(
|
|
80
|
+
if (opts.help) { console.log(helpText()); return 0; }
|
|
74
81
|
|
|
75
82
|
// doctor 모드 (#558) — 읽기 전용 진단. 템플릿을 내려받지 않으므로 네트워크 없이도 동작한다.
|
|
76
83
|
if (opts.mode === "doctor") {
|
|
@@ -95,14 +102,14 @@ async function runCore(argv, { cwd = process.cwd(), source = { type: "git" }, cl
|
|
|
95
102
|
// 대화형 모드 — 인자 없이 실행 or --mode interactive
|
|
96
103
|
if (opts.mode === "interactive") {
|
|
97
104
|
if (!process.stdout.isTTY) {
|
|
98
|
-
console.error("
|
|
105
|
+
console.error(t("cli.nonTty"));
|
|
99
106
|
return 1;
|
|
100
107
|
}
|
|
101
108
|
return await runInteractive({}, { cwd, source, clock });
|
|
102
109
|
}
|
|
103
110
|
// 명시 모드인데 --force 없으면 (비대화형 CLI는 --force 필요)
|
|
104
111
|
if (!opts.force && !process.stdout.isTTY) {
|
|
105
|
-
console.error("
|
|
112
|
+
console.error(t("cli.needForce"));
|
|
106
113
|
return 1;
|
|
107
114
|
}
|
|
108
115
|
|
|
@@ -128,19 +135,19 @@ async function runCore(argv, { cwd = process.cwd(), source = { type: "git" }, cl
|
|
|
128
135
|
|
|
129
136
|
// 감지 (CLI 인자 우선, 없으면 자동 감지 — version.yml 우선 규칙은 detectTypes/detectVersion 내부)
|
|
130
137
|
const types = opts.types.length ? opts.types : detectTypes(cwd);
|
|
131
|
-
trace.event("detect", "types", types.join(",") || "(
|
|
132
|
-
source: opts.types.length ? "cli-flag(--type)" : "auto-detect(
|
|
138
|
+
trace.event("detect", "types", types.join(",") || "(none)", {
|
|
139
|
+
source: opts.types.length ? "cli-flag(--type)" : "auto-detect(marker file scan)",
|
|
133
140
|
});
|
|
134
141
|
// version: 기존 version.yml 최우선(SSoT — 재실행 시 덮어쓰기 방지) → CLI 지정 → 파일 감지
|
|
135
142
|
const version = (existing?.version) || opts.version || detectVersion(cwd);
|
|
136
143
|
trace.event("detect", "version", version, {
|
|
137
|
-
source: existing?.version ? "version.yml(
|
|
138
|
-
: (opts.version ? "cli-flag(--project-version)" : "
|
|
144
|
+
source: existing?.version ? "version.yml(existing value kept)"
|
|
145
|
+
: (opts.version ? "cli-flag(--project-version)" : "project file detection"),
|
|
139
146
|
});
|
|
140
147
|
const versionCode = existing?.versionCode ?? 1; // 기존 빌드번호 보존 (.sh L2208~2221)
|
|
141
148
|
const branch = detectDefaultBranch(cwd);
|
|
142
149
|
const repoName = detectRepoName(cwd);
|
|
143
|
-
trace.event("detect", "repo", repoName || "(
|
|
150
|
+
trace.event("detect", "repo", repoName || "(unknown)", { defaultBranch: branch, versionCode });
|
|
144
151
|
// 경로 확정 (.sh resolve_project_paths 비대화형 경로 — --paths 우선 → 저장값 → 후보 1개 자동 → 루트 폴백)
|
|
145
152
|
let cliPaths;
|
|
146
153
|
try {
|
|
@@ -166,9 +173,9 @@ async function runCore(argv, { cwd = process.cwd(), source = { type: "git" }, cl
|
|
|
166
173
|
const at = pth === "." ? marker : `${pth}/${marker}`;
|
|
167
174
|
trace.event("detect", "project-path", ty, {
|
|
168
175
|
path: pth,
|
|
169
|
-
marker: existsSync(join(cwd, at)) ? at : `${at} (
|
|
176
|
+
marker: existsSync(join(cwd, at)) ? at : `${at} (missing)`,
|
|
170
177
|
source: parsePathsCsv(opts.pathsCsv).has(ty) ? "cli-flag(--paths)"
|
|
171
|
-
: (existing?.paths?.has(ty) ? "version.yml(
|
|
178
|
+
: (existing?.paths?.has(ty) ? "version.yml(stored value)" : "marker file search"),
|
|
172
179
|
});
|
|
173
180
|
}
|
|
174
181
|
|
|
@@ -197,23 +204,23 @@ async function runCore(argv, { cwd = process.cwd(), source = { type: "git" }, cl
|
|
|
197
204
|
|
|
198
205
|
// 축 확정 근거 (#561) — "왜 이 값인가"가 가장 헷갈리는 자리다.
|
|
199
206
|
// CLI 플래그 / 저장값 / intent 유도 / 타입 적용성 정리 중 무엇이 이겼는지 남긴다.
|
|
200
|
-
trace.event("resolve", "intent", String(intent ?? "(
|
|
207
|
+
trace.event("resolve", "intent", String(intent ?? "(unset)"), {
|
|
201
208
|
source: opts.intent != null ? "cli-flag(--intent)"
|
|
202
|
-
: (existing?.options?.intent ? "version.yml(
|
|
209
|
+
: (existing?.options?.intent ? "version.yml(stored value)" : "not given, inferred from deploy/publish"),
|
|
203
210
|
});
|
|
204
211
|
trace.event("resolve", "deploy", deployTarget, {
|
|
205
212
|
source: opts.deployTarget != null ? "cli-flag(--deploy)"
|
|
206
|
-
: (existing?.options?.deploy ? "version.yml(
|
|
213
|
+
: (existing?.options?.deploy ? "version.yml(stored value)" : "default"),
|
|
207
214
|
applicableForTypes: applicable.deploy,
|
|
208
215
|
adjusted: beforeCleanup.deploy !== deployTarget
|
|
209
|
-
? `${beforeCleanup.deploy} → ${deployTarget} (
|
|
216
|
+
? `${beforeCleanup.deploy} → ${deployTarget} (not applicable to the selected types)` : null,
|
|
210
217
|
});
|
|
211
|
-
trace.event("resolve", "publish", publishTargets.join(",") || "(
|
|
218
|
+
trace.event("resolve", "publish", publishTargets.join(",") || "(none)", {
|
|
212
219
|
source: opts.publishTargets != null ? "cli-flag(--publish)"
|
|
213
|
-
: (existing?.options?.publish ? "version.yml(
|
|
220
|
+
: (existing?.options?.publish ? "version.yml(stored value)" : "default"),
|
|
214
221
|
applicableForTypes: applicable.publish,
|
|
215
222
|
adjusted: beforeCleanup.publish.join(",") !== publishTargets.join(",")
|
|
216
|
-
? `${beforeCleanup.publish.join(",") || "(
|
|
223
|
+
? `${beforeCleanup.publish.join(",") || "(none)"} → ${publishTargets.join(",") || "(none)"} (cleaned up, not applicable)` : null,
|
|
217
224
|
});
|
|
218
225
|
|
|
219
226
|
const context = createContext({
|
|
@@ -224,6 +231,11 @@ async function runCore(argv, { cwd = process.cwd(), source = { type: "git" }, cl
|
|
|
224
231
|
includeSecretBackup: opts.includeSecretBackup ?? existing?.options?.secretBackup ?? false,
|
|
225
232
|
// #566 — CLI 값 우선, 없으면 저장값 보존, 그것도 없으면 true(설정 없이 바로 동작).
|
|
226
233
|
aiPrSummary: opts.aiPrSummary ?? existing?.options?.aiPrSummary ?? true,
|
|
234
|
+
// Projects 보드 동기화(#716): CLI > 저장값 > 이미 설치돼 있으면 유지 > 신규는 제외.
|
|
235
|
+
// 설정(Secret, PROJECT_URL)이 없는 레포에 설치되면 라벨이 바뀔 때마다 알림만 쌓이기 때문이다.
|
|
236
|
+
// 이미 설치된 레포가 갱신만으로 연동을 잃지 않도록 파일이 있으면 켜진 것으로 본다.
|
|
237
|
+
projectsSync: opts.projectsSync ?? existing?.options?.projectsSync
|
|
238
|
+
?? existsSync(join(cwd, ".github/workflows/PROJECT-COMMON-PROJECTS-SYNC-MANAGER.yaml")),
|
|
227
239
|
// #502 — version 모드가 기존 full 통합 기록(mode)을 강등하지 않도록 (full이 우세)
|
|
228
240
|
recordMode: existing?.templateMode === "full" ? "full" : "version",
|
|
229
241
|
// 릴리스 배포 브랜치(#456): CLI 플래그 → version.yml 저장값 → 빈 값(미출력, 스킬이 develop 폴백)
|
|
@@ -239,6 +251,12 @@ async function runCore(argv, { cwd = process.cwd(), source = { type: "git" }, cl
|
|
|
239
251
|
// 앱 심사 배포 레포 여부(#553): 저장값만 보존한다. 마법사가 묻지 않으므로 새로 켜지 않는다
|
|
240
252
|
// (사용자가 version.yml에 직접 쓰거나 스킬이 기록한 값을 그대로 유지).
|
|
241
253
|
appRelease: existing?.options?.appRelease ?? null,
|
|
254
|
+
// 상태 라벨 표기(#776): 플래그 > 저장값 > (신규 en / 기존 ko). 기존 레포의 라벨 이름은 업데이트만으로 바뀌지 않는다.
|
|
255
|
+
// 릴리스 시 완료 이슈 닫기(#771): 저장값 보존, 없으면 신규만 true. 기존 레포는 키를 만들지 않아 현행 유지.
|
|
256
|
+
closeOnRelease: existing?.options?.closeOnRelease ?? (existing ? null : true),
|
|
257
|
+
// 레포 문구 언어(#769): 플래그 > 저장값 > (신규 en / 기존 ko). 기존 레포의 템플릿 언어는 업데이트만으로 바뀌지 않는다.
|
|
258
|
+
language: resolveRepoLanguage({ flag: opts.language, stored: existing?.options?.language, existing: !!existing }),
|
|
259
|
+
labelStyle: resolveLabelStyle({ flag: opts.labelStyle, stored: existing?.options?.labelStyle, existing: !!existing }),
|
|
242
260
|
repoName,
|
|
243
261
|
// 실 resolver 4종 (.sh resolve_token 등가 — spring-app-yml 스텁 제거)
|
|
244
262
|
resolvers: makeResolvers(cwd, repoName, paths),
|
|
@@ -252,16 +270,16 @@ async function runCore(argv, { cwd = process.cwd(), source = { type: "git" }, cl
|
|
|
252
270
|
secretBackup: context.includeSecretBackup,
|
|
253
271
|
changelogProvider: context.changelogProvider,
|
|
254
272
|
coderabbit: context.codeReviewCoderabbit,
|
|
255
|
-
deployBranch: context.deployBranch || "(
|
|
273
|
+
deployBranch: context.deployBranch || "(not given, falls back to develop)",
|
|
256
274
|
recordMode: context.recordMode,
|
|
257
275
|
});
|
|
258
276
|
trace.event("resolve", "semver-auto", String(context.semverAuto), {
|
|
259
|
-
reason: existing?.options?.semverAuto != null ? "version.yml
|
|
260
|
-
: (existing ? "
|
|
261
|
-
: "
|
|
277
|
+
reason: existing?.options?.semverAuto != null ? "stored value in version.yml kept"
|
|
278
|
+
: (existing ? "existing integrated repo, false to avoid unannounced version jumps"
|
|
279
|
+
: "new integration, true"),
|
|
262
280
|
});
|
|
263
281
|
trace.event("resolve", "app-release", String(context.appRelease), {
|
|
264
|
-
reason: existing?.options?.appRelease != null ? "version.yml
|
|
282
|
+
reason: existing?.options?.appRelease != null ? "stored value in version.yml kept" : "unset (key not written)",
|
|
265
283
|
});
|
|
266
284
|
|
|
267
285
|
let result = null;
|
|
@@ -289,7 +307,7 @@ async function runCore(argv, { cwd = process.cwd(), source = { type: "git" }, cl
|
|
|
289
307
|
}));
|
|
290
308
|
trace.event("breaking", "result", proceed ? "proceed" : "halt", { items: (breakingReport ?? []).length });
|
|
291
309
|
if (!proceed) {
|
|
292
|
-
trace.event("run", "cancelled", "breaking-gate", { reason: "
|
|
310
|
+
trace.event("run", "cancelled", "breaking-gate", { reason: "stopped by breaking-change warning" });
|
|
293
311
|
return 0;
|
|
294
312
|
}
|
|
295
313
|
|
|
@@ -305,12 +323,12 @@ async function runCore(argv, { cwd = process.cwd(), source = { type: "git" }, cl
|
|
|
305
323
|
const orphans = trace.step("orphan-scan",
|
|
306
324
|
() => detectOrphanWorkflows({ tempDir, targetRoot: cwd, selectedTypes: types,
|
|
307
325
|
// 껐는데 남아 계속 도는 워크플로우도 함께 잡는다 (#566)
|
|
308
|
-
options: { includeSecretBackup: context.includeSecretBackup, aiPrSummary: context.aiPrSummary, deployTarget } }),
|
|
326
|
+
options: { includeSecretBackup: context.includeSecretBackup, aiPrSummary: context.aiPrSummary, projectsSync: context.projectsSync, deployTarget } }),
|
|
309
327
|
{ selectedTypes: types });
|
|
310
328
|
orphanPending = orphans.map((o) => o.filename);
|
|
311
|
-
for (const o of orphans) trace.event("orphan", "detected", o.filename, { type: o.type, action: "
|
|
329
|
+
for (const o of orphans) trace.event("orphan", "detected", o.filename, { type: o.type, action: "notice only (non-interactive)" });
|
|
312
330
|
for (const o of orphans) {
|
|
313
|
-
console.error(
|
|
331
|
+
console.error(t("run.orphanNotice", { type: o.type, file: o.filename }));
|
|
314
332
|
}
|
|
315
333
|
}
|
|
316
334
|
|
|
@@ -334,7 +352,7 @@ async function runCore(argv, { cwd = process.cwd(), source = { type: "git" }, cl
|
|
|
334
352
|
migrationGuidePath = appendGuideEntry(cwd, {
|
|
335
353
|
now, mode: opts.mode, types, repoName,
|
|
336
354
|
templateFrom: existing?.templateVersion || "", templateTo: context.templateVersion,
|
|
337
|
-
options: { deploy: deployTarget, publish: publishTargets, secretBackup: context.includeSecretBackup, coderabbit: context.codeReviewCoderabbit, changelogProvider: context.changelogProvider, intent, semverAuto: context.semverAuto , appRelease: context.appRelease },
|
|
355
|
+
options: { deploy: deployTarget, publish: publishTargets, secretBackup: context.includeSecretBackup, coderabbit: context.codeReviewCoderabbit, changelogProvider: context.changelogProvider, intent, semverAuto: context.semverAuto , appRelease: context.appRelease, language: context.language, labelStyle: context.labelStyle, closeOnRelease: context.closeOnRelease, projectsSync: context.projectsSync },
|
|
338
356
|
branches: { defaultBranch: branch, deployBranch: context.deployBranch || "develop", ready: null, created: null },
|
|
339
357
|
breaking: breakingReport, migrations: migrationsResult, orphans: { cleaned: [], pending: orphanPending },
|
|
340
358
|
events: trace.events, counters: { skipped: result?.workflows?.skipped ?? 0 },
|
package/src/ui/banner.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
//
|
|
2
|
-
// .ps1 Print-Banner 계승 + 브랜딩을 projectops로 갱신.
|
|
1
|
+
// Start screen banner (layer 1). Classic boxed title, kept from the original wizard.
|
|
3
2
|
import { A, paint, visualWidth } from "./ansi.js";
|
|
3
|
+
import { t, getLang, getLangSource } from "../i18n/index.js";
|
|
4
4
|
|
|
5
|
-
const INNER = 56; //
|
|
5
|
+
const INNER = 56; // inner width of the box
|
|
6
6
|
|
|
7
7
|
function boxLine(out, content = "") {
|
|
8
8
|
const pad = Math.max(0, INNER - visualWidth(content));
|
|
9
9
|
out(paint("║", A.cyan) + content + " ".repeat(pad) + paint("║", A.cyan) + "\n");
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
//
|
|
12
|
+
// Interactive banner: boxed title + meta lines. The language line says which language is used and why,
|
|
13
|
+
// so a different locale on a teammate's machine is never a surprise.
|
|
13
14
|
export function printBanner({ version, modeLabel }, out = (s) => process.stdout.write(s)) {
|
|
14
15
|
out("\n");
|
|
15
16
|
out(paint(`╔${"═".repeat(INNER)}╗`, A.cyan) + "\n");
|
|
@@ -20,11 +21,12 @@ export function printBanner({ version, modeLabel }, out = (s) => process.stdout.
|
|
|
20
21
|
out(` 🌙 Version : ${paint(`v${version}`, A.green)}\n`);
|
|
21
22
|
out(` 🐵 Author : Cassiiopeia\n`);
|
|
22
23
|
out(` 🪐 Mode : ${modeLabel}\n`);
|
|
24
|
+
out(` 🌐 Language: ${getLang()} (${t(`cli.langSource.${getLangSource()}`)})\n`);
|
|
23
25
|
out(` 📦 Repo : ${paint("github.com/Cassiiopeia/projectops", A.dim)}\n`);
|
|
24
26
|
out("\n");
|
|
25
27
|
}
|
|
26
28
|
|
|
27
|
-
//
|
|
29
|
+
// One-line banner for non-interactive runs (--force / CI): keeps logs short but still shows the version.
|
|
28
30
|
export function printBannerCompact({ version, mode }, out = (s) => process.stdout.write(s)) {
|
|
29
|
-
out(`${paint("✦", A.yellow)} ${paint("projectops", A.bold)} v${version}
|
|
31
|
+
out(`${paint("✦", A.yellow)} ${paint("projectops", A.bold)} v${version} ${t("banner.compactMode", { mode })}\n`);
|
|
30
32
|
}
|