projectops 4.37.0 → 4.38.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 CHANGED
@@ -6,10 +6,10 @@
6
6
 
7
7
  [![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/)
8
8
 
9
- **Status: actively maintained.** New versions ship often, sometimes several a day. To stay on a version you have tested, run `npx projectops@<version>` ([releases](https://github.com/Cassiiopeia/projectops/releases)).
9
+ **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)).
10
10
 
11
11
  <!-- AUTO-VERSION-SECTION: DO NOT EDIT MANUALLY -->
12
- ## Latest version : v4.36.4 (2026-10-07)
12
+ ## Latest version : v4.37.0 (2026-10-10)
13
13
 
14
14
  [View full version history](CHANGELOG.md)
15
15
 
@@ -300,7 +300,7 @@ We would rather tell you up front when this is not a fit.
300
300
  - **Releases assume a develop branch → default branch PR flow.** Branch names can be changed in `version.yml`, but it does not suit a repo that does not use two branches.
301
301
  - **A personal access token is optional.** You need one only to merge past branch protection or to sync a Projects board. See [Setup](#setup).
302
302
  - **It adds about 50 files to your repository** (workflows, helper scripts, templates). See [the measurement](#compared-with-other-tools).
303
- - **New versions ship often.** Pin a version you have tested with `npx projectops@<version>`.
303
+ - **New versions ship often.** `latest` follows every push to `main` with no delay. Pin a version you have tested with `npx projectops@<version>`.
304
304
  - **Server deploy workflows assume a Docker server reachable over SSH.**
305
305
 
306
306
  ---
@@ -327,6 +327,7 @@ Browse and search everything on the **[docs site](https://cassiiopeia.github.io/
327
327
  - [Issues](https://github.com/Cassiiopeia/projectops/issues): bug reports and feature requests
328
328
  - Finished issues are marked with the `status: done` label and closed automatically when a release is merged (`close_on_release`).
329
329
  - [CONTRIBUTING.md](CONTRIBUTING.md): contribution guide
330
+ - [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md): how we expect everyone to behave, and where to report problems
330
331
  - [SECURITY.md](SECURITY.md): please report security vulnerabilities privately, not in a public issue
331
332
 
332
333
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "projectops",
3
- "version": "4.37.0",
3
+ "version": "4.38.0",
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",
package/src/cli/args.js CHANGED
@@ -5,12 +5,13 @@ import { VALID_TYPES } from "../context.js";
5
5
  import { SUPPORTED_LANGS, t } from "../i18n/index.js";
6
6
  import { REPO_LANGUAGES } from "../core/repo-language.js";
7
7
 
8
- export const DEPLOY_TARGETS = ["docker-ssh", "vercel", "none"];
9
- export const PUBLISH_TARGETS = ["nexus", "npm", "github-packages"];
10
- export const LABEL_STYLE_VALUES = ["en", "ko"];
8
+ import { DEPLOY_TARGETS, PUBLISH_TARGETS, INTENT_VALUES, LABEL_STYLES } from "../core/constants.js";
9
+
10
+ // 값 목록은 core/constants.js 가 정본이다. 기존 import 경로를 깨지 않도록 여기서 다시 내보낸다.
11
+ export { DEPLOY_TARGETS, PUBLISH_TARGETS, INTENT_VALUES };
12
+ export const LABEL_STYLE_VALUES = LABEL_STYLES;
11
13
  // 지원 언어의 단일 목록은 repo-language.js 다 (#787). 여기서 따로 들고 있으면 두 벌이 어긋난다.
12
14
  export const LANGUAGE_VALUES = REPO_LANGUAGES;
13
- export const INTENT_VALUES = ["app", "library", "both", "none", "manual"];
14
15
  const SEMVER_RE = /^\d+\.\d+\.\d+$/;
15
16
  // 유니코드 글자·숫자 허용 (#719) — 공백·셸 메타문자는 문자 클래스에 없어 그대로 거부된다.
16
17
  const BRANCH_RE = /^[\p{L}\p{N}][\p{L}\p{N}._/-]*$/u;
package/src/context.js CHANGED
@@ -27,7 +27,7 @@ export function createContext(overrides = {}) {
27
27
  codeReviewCoderabbit: null,
28
28
  deployBranch: "", // 릴리스 PR head 브랜치 (#456). 빈 값=metadata.deploy_branch 미출력
29
29
  intent: null, // 프로젝트 성격 (#485 — app/library/both/none/manual). null=미설정(역추론)
30
- semverAuto: null, // semver 자동 승격 (#546). null=미설정(신규 통합 true / 기존 레포 false)
30
+ semverAuto: null, // semver 자동 승격 (#546). null=미설정 → 신규·기존 모두 true (#814). 명시적 false 만 끈다
31
31
  closeOnRelease: null, // 릴리스 시 완료 이슈 닫기 (#771). null=미설정(신규 true / 기존 레포 키 없음)
32
32
  language: null, // 레포 문구 언어 (#769). null=미설정(신규 en / 기존 레포 ko)
33
33
  labelStyle: null, // 상태 라벨 표기 (#776). null=미설정(신규 en / 기존 레포 ko)
@@ -0,0 +1,28 @@
1
+ // 여러 파일이 함께 써야 하는 값 목록의 정본.
2
+ //
3
+ // 왜 한 곳인가: deploy·publish 목록이 CLI 인자 검사(args.js), 마법사 질문(options-ask.js),
4
+ // 복사 엔진(copy/workflows.js), version.yml 파서(version-yml.js), 옵션 표(options-schema.js)
5
+ // 다섯 곳에 따로 적혀 있었다. 하나만 고치면 "CLI 는 받는데 파서가 버린다" 같은 조용한 불일치가 생긴다.
6
+ // 값을 추가할 때는 여기 한 줄만 고치고, 그 값에 해당하는 워크플로우 폴더를 만든다
7
+ // (deploy: project-types/common/deploy/<값>/ · publish: project-types/<type>/publish/<값>/).
8
+ //
9
+ // 이 파일은 아무것도 import 하지 않는다 — 어디서 불러도 순환 import 가 생기지 않게.
10
+ // 레포 문구 언어 목록은 repo-language.js 의 REPO_LANGUAGES 가 정본이다 (번역 파일과 같이 검사된다).
11
+
12
+ /** 실행물을 어디에 배포하나 — 택1 (#439) */
13
+ export const DEPLOY_TARGETS = Object.freeze(["docker-ssh", "vercel", "none"]);
14
+
15
+ /** 라이브러리를 어느 레지스트리에 내나 — 0..n (#439) */
16
+ export const PUBLISH_TARGETS = Object.freeze(["nexus", "npm", "github-packages"]);
17
+
18
+ /** 프로젝트 성격 — deploy/publish 질문을 유도한다 (#485) */
19
+ export const INTENT_VALUES = Object.freeze(["app", "library", "both", "none", "manual"]);
20
+
21
+ /** 상태 라벨 표기 — en: `status: todo`, ko: 기존 한글 `작업전` (#776) */
22
+ export const LABEL_STYLES = Object.freeze(["en", "ko"]);
23
+
24
+ /**
25
+ * 릴리스 노트 생성기 (#455, #566). 기본값은 commit — 외부 의존이 없어 어디서나 결과가 나온다.
26
+ * 실제 구현은 .github/scripts/changelog_providers/ 에 있고, 그쪽 목록과의 정합은 테스트가 본다.
27
+ */
28
+ export const CHANGELOG_PROVIDERS = Object.freeze(["copilot", "coderabbit", "openai", "gemini", "claude", "groq", "mistral", "ollama", "commit"]);
@@ -11,6 +11,7 @@ import { isUserModified, readBaseline, writeBaseline, sha256 } from "../baseline
11
11
  import { substituteBranches } from "../branch-sub.js";
12
12
  import { saveIncoming, lineDiffCounts } from "../incoming.js";
13
13
  import { branchStatus } from "../git-branch.js";
14
+ import { PUBLISH_TARGETS } from "../constants.js";
14
15
 
15
16
  // on: 블록의 브랜치 필터가 전부 develop 뿐인가 (#810).
16
17
  // 개발 브랜치가 없는 레포에 깔면 영영 실행되지 않는 워크플로우를 가린다.
@@ -430,7 +431,6 @@ export async function copyWorkflowsInteractive(context, tempDir, targetRoot = ".
430
431
  return copyWorkflows(context, tempDir, targetRoot, { decisions });
431
432
  }
432
433
 
433
- const PUBLISH_TARGETS = ["nexus", "npm", "github-packages"];
434
434
 
435
435
  function copyWorkflowsForType(type, projectTypesDir, workflowsDir, ctx, counters) {
436
436
  const { deployTarget = "docker-ssh", publishTargets = [], force = false, paths = new Map(), repoName = "", resolvers = {}, envOptsFor, collectAsks = null, decisions = new Map(), trace = null, baseline = null } = ctx;
@@ -5,6 +5,7 @@
5
5
  export const DOCS_TO_REMOVE = [
6
6
  "CONTRIBUTING.md",
7
7
  "SECURITY.md",
8
+ "CODE_OF_CONDUCT.md",
8
9
  "ARCHITECTURE.md",
9
10
  "CLAUDE.md",
10
11
  "AGENTS.md",
@@ -13,6 +13,7 @@ import { PATHS } from "./paths.js";
13
13
  import { t as msg } from "../i18n/index.js";
14
14
  import { parseTemplateOptions, inferIntent } from "./version-yml.js";
15
15
  import { branchStatus, createBranch, pushBranch } from "./git-branch.js";
16
+ import { DEPLOY_TARGETS, PUBLISH_TARGETS, CHANGELOG_PROVIDERS } from "./constants.js";
16
17
 
17
18
  // 개발(배포) 브랜치 존재 확인 + 생성 제안 (#477) — 대화형 전용.
18
19
  // 로컬에 없고 원격에도 없(거나 불명이)면 기본 브랜치에서 생성을 제안하고, 생성 후 push 여부도 묻는다.
@@ -55,8 +56,8 @@ export async function ensureDeployBranch({ targetRoot = ".", deployBranch = "",
55
56
  // 재노출 — 파서 본체는 version-yml.js에 있다 (순환 import 방지: options-ask → version-yml 방향만 허용)
56
57
  export { parseTemplateOptions };
57
58
 
58
- export const DEPLOY_TARGETS = ["docker-ssh", "vercel", "none"];
59
- export const PUBLISH_TARGETS = ["nexus", "npm", "github-packages"];
59
+ // 값 목록은 core/constants.js 가 정본이다. 기존 import 경로를 깨지 않도록 다시 내보낸다.
60
+ export { DEPLOY_TARGETS, PUBLISH_TARGETS, CHANGELOG_PROVIDERS };
60
61
 
61
62
  // #498 — 타입별 적용 가능 deploy/publish 타겟 선언.
62
63
  // 빈 배열 = 그 축이 개념상 성립하지 않는 타입. 모바일 앱(flutter/react-native/expo)은 스토어 배포
@@ -95,9 +96,9 @@ export function applicableTargets(types = []) {
95
96
  }
96
97
  // changelog 생성기 provider (#455, #566에서 재정비).
97
98
  // 기본값은 commit — 외부 의존이 없어 어떤 환경에서도 결과가 나온다. AI를 쓰려면
98
- // MODEL_API_KEY를 등록하면 사다리가 자동으로 집어 쓴다(마법사는 묻지 않는다).
99
+ // 서비스별 키(GEMINI_API_KEY 등)를 등록하면 사다리가 자동으로 집어 쓴다(마법사는 묻지 않는다).
100
+ // MODEL_API_KEY 는 구 이름이라 하위호환으로만 읽는다.
99
101
  // github-ai는 GitHub Models 종료(2026-07-30)로 제외됐다.
100
- export const CHANGELOG_PROVIDERS = ["copilot", "coderabbit", "openai", "gemini", "claude", "groq", "mistral", "ollama", "commit"];
101
102
 
102
103
  // 더 이상 동작하지 않는 저장값을 살아있는 값으로 옮긴다 (#566).
103
104
  // 기존 저장소가 업데이트만 돌려도 죽은 설정에서 벗어나게 하는 유일한 경로다.
@@ -11,6 +11,11 @@
11
11
  //
12
12
  // 설명은 영문이다 — 기계가 읽는 표이고, 화면 언어(i18n)와 무관하게 같아야 한다.
13
13
 
14
+ import { DEPLOY_TARGETS, PUBLISH_TARGETS, INTENT_VALUES, LABEL_STYLES, CHANGELOG_PROVIDERS } from "./constants.js";
15
+ import { REPO_LANGUAGES } from "./repo-language.js";
16
+ import { VALID_TYPES } from "../context.js";
17
+ import { SUPPORTED_LANGS } from "../i18n/index.js";
18
+
14
19
  export const SCHEMA_VERSION = 1;
15
20
 
16
21
  export const MODES = [
@@ -30,22 +35,22 @@ export const FLAGS = [
30
35
  description: "What to do. Agents and CI should always pass an explicit non-interactive mode together with --force." },
31
36
  { flag: "--type", alias: "-t", value: "CSV", default: "auto-detected",
32
37
  description: "Project types, comma separated. The first one is the primary type.",
33
- values: ["spring", "flutter", "react", "react-native", "react-native-expo", "node", "python", "basic"] },
38
+ values: [...VALID_TYPES] },
34
39
  { flag: "--project-version", value: "VERSION", default: "auto-detected",
35
40
  description: "Initial version of the target project (x.y.z)." },
36
41
  { flag: "--paths", value: "type=path,...", default: "repo root",
37
42
  description: "Per-type project folders for monorepos, e.g. flutter=app,react=client.", versionYml: "project_paths" },
38
- { flag: "--intent", value: "KIND", values: ["app", "library", "both", "none", "manual"], default: "inferred from --deploy/--publish",
43
+ { flag: "--intent", value: "KIND", values: [...INTENT_VALUES], default: "inferred from --deploy/--publish",
39
44
  description: "Project kind. Decides whether deploy and publish questions are asked.", versionYml: "metadata.template.options.intent" },
40
- { flag: "--deploy", value: "TARGET", values: ["docker-ssh", "vercel", "none"], default: "docker-ssh",
45
+ { flag: "--deploy", value: "TARGET", values: [...DEPLOY_TARGETS], default: "docker-ssh",
41
46
  description: "Where the app is deployed. Pick one.", versionYml: "metadata.template.options.deploy" },
42
- { flag: "--publish", value: "CSV", values: ["nexus", "npm", "github-packages"], default: "none",
47
+ { flag: "--publish", value: "CSV", values: [...PUBLISH_TARGETS], default: "none",
43
48
  description: "Registries a library is published to. Several allowed.", versionYml: "metadata.template.options.publish" },
44
49
  { flag: "--deploy-branch", value: "NAME", default: "develop",
45
50
  description: "Development branch: the head of the release PR. Not the repository default branch.", versionYml: "metadata.deploy_branch" },
46
- { flag: "--language", value: "LANG", values: ["en", "ko"], default: "en for new installs, ko for existing installs",
51
+ { flag: "--language", value: "LANG", values: [...REPO_LANGUAGES], default: "en for new installs, ko for existing installs",
47
52
  description: "Language of issue/PR templates and bot messages written into the repo.", versionYml: "metadata.template.options.language" },
48
- { flag: "--label-style", value: "STYLE", values: ["en", "ko"], default: "en for new installs, ko for existing installs",
53
+ { flag: "--label-style", value: "STYLE", values: [...LABEL_STYLES], default: "en for new installs, ko for existing installs",
49
54
  description: "Status label names: en = 'status: todo', ko = legacy Korean names.", versionYml: "metadata.template.options.label_style" },
50
55
  { flag: "--secret-backup", negation: "--no-secret-backup",
51
56
  description: "Include / exclude the workflow that uploads GitHub Secrets to a server over SSH.", default: "excluded",
@@ -59,7 +64,7 @@ export const FLAGS = [
59
64
  { flag: "--remove-legacy",
60
65
  description: "Rename retired old-generation workflows to .bak. Without it they are only listed in the summary and in doctor.", default: "off" },
61
66
  { flag: "--force", alias: "-y", aliases: ["-y", "--yes"], description: "Skip every confirmation and use non-interactive defaults. Required for agents and CI.", default: "off" },
62
- { flag: "--lang", value: "LANG", values: ["en", "ko"], default: "system language; pinned to en in CI and with --force",
67
+ { flag: "--lang", value: "LANG", values: [...SUPPORTED_LANGS], default: "system language; pinned to en in CI and with --force",
63
68
  description: "Language of the CLI screen. Not the language written into the repo (that is --language)." },
64
69
  { flag: "--json", description: "With --mode options: print machine-readable JSON instead of a table.", default: "off" },
65
70
  { flag: "--version", alias: "-v", description: "Print the projectops package version and exit." },
@@ -78,11 +83,11 @@ export const VERSION_YML = [
78
83
  { path: "metadata.default_branch", type: "string", default: "main", description: "Repository default (production) branch. Pushes here deploy.", edit: "wizard" },
79
84
  { path: "metadata.deploy_branch", parserKey: "deployBranch", type: "string", default: "develop",
80
85
  description: "Development branch: head of the release PR. Despite the name it is NOT where deploys run.", edit: "wizard (--deploy-branch)" },
81
- { path: "metadata.template.options.intent", parserKey: "intent", type: "enum", values: ["app", "library", "both", "none", "manual"],
86
+ { path: "metadata.template.options.intent", parserKey: "intent", type: "enum", values: [...INTENT_VALUES],
82
87
  description: "Project kind; derives the deploy and publish questions.", edit: "wizard (--intent)" },
83
- { path: "metadata.template.options.deploy", parserKey: "deploy", type: "enum", values: ["docker-ssh", "vercel", "none"], default: "docker-ssh",
88
+ { path: "metadata.template.options.deploy", parserKey: "deploy", type: "enum", values: [...DEPLOY_TARGETS], default: "docker-ssh",
84
89
  description: "Where the app is deployed.", edit: "wizard (--deploy)" },
85
- { path: "metadata.template.options.publish", parserKey: "publish", type: "enum[]", values: ["nexus", "npm", "github-packages"], default: [],
90
+ { path: "metadata.template.options.publish", parserKey: "publish", type: "enum[]", values: [...PUBLISH_TARGETS], default: [],
86
91
  description: "Library registries.", edit: "wizard (--publish)" },
87
92
  { path: "metadata.template.options.secret_backup", parserKey: "secretBackup", type: "boolean", default: false,
88
93
  description: "Secret-to-server upload workflow installed.", edit: "wizard (--secret-backup)" },
@@ -92,8 +97,8 @@ export const VERSION_YML = [
92
97
  edit: "by hand" },
93
98
  { path: "metadata.template.options.close_on_release", parserKey: "closeOnRelease", type: "boolean", default: "true for new installs; missing for existing installs",
94
99
  description: "A release merge closes issues that carry the done label and are referenced by a release commit.", edit: "by hand" },
95
- { path: "metadata.template.options.language", parserKey: "language", type: "enum", values: ["en", "ko"], description: "Issue/PR template and bot message language.", edit: "wizard (--language)" },
96
- { path: "metadata.template.options.label_style", parserKey: "labelStyle", type: "enum", values: ["en", "ko"], description: "Status label names.", edit: "wizard (--label-style)" },
100
+ { path: "metadata.template.options.language", parserKey: "language", type: "enum", values: [...REPO_LANGUAGES], description: "Issue/PR template and bot message language.", edit: "wizard (--language)" },
101
+ { path: "metadata.template.options.label_style", parserKey: "labelStyle", type: "enum", values: [...LABEL_STYLES], description: "Status label names.", edit: "wizard (--label-style)" },
97
102
  { path: "metadata.template.options.projects_sync", parserKey: "projectsSync", type: "boolean", description: "Projects status sync workflow installed.", edit: "wizard (--projects-sync)" },
98
103
  { path: "metadata.template.options.app_release", parserKey: "appRelease", type: "boolean",
99
104
  description: "Releases go through App Store / Play Store review. Release notes get a review warning banner. Never auto-enabled.", edit: "by hand" },
@@ -103,7 +108,7 @@ export const VERSION_YML = [
103
108
  { path: "metadata.template.options.code_review.coderabbit", parserKey: "codeReviewCoderabbit", type: "boolean", description: "CodeRabbit config installed.", edit: "wizard" },
104
109
  { path: "metadata.template.options.code_review.ai_summary", parserKey: "aiPrSummary", type: "boolean", default: true, description: "AI PR summary workflow installed.", edit: "wizard (--ai-summary)" },
105
110
  { path: "metadata.template.options.changelog.provider", parserKey: "changelogProvider", type: "enum",
106
- values: ["commit", "openai", "gemini", "claude", "groq", "mistral", "ollama", "copilot", "coderabbit"], default: "commit",
111
+ values: [...CHANGELOG_PROVIDERS], default: "commit",
107
112
  description: "Release note generator. 'commit' is free and always works; AI keys are picked up automatically when registered as secrets.", edit: "by hand" },
108
113
  { path: "metadata.template.options.changelog.base_url", parserKey: "changelogBaseUrl", type: "string", description: "Only for provider ollama.", edit: "by hand" },
109
114
  ];
@@ -2,6 +2,14 @@
2
2
  // ⚠️ YAML 재직렬화 금지 — 주석이 데이터. .sh heredoc과 바이트 동일한 템플릿 문자열.
3
3
  // 실측 기준: template_integrator.sh 2184~2354.
4
4
  import { execFileSync } from "node:child_process";
5
+ import { DEPLOY_TARGETS, LABEL_STYLES } from "./constants.js";
6
+ import { REPO_LANGUAGES } from "./repo-language.js";
7
+
8
+ // 값 목록으로 만든 매칭 패턴 — 목록에 언어·표기를 추가하면 파서가 저절로 읽는다.
9
+ // (예전엔 `(en|ko)` 가 박혀 있어 세 번째 언어를 저장하면 다음 업데이트에서 못 읽고 ko 로 되돌아갔다.)
10
+ const alt = (values) => values.map((v) => v.replace(/[.*+?^${}()|[\]\\-]/g, "\\$&")).join("|");
11
+ const LANGUAGE_RE = new RegExp(`^\\s+language:\\s*["']?(${alt(REPO_LANGUAGES)})["']?`);
12
+ const LABEL_STYLE_RE = new RegExp(`^\\s+label_style:\\s*["']?(${alt(LABEL_STYLES)})["']?`);
5
13
 
6
14
  const HEADER_EN = `# ===================================================================
7
15
  # Project version management file
@@ -19,11 +27,11 @@ const HEADER_EN = `# ===========================================================
19
27
  # Automatic version updates:
20
28
  # - version_code: incremented by 1 on every build (independent of the rules below)
21
29
  # - How far the version is bumped is decided by metadata.template.options.semver_auto
22
- # semver_auto: true -> decided by the commit titles in the release range
30
+ # semver_auto: true -> decided by the commit titles in the release range (also when the key is missing)
23
31
  # "title : feat! : body" or "feat!:" -> major (x+1.0.0)
24
32
  # "title : feat : body" or "feat:" -> minor (x.y+1.0)
25
33
  # anything else (fix/docs/chore/refactor/test) -> patch (x.y.z+1)
26
- # key missing / false -> always patch +1
34
+ # semver_auto: false -> always patch +1
27
35
  # - You may edit the version value by hand regardless of the rules above
28
36
  #
29
37
  # Files synced per project type:
@@ -64,11 +72,11 @@ const HEADER_KO = `# ===========================================================
64
72
  # 자동 버전 업데이트:
65
73
  # - version_code: 매 빌드마다 자동으로 1씩 증가 (아래 판정과 무관)
66
74
  # - 버전 승격 폭은 metadata.template.options.semver_auto 가 정합니다
67
- # semver_auto: true -> 릴리스 구간 커밋 제목으로 판정
75
+ # semver_auto: true -> 릴리스 구간 커밋 제목으로 판정 (키가 없어도 같다)
68
76
  # "제목 : feat! : 내용" 또는 "feat!:" -> major (x+1.0.0)
69
77
  # "제목 : feat : 내용" 또는 "feat:" -> minor (x.y+1.0)
70
78
  # 그 외(fix/docs/chore/refactor/test) -> patch (x.y.z+1)
71
- # 키 없음 / false -> 항상 patch +1
79
+ # semver_auto: false -> 항상 patch +1
72
80
  # - 위 판정과 무관하게 version 값을 직접 수정해도 됩니다
73
81
  #
74
82
  # 프로젝트 타입별 동기화 파일:
@@ -198,7 +206,7 @@ export function parseTemplateOptions(content) {
198
206
  let m = line.match(/^\s+deploy:\s*(.+)/);
199
207
  if (m) {
200
208
  const v = strip(m[1]);
201
- if (["docker-ssh", "vercel", "none"].includes(v)) out.deploy = v;
209
+ if (DEPLOY_TARGETS.includes(v)) out.deploy = v;
202
210
  continue;
203
211
  }
204
212
  m = line.match(/^\s+publish:\s*\[([^\]]*)\]/);
@@ -220,7 +228,7 @@ export function parseTemplateOptions(content) {
220
228
  if (v === "false") out.secretBackup = false;
221
229
  continue;
222
230
  }
223
- // semver 자동 승격(#546). null(미기재)은 "기존 통합 레포" 신호 — 호출부가 OFF로 해석한다.
231
+ // semver 자동 승격(#546). null(미기재)이면 호출부가 ON(true)으로 해석한다 (#814). 명시적 false 만 끈다.
224
232
  // 캡처를 true|false로 한정한다(intent와 같은 방식) — 값 뒤 인라인 주석이 값에 딸려오면
225
233
  // strip()은 따옴표·공백만 걷어내므로 "true # ..."가 되어 매칭이 조용히 실패한다.
226
234
  m = line.match(/^\s+semver_auto:\s*["']?(true|false)["']?/);
@@ -229,13 +237,13 @@ export function parseTemplateOptions(content) {
229
237
  continue;
230
238
  }
231
239
  // 레포 문구 언어(#769) — en | ko. 미기재는 "기존 통합 레포" 신호라 호출부가 ko 로 해석한다.
232
- m = line.match(/^\s+language:\s*["']?(en|ko)["']?/);
240
+ m = line.match(LANGUAGE_RE);
233
241
  if (m) {
234
242
  out.language = m[1];
235
243
  continue;
236
244
  }
237
245
  // 상태 라벨 표기(#776) — en(영문 표준) | ko(기존 한글). 미기재는 "기존 통합 레포" 신호라 호출부가 ko로 해석한다.
238
- m = line.match(/^\s+label_style:\s*["']?(en|ko)["']?/);
246
+ m = line.match(LABEL_STYLE_RE);
239
247
  if (m) {
240
248
  out.labelStyle = m[1];
241
249
  continue;
@@ -435,7 +443,7 @@ export function buildVersionYml({ version, types = [], paths = new Map(), pathMa
435
443
 
436
444
  let out = (lang === "ko" ? HEADER_KO : HEADER_EN) + "\n";
437
445
  out += `version: "${version}"\n`;
438
- out += `version_code: ${versionCode} # ${C.versionCode}\n`;
446
+ out += `version_code: ${versionCode} # ${C.versionCode}\n`; // 공백 1칸 — version_manager.py 가 쓰는 형태와 같아야 업데이트 diff 가 안 생긴다
439
447
  out += `project_types: ${typesJson} # ${C.projectTypes}\n`;
440
448
 
441
449
  // project_paths 블록. pathMarkers: Map<type, markerFilename> (있으면 " type: "path" # path/marker" 주석).
package/src/index.js CHANGED
@@ -253,9 +253,8 @@ async function runCore(argv, { cwd = process.cwd(), source = { type: "git" }, cl
253
253
  changelogProvider: migrateProvider(existing?.options?.changelogProvider) ?? "commit",
254
254
  changelogBaseUrl: existing?.options?.changelogBaseUrl ?? "",
255
255
  codeReviewCoderabbit: existing?.options?.codeReviewCoderabbit ?? false,
256
- // semver 자동 승격(#546): 저장값 → (기존 통합 레포면 false / 신규면 true).
257
- // 이미 통합된 레포의 버전이 업데이트만으로 예고 없이 minor로 튀지 않게 하는 안전장치다.
258
- // 저장값(명시적 false 포함)은 존중하고, 키가 없으면 기존 레포도 켠다. 켜진 사실은 완료 화면이 알린다.
256
+ // semver 자동 승격(#546, #814): 저장값(명시적 false 포함)은 존중하고, 키가 없으면 신규·기존 모두 켠다.
257
+ // 기존 레포에서 처음 켜질 때는 버전이 예고 없이 오르지 않게 완료 화면이 알린다 (semverAutoNewlyOn).
259
258
  semverAuto: existing?.options?.semverAuto ?? true,
260
259
  // 앱 심사 배포 레포 여부(#553): 저장값만 보존한다. 마법사가 묻지 않으므로 새로 켜지 않는다
261
260
  // (사용자가 version.yml에 직접 쓰거나 스킬이 기록한 값을 그대로 유지).