projectops 4.70.0 → 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 +18 -19
- package/package.json +1 -1
- package/src/core/wizard-labels.js +34 -15
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.
|
|
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
|

|
|
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/
|
|
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/
|
|
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/
|
|
145
|
-
| **npx CLI** | Installs and updates workflows in an existing project, and diagnoses its state | [CLI reference](https://cassiiopeia.github.io/projectops/
|
|
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/
|
|
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/
|
|
346
|
-
|
|
347
|
-
- [Getting started](https://cassiiopeia.github.io/projectops/
|
|
348
|
-
- [CLI reference](https://cassiiopeia.github.io/projectops/
|
|
349
|
-
- [Agent Skills guide](https://cassiiopeia.github.io/projectops/
|
|
350
|
-
- [Versioning](https://cassiiopeia.github.io/projectops/
|
|
351
|
-
- [Changelog automation](https://cassiiopeia.github.io/projectops/
|
|
352
|
-
- [PR Preview](https://cassiiopeia.github.io/projectops/
|
|
353
|
-
- [Issue automation](https://cassiiopeia.github.io/projectops/
|
|
354
|
-
- [SSH + Docker deploy](https://cassiiopeia.github.io/projectops/
|
|
355
|
-
- [Flutter CI/CD](https://cassiiopeia.github.io/projectops/
|
|
356
|
-
- [Troubleshooting](https://cassiiopeia.github.io/projectops/
|
|
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
|
@@ -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
|
|
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
|
|
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;
|
|
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
|
-
|
|
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 (
|
|
66
|
+
if (currentWfNames) {
|
|
54
67
|
// " KEY: "값"" 형식만 인정
|
|
55
68
|
const q = m[2].match(/^"(.*)"\s*$/);
|
|
56
|
-
if (q)
|
|
69
|
+
if (q) currentWfNames.push({ key: m[1], value: q[1] });
|
|
57
70
|
continue;
|
|
58
71
|
}
|
|
59
|
-
|
|
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
|
-
|
|
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
|
|
87
|
-
|
|
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$/, "");
|