projectops 4.89.3 → 4.90.2

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
@@ -8,13 +8,6 @@
8
8
 
9
9
  [![npm](https://img.shields.io/npm/v/projectops?label=npm)](https://www.npmjs.com/package/projectops) [![Release](https://img.shields.io/github/v/release/Cassiiopeia/projectops?label=release)](https://github.com/Cassiiopeia/projectops/releases) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![Docs](https://img.shields.io/badge/docs-site-4f46e5)](https://cassiiopeia.github.io/projectops/)
10
10
 
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. Note that `npx projectops@<version>` pins the installer only (workflow templates always come from the current `main`) ([releases](https://github.com/Cassiiopeia/projectops/releases)).
12
-
13
- <!-- AUTO-VERSION-SECTION: DO NOT EDIT MANUALLY -->
14
- ## Latest version : v4.89.3 (2026-11-07)
15
-
16
- [View full version history](CHANGELOG.md)
17
-
18
11
  </div>
19
12
 
20
13
  ---
@@ -34,6 +27,13 @@
34
27
 
35
28
  > 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
29
 
30
+ **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. Note that `npx projectops@<version>` pins the installer only (workflow templates always come from the current `main`) ([releases](https://github.com/Cassiiopeia/projectops/releases)).
31
+
32
+ <!-- AUTO-VERSION-SECTION: DO NOT EDIT MANUALLY -->
33
+ ## Latest version : v4.90.2 (2026-11-08)
34
+
35
+ [View full version history](CHANGELOG.md)
36
+
37
37
  ---
38
38
 
39
39
  ## Why this template?
@@ -107,6 +107,14 @@ Open a PR from the development branch to `main` and it writes the release notes,
107
107
  **Drive it from your agent:** `/pro-changelog-deploy` pushes develop and opens the release PR for you.
108
108
 
109
109
 
110
+ ### 4. See UI mockups, press through them, and pick one
111
+
112
+ ![Two linked mockup screens updating each other](https://raw.githubusercontent.com/Cassiiopeia/projectops/main/docs/images/feature-ui-design.gif)
113
+
114
+ Ask for mockups and get a local page where they really move and respond. Here two screens share one state: an order placed on the left appears in the kitchen on the right. Mockups use your repo's tokens and fonts, and the same run can post a GIF like this one to the issue.
115
+
116
+ **Drive it from your agent:** `/pro-ui-design` shows the options, records your pick for the next skills, and hands it to a designer if you cannot decide.
117
+
110
118
  ---
111
119
 
112
120
  ## Make it yours
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "projectops",
3
- "version": "4.89.3",
3
+ "version": "4.90.2",
4
4
  "description": "One npx install for GitHub release automation: semantic versioning, changelogs, release PRs, CI/CD workflows and AI agent skills",
5
5
  "keywords": [
6
6
  "github-actions",
@@ -50,6 +50,16 @@ export function applySafeMigrations(targetRoot, entries) {
50
50
  // log - 한 줄 출력 함수 (기본 console.log)
51
51
  // removeLegacy - true면 confirm 티어(구세대 배포 워크플로우)도 .bak 무해화 (--remove-legacy, #809)
52
52
  // 반환: { applied: [결과], confirmPending: [entry], askPending: [entry] }
53
+ // 폐기 워크플로에서 옮긴 설정 > 화면과 실행 기록용 줄 (사용자가 설정이 옮겨졌는지 확인, #1060)
54
+ export function describeCarriedSettings(results) {
55
+ const lines = [];
56
+ for (const result of results) {
57
+ if (result.carried?.length) lines.push(t("migrate.safe.carried", { file: result.from, keys: result.carried.join(", ") }));
58
+ if (result.carryFailed) lines.push(t("migrate.safe.carryFailed", { file: result.from, error: result.carryFailed }));
59
+ }
60
+ return lines;
61
+ }
62
+
53
63
  export async function runMigrations({ targetRoot = ".", askYesNo = null, log = console.log, removeLegacy = false } = {}) {
54
64
  const { safe, confirm, ask } = detectMigrations(targetRoot);
55
65
  if (safe.length === 0 && confirm.length === 0 && ask.length === 0) {
@@ -73,6 +83,7 @@ export async function runMigrations({ targetRoot = ".", askYesNo = null, log = c
73
83
  const failed = applied.filter((r) => r.action === "error");
74
84
  log(t("migrate.safe.done", { n: ok.length, failed: failed.length ? t("migrate.safe.failed", { n: failed.length }) : "" }));
75
85
  for (const f of failed) log(` ⚠️ ${f.from}: ${f.error}`);
86
+ for (const line of describeCarriedSettings(ok)) log(line);
76
87
  } else {
77
88
  log(t("migrate.safe.skip"));
78
89
  }
@@ -23,9 +23,10 @@ export function detect(targetRoot, entry) {
23
23
  export function apply(targetRoot, entry) {
24
24
  // 무해화 전 설정 이관 (실패해도 무해화는 진행 — 부분 실패 허용 원칙)
25
25
  let carried = [];
26
+ let carryFailed = null;
26
27
  if (entry.settingsExtractor && EXTRACTORS[entry.settingsExtractor]) {
27
28
  try { carried = EXTRACTORS[entry.settingsExtractor](targetRoot, entry).carried; }
28
- catch { carried = []; }
29
+ catch (error) { carried = []; carryFailed = error?.message || String(error); }
29
30
  }
30
31
  const src = target(targetRoot, entry);
31
32
  const bak = `${src}.bak`;
@@ -33,5 +34,6 @@ export function apply(targetRoot, entry) {
33
34
  renameSync(src, bak);
34
35
  const result = { action: "bak", from: entry.file, to: `${entry.file}.bak` };
35
36
  if (carried.length > 0) result.carried = carried;
37
+ if (carryFailed) result.carryFailed = carryFailed;
36
38
  return result;
37
39
  }
@@ -48,6 +48,8 @@ export default {
48
48
  "migrate.safe.ask": "Clean up the {n} file(s) above? (workflows are neutralized as .bak and can be restored)",
49
49
  "migrate.safe.done": "Legacy cleanup done: {n}{failed}",
50
50
  "migrate.safe.failed": " ({n} failed)",
51
+ "migrate.safe.carried": " Settings moved to version.yml from {file}: {keys}",
52
+ "migrate.safe.carryFailed": " ⚠️ Could not move settings from {file}: {error}. Copy them to version.yml by hand; the old file is kept as .bak",
51
53
  "migrate.safe.skip": "Skipping legacy cleanup (you will be asked again on the next update)",
52
54
  "migrate.dir.found": "Found {n} output folder(s) with an old name. They contain your documents, so they are moved only after confirmation:",
53
55
  "migrate.dir.ask": "Move the folders above to the new name? (no files are lost, the original is kept on conflict)",
@@ -48,6 +48,8 @@ export default {
48
48
  "migrate.safe.ask": "위 {n}개를 정리할까요? (워크플로우는 .bak 무해화 — 복원 가능)",
49
49
  "migrate.safe.done": "✅ 레거시 정리 완료: {n}개{failed}",
50
50
  "migrate.safe.failed": " (실패 {n}개)",
51
+ "migrate.safe.carried": " {file}의 설정을 version.yml로 옮겼습니다: {keys}",
52
+ "migrate.safe.carryFailed": " ⚠️ {file}의 설정을 옮기지 못했습니다: {error}. version.yml에 직접 옮겨 주세요. 옛 파일은 .bak으로 남아 있습니다",
51
53
  "migrate.safe.skip": "→ 레거시 정리를 건너뜁니다 (다음 업데이트 때 다시 안내)",
52
54
  "migrate.dir.found": "📁 구명칭 산출물 폴더 {n}개 감지 — 사용자 문서가 들어 있어 확인 후 이동합니다:",
53
55
  "migrate.dir.ask": "위 폴더를 새 이름으로 이동할까요? (파일 손실 없음 — 충돌 시 원본 유지)",