projectops 4.69.3 → 4.70.1

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 CHANGED
@@ -11,7 +11,7 @@
11
11
  **Status: actively maintained.** npm `latest` is exactly what is on the `main` branch; work in progress on `develop` is never published. Versions can still ship several times a day. To stay on a version you have tested, run `npx projectops@<version>` ([releases](https://github.com/Cassiiopeia/projectops/releases)).
12
12
 
13
13
  <!-- AUTO-VERSION-SECTION: DO NOT EDIT MANUALLY -->
14
- ## Latest version : v4.69.2 (2026-10-21)
14
+ ## Latest version : v4.70.0 (2026-10-21)
15
15
 
16
16
  [View full version history](CHANGELOG.md)
17
17
 
@@ -32,7 +32,7 @@
32
32
 
33
33
  ![npx projectops interactive wizard](https://raw.githubusercontent.com/Cassiiopeia/projectops/main/docs/images/cli-wizard.gif)
34
34
 
35
- > This is a **real run** on a sample Spring project (the wizard follows your system language, or use `--lang en|ko`). Version numbers shown were current when it was recorded. For non-interactive use and every option, see the [CLI reference](https://cassiiopeia.github.io/projectops/en/cli).
35
+ > This is a **real run** on a sample Spring project (the wizard follows your system language, or use `--lang en|ko`). Version numbers shown were current when it was recorded. For non-interactive use and every option, see the [CLI reference](https://cassiiopeia.github.io/projectops/cli).
36
36
 
37
37
  ---
38
38
 
@@ -104,7 +104,6 @@ Open a PR from the development branch to `main` and it writes the release notes,
104
104
 
105
105
  **Drive it from your agent:** `/pro-changelog-deploy` pushes develop and opens the release PR for you.
106
106
 
107
- > The warning banner GitHub shows for Korean branch names is hidden in the recording. Everything else is the unedited screen.
108
107
 
109
108
  ---
110
109
 
@@ -140,9 +139,9 @@ What it changed: descriptions were cut to short English triggers, a shared contr
140
139
 
141
140
  | Part | What it does | Docs |
142
141
  |---|---|---|
143
- | **GitHub Actions** | One push to main handles versioning, changelogs, and CI/CD deployment | [Versioning](https://cassiiopeia.github.io/projectops/VERSION-CONTROL) (Korean) |
144
- | **Agent Skills** (14) | `/pro-github`, `/pro-commit`, `/pro-report` and more let an AI agent drive each step: issues, commits in your style, reports, release PRs | [Skills guide](https://cassiiopeia.github.io/projectops/SKILLS) (Korean) |
145
- | **npx CLI** | Installs and updates workflows in an existing project, and diagnoses its state | [CLI reference](https://cassiiopeia.github.io/projectops/en/cli) |
142
+ | **GitHub Actions** | One push to main handles versioning, changelogs, and CI/CD deployment | [Versioning](https://cassiiopeia.github.io/projectops/version-control) |
143
+ | **Agent Skills** (14) | `/pro-github`, `/pro-commit`, `/pro-report` and more let an AI agent drive each step: issues, commits in your style, reports, release PRs | [Skills guide](https://cassiiopeia.github.io/projectops/skills) |
144
+ | **npx CLI** | Installs and updates workflows in an existing project, and diagnoses its state | [CLI reference](https://cassiiopeia.github.io/projectops/cli) |
146
145
 
147
146
  <details>
148
147
  <summary>See all 14 Agent Skills</summary>
@@ -292,7 +291,7 @@ Run automation by commenting on an issue or PR.
292
291
  | `@projectops ios build` | Build iOS only | Flutter |
293
292
  | `@projectops create qa` | Create a QA issue automatically | All projects |
294
293
 
295
- > Details: [PR Preview](docs/en/pr-preview.md) | [Flutter builds](docs/FLUTTER-TEST-BUILD-TRIGGER.md) | [Issue automation](docs/en/issue-automation.md)
294
+ > Details: [PR Preview](docs/en/pr-preview.md) | [Flutter builds](docs/en/flutter-test-build-trigger.md) | [Issue automation](docs/en/issue-automation.md)
296
295
 
297
296
  ---
298
297
 
@@ -342,18 +341,18 @@ We would rather tell you up front when this is not a fit.
342
341
 
343
342
  ## Documentation
344
343
 
345
- Browse and search everything on the **[docs site](https://cassiiopeia.github.io/projectops/en/)**. The main guides are in English. A few wizard pages are still Korean only (translations are welcome, see [#799](https://github.com/Cassiiopeia/projectops/issues/799)).
346
-
347
- - [Getting started](https://cassiiopeia.github.io/projectops/en/getting-started)
348
- - [CLI reference](https://cassiiopeia.github.io/projectops/en/cli)
349
- - [Agent Skills guide](https://cassiiopeia.github.io/projectops/en/skills)
350
- - [Versioning](https://cassiiopeia.github.io/projectops/en/version-control)
351
- - [Changelog automation](https://cassiiopeia.github.io/projectops/en/changelog-automation)
352
- - [PR Preview](https://cassiiopeia.github.io/projectops/en/pr-preview)
353
- - [Issue automation](https://cassiiopeia.github.io/projectops/en/issue-automation)
354
- - [SSH + Docker deploy](https://cassiiopeia.github.io/projectops/en/ssh-docker-deployment-guide)
355
- - [Flutter CI/CD](https://cassiiopeia.github.io/projectops/en/flutter-cicd-overview)
356
- - [Troubleshooting](https://cassiiopeia.github.io/projectops/en/troubleshooting)
344
+ Browse and search everything on the **[docs site](https://cassiiopeia.github.io/projectops/)**. The guides are available in English and [Korean](https://cassiiopeia.github.io/projectops/ko/).
345
+
346
+ - [Getting started](https://cassiiopeia.github.io/projectops/getting-started)
347
+ - [CLI reference](https://cassiiopeia.github.io/projectops/cli)
348
+ - [Agent Skills guide](https://cassiiopeia.github.io/projectops/skills)
349
+ - [Versioning](https://cassiiopeia.github.io/projectops/version-control)
350
+ - [Changelog automation](https://cassiiopeia.github.io/projectops/changelog-automation)
351
+ - [PR Preview](https://cassiiopeia.github.io/projectops/pr-preview)
352
+ - [Issue automation](https://cassiiopeia.github.io/projectops/issue-automation)
353
+ - [SSH + Docker deploy](https://cassiiopeia.github.io/projectops/ssh-docker-deployment-guide)
354
+ - [Flutter CI/CD](https://cassiiopeia.github.io/projectops/flutter-cicd-overview)
355
+ - [Troubleshooting](https://cassiiopeia.github.io/projectops/troubleshooting)
357
356
 
358
357
  ---
359
358
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "projectops",
3
- "version": "4.69.3",
3
+ "version": "4.70.1",
4
4
  "description": "ProjectOps: installer CLI for a fully automated GitHub project management template (versioning, CI/CD, release notes, agent skills)",
5
5
  "keywords": [
6
6
  "devops",
@@ -3,6 +3,7 @@
3
3
  import { join } from "node:path";
4
4
  import * as nodeFs from "node:fs";
5
5
  import { PATHS } from "./paths.js";
6
+ import { getLang } from "../i18n/index.js";
6
7
 
7
8
  // wizard-prompts.yml 위치 (레포 루트 기준 상대경로)
8
9
  export const LABELS_FILE = ".github/config/wizard-prompts.yml";
@@ -15,14 +16,17 @@ function unquote(s) {
15
16
  }
16
17
 
17
18
  // wizard-prompts.yml 텍스트 → 파싱 객체 (순수 함수 — 테스트 직접 사용 가능).
18
- // 반환: { fields: Map<조회키, {label?,help?,example?}>, workflowNames: [{key,value}] }
19
+ // 반환: { fields: Map<조회키, {label?,help?,example?,label_ko?...}>, workflowNames: [{key,value}],
20
+ // workflowNamesByLang: Map<언어, [{key,value}]> }
19
21
  // - 조회키: "PROJECT_NAME" 또는 "flutter.APP_ARTIFACT_NAME" (dotted 타입 오버라이드)
22
+ // - 언어별 문구 (#976): label/help/example = 영어 원문, label_ko 등 = 번역. _workflow_names_ko = 번역 블록
20
23
  // - 구형 1줄(KEY: "라벨")은 fields의 label로 흡수 (label만 의미)
21
24
  export function parseWizardPrompts(text) {
22
25
  const fields = new Map();
23
26
  const workflowNames = [];
27
+ const workflowNamesByLang = new Map();
24
28
  let current = null; // 현재 블록의 fields 엔트리 (들여쓰기 라인 소속처)
25
- let inWfNames = false; // _workflow_names 블록 내부 여부
29
+ let currentWfNames = null; // _workflow_names(_언어) 블록 내부면 그 목록
26
30
 
27
31
  for (const raw of String(text).split(/\r?\n/)) {
28
32
  const line = raw.replace(/\r$/, "");
@@ -30,12 +34,21 @@ export function parseWizardPrompts(text) {
30
34
 
31
35
  if (!/^\s/.test(line)) {
32
36
  // 최상위 키 라인 — 이전 블록 종료
33
- current = null; inWfNames = false;
37
+ current = null; currentWfNames = null;
34
38
  const m = line.match(/^([A-Za-z_][A-Za-z0-9_.\-]*):(.*)$/);
35
39
  if (!m) continue;
36
40
  const key = m[1];
37
41
  const rest = m[2].trim();
38
- if (key === "_workflow_names") { inWfNames = true; continue; }
42
+ const wfNamesBlock = key.match(/^_workflow_names(?:_([a-z]{2}(?:-[A-Z]{2})?))?$/);
43
+ if (wfNamesBlock) {
44
+ const lang = wfNamesBlock[1];
45
+ if (!lang) currentWfNames = workflowNames;
46
+ else {
47
+ if (!workflowNamesByLang.has(lang)) workflowNamesByLang.set(lang, []);
48
+ currentWfNames = workflowNamesByLang.get(lang);
49
+ }
50
+ continue;
51
+ }
39
52
  const entry = fields.get(key) || {};
40
53
  if (rest) {
41
54
  // 구형 1줄: KEY: "라벨" (label로만 사용)
@@ -50,18 +63,19 @@ export function parseWizardPrompts(text) {
50
63
  // 들여쓰기 라인 — 현재 블록 소속
51
64
  const m = line.match(/^\s+([A-Za-z_][A-Za-z0-9_.\-]*):\s*(.*)$/);
52
65
  if (!m) continue;
53
- if (inWfNames) {
66
+ if (currentWfNames) {
54
67
  // " KEY: "값"" 형식만 인정
55
68
  const q = m[2].match(/^"(.*)"\s*$/);
56
- if (q) workflowNames.push({ key: m[1], value: q[1] });
69
+ if (q) currentWfNames.push({ key: m[1], value: q[1] });
57
70
  continue;
58
71
  }
59
- if (current && (m[1] === "label" || m[1] === "help" || m[1] === "example")) {
72
+ // 필드명: label | help | example, 번역은 _언어 접미사 (label_ko)
73
+ if (current && /^(label|help|example)(_[a-z]{2}(-[A-Z]{2})?)?$/.test(m[1])) {
60
74
  // 블록 내 첫 등장만 채택
61
75
  if (current[m[1]] == null) current[m[1]] = unquote(m[2]);
62
76
  }
63
77
  }
64
- return { fields, workflowNames };
78
+ return { fields, workflowNames, workflowNamesByLang };
65
79
  }
66
80
 
67
81
  // wizard-prompts.yml을 찾아 읽고 파싱.
@@ -78,13 +92,17 @@ export function loadWizardPrompts(targetRoot = ".", tempDir = "", fs = nodeFs) {
78
92
  return null;
79
93
  }
80
94
 
81
- // 필드 조회. field: "label" | "help" | "example".
95
+ // 필드 조회. field: "label" | "help" | "example", lang: 화면 언어 (기본 현재 CLI 언어)
82
96
  // 우선순위: "{type}.KEY" 블록 → "KEY" 블록(구형 1줄 포함) → 폴백(label이면 KEY명, 아니면 "").
83
- export function wfField(prompts, type, key, field) {
97
+ // 블록마다 번역(label_ko) 먼저, 없으면 영어 원문(label)
98
+ export function wfField(prompts, type, key, field, lang = getLang()) {
84
99
  if (prompts && prompts.fields) {
85
100
  for (const q of [`${type}.${key}`, key]) {
86
- const v = prompts.fields.get(q)?.[field];
87
- if (v != null && v !== "") return v;
101
+ const entry = prompts.fields.get(q);
102
+ for (const name of [`${field}_${lang}`, field]) {
103
+ const v = entry?.[name];
104
+ if (v != null && v !== "") return v;
105
+ }
88
106
  }
89
107
  }
90
108
  return field === "label" ? key : "";
@@ -92,12 +110,13 @@ export function wfField(prompts, type, key, field) {
92
110
 
93
111
  // 워크플로우 파일명 → 사람이 읽는 짧은 이름.
94
112
  // _workflow_names에서 "키가 파일명에 포함되면" 그 값 사용 — 최장 키 우선(REACT-CI vs REACT-CICD 구분).
95
- // 미매칭이면 .yaml/.yml 확장자만 제거해 반환.
96
- export function workflowDisplayName(prompts, filename) {
113
+ // 미매칭이면 .yaml/.yml 확장자만 제거해 반환. 언어 번역 블록(_workflow_names_ko)에 키가 있으면 그 값
114
+ export function workflowDisplayName(prompts, filename, lang = getLang()) {
97
115
  const base = String(filename).split("/").pop().split("\\").pop(); // 경로 제거
116
+ const translated = new Map((prompts?.workflowNamesByLang?.get(lang) ?? []).map(({ key, value }) => [key, value]));
98
117
  let best = null; let bestLen = 0;
99
118
  for (const { key, value } of prompts?.workflowNames ?? []) {
100
- if (base.includes(key) && key.length > bestLen) { best = value; bestLen = key.length; }
119
+ if (base.includes(key) && key.length > bestLen) { best = translated.get(key) ?? value; bestLen = key.length; }
101
120
  }
102
121
  if (best != null) return best;
103
122
  return base.replace(/\.ya?ml$/, "");