project-auto-wizard 0.1.9 → 0.1.10

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
@@ -16,7 +16,7 @@ npx project-auto-wizard
16
16
  [![node](https://img.shields.io/badge/node-%3E%3D20.12-brightgreen)](package.json)
17
17
 
18
18
  <!-- AUTO-VERSION-SECTION: DO NOT EDIT MANUALLY -->
19
- ## 최신 버전 : v0.1.9 (2026-08-02)
19
+ ## 최신 버전 : v0.1.10 (2026-08-02)
20
20
 
21
21
  [전체 버전 기록 보기](CHANGELOG.md)
22
22
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "project-auto-wizard",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "One command DevOps: npx wizard that installs GitHub-native AI Release Automation into any project",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,5 +1,6 @@
1
1
  // full 모드 오케스트레이터 (.sh execute_integration full case 등가).
2
- // 복사 순서: workflows(+env 치환) → version.yml → readme → scripts → coderabbit → gitignore
2
+ // 복사 순서: workflows(+env 치환) → version.yml → readme → scripts → coderabbit → gitignore(조건부)
3
+ // gitignore는 충돌 백업 부산물(.bak/.template.yaml)이 이번 실행에서 실제로 생겼을 때만 갱신한다 — issue #7.
3
4
  // (원본의 util/issue/discussion/setup-guide/config 설치는 project-auto-wizard 스코프에서 제외 — DESIGN-SPEC §2)
4
5
  import { join } from "node:path";
5
6
  import { writeText } from "../core/fsutil.js";
@@ -46,9 +47,13 @@ export function runFull(context, payloadRoot, targetRoot = ".", hooks = {}) {
46
47
  // 4. scripts (payload/scripts/*.py → .github/scripts/)
47
48
  copyScripts(payloadRoot, targetRoot);
48
49
 
49
- // 5. coderabbit (opt-in true일 때만 — DESIGN-SPEC §4 질문②) / gitignore
50
- if (includeCodeRabbit === true) copyCoderabbit(payloadRoot, { force }, targetRoot);
51
- ensureGitignore(targetRoot);
50
+ // 5. coderabbit (opt-in true일 때만 — DESIGN-SPEC §4 질문②)
51
+ const coderabbitResult = includeCodeRabbit === true ? copyCoderabbit(payloadRoot, { force }, targetRoot) : null;
52
52
 
53
- return { workflows: wfCounters };
53
+ // 6. gitignore 워크플로우/coderabbit 충돌 처리가 .bak나 .template.yaml을 실제로 만든 경우에만 갱신한다.
54
+ // 충돌 없는 설치(대부분의 최초 설치)는 .gitignore를 전혀 건드리지 않는다 — issue #7.
55
+ const gitignoreUpdated = wfCounters.backupAdded > 0 || wfCounters.templateAdded > 0 || coderabbitResult === "overwritten-backup";
56
+ if (gitignoreUpdated) ensureGitignore(targetRoot);
57
+
58
+ return { workflows: wfCounters, gitignoreUpdated };
54
59
  }
@@ -249,6 +249,7 @@ export async function runInteractive(baseCtx, { cwd = process.cwd(), payloadRoot
249
249
  io.summary?.({
250
250
  mode, types, version, branches, includeCodeRabbit,
251
251
  counters: { workflows: result?.workflows?.copied ?? 0 },
252
+ gitignoreUpdated: result?.gitignoreUpdated === true,
252
253
  }, cwd);
253
254
  io.outro?.(`통합 완료 — ${mode} 모드로 설치했습니다.`);
254
255
  return 0;
@@ -80,7 +80,7 @@ export function printPurgeResult(result) {
80
80
  if (result.versionYml) lines.push("파일: version.yml");
81
81
  if (result.readmeSection) lines.push("README.md: AUTO-VERSION-SECTION 블록");
82
82
  for (const f of result.changelog) lines.push(`파일: ${f}`);
83
- lines.push("(.gitignore 자동 추가 블록은 보존됩니다 별도 이슈에서 다룸)");
83
+ lines.push("(.gitignore 추가된 백업 파일 제외 항목(*.bak/*.template.yaml)은 purge 대상에서 제외되어 그대로 보존됩니다)");
84
84
  lines.push("");
85
85
  console.log(lines.join("\n"));
86
86
  }
@@ -1,6 +1,7 @@
1
1
  // version 모드 (.sh execute_integration version case 등가).
2
- // 순서: version.yml → readme → scripts → gitignore.
3
- // (워크플로우 미복사 deploy 블록 없음. util·issue·coderabbit·setup-guide는 스코프 제외.)
2
+ // 순서: version.yml → readme → scripts.
3
+ // (워크플로우/coderabbit을 복사하지 않으므로 충돌 백업 부산물이 생길 수 없다 — gitignore 갱신 대상 없음, issue #7.
4
+ // util·issue·coderabbit·setup-guide는 스코프 제외.)
4
5
  import { join } from "node:path";
5
6
  import { writeText } from "../core/fsutil.js";
6
7
  import { PATHS } from "../core/paths.js";
@@ -9,7 +10,6 @@ import { readVersionYmlTemplate } from "../core/assets.js";
9
10
  import { markerForType } from "../core/detect.js";
10
11
  import { addVersionSectionToReadme } from "../core/copy/readme.js";
11
12
  import { copyScripts } from "../core/copy/simple.js";
12
- import { ensureGitignore } from "../core/copy/gitignore.js";
13
13
 
14
14
  export function runVersion(context, payloadRoot, targetRoot = ".") {
15
15
  const { version, types = [], paths = new Map(), branch = "main", versionCode = 1,
@@ -28,5 +28,4 @@ export function runVersion(context, payloadRoot, targetRoot = ".") {
28
28
  }));
29
29
  addVersionSectionToReadme(version, targetRoot);
30
30
  copyScripts(payloadRoot, targetRoot);
31
- ensureGitignore(targetRoot);
32
31
  }
@@ -1,8 +1,17 @@
1
- // .gitignore 보장 (.sh ensure_gitignore + normalize/check 등가) template_integrator.sh 3996~4111.
1
+ // .gitignore 보장 마법사 자신이 만드는 충돌 백업 부산물(*.bak, *.template.yaml)만 대상으로 한다.
2
+ // 마법사가 설치하는 것과 무관한 개인 개발환경 설정(IDE 등)은 마법사 책임 범위 밖 — issue #7.
3
+ // 주의(의도된 트레이드오프): 이 변경 이전 버전으로 설치해 /.idea·/.claude/settings.local.json이 이미
4
+ // 배너 블록에 남아있는 레포에서 removeAutoAddedEntriesFromGitignore()를 실행하면, 배너는 제거되지만
5
+ // REQUIRED_ENTRIES가 더 이상 옛 항목과 일치하지 않아 그 두 줄은 지워지지 않고 배너 표시 없이 남는다.
6
+ // 이슈 #7이 "이미 설치된 레포의 .gitignore는 소급 처리하지 않고 사용자 판단에 맡긴다"고 명시하므로
7
+ // 별도 마이그레이션 로직을 추가하지 않는다 — 남은 항목은 무해하며 필요하면 사용자가 직접 지운다.
2
8
  import { join } from "node:path";
3
9
  import { existsSync, readFileSync, writeFileSync, rmSync } from "node:fs";
4
10
 
5
- const REQUIRED_ENTRIES = ["/.idea", "/.claude/settings.local.json"];
11
+ // issue #7: 마법사가 설치하는 것과 무관한 개인 개발환경 항목(/.idea 등)은 마법사 책임 밖이므로 제거.
12
+ // 대신 마법사 자신의 충돌 처리(workflows.js backup/template 결정, coderabbit.js 덮어쓰기 백업)가
13
+ // 실제로 만들어내는 부산물만 gitignore 대상으로 삼는다.
14
+ const REQUIRED_ENTRIES = ["*.bak", "*.template.yaml"];
6
15
 
7
16
  // .sh normalize_gitignore_entry: 주석 제거·트림·앞 / 제거·앞 ./ 제거·뒤 / 제거. 빈값이면 원본.
8
17
  export function normalizeGitignoreEntry(entry) {
@@ -26,11 +35,9 @@ function entryExists(target, content) {
26
35
  }
27
36
 
28
37
  const NEW_FILE_CONTENT =
29
- "# IDE Settings\n" +
30
- "/.idea\n" +
31
- "\n" +
32
- "# Claude AI Settings\n" +
33
- "/.claude/settings.local.json\n";
38
+ "# project-auto-wizard: 충돌 처리 시 생성되는 백업 파일 (안전하게 무시해도 됩니다)\n" +
39
+ "*.bak\n" +
40
+ "*.template.yaml\n";
34
41
 
35
42
  // 반환: {created, added:[...]}
36
43
  export function ensureGitignore(targetRoot = ".") {
@@ -58,7 +58,7 @@ function classify(srcDir, workflowsDir, envOpts, srcText) {
58
58
  // envValues?:Map<key,value>, envUseDefaults?:boolean } ← env 계획(promptEnvPlan) 결과 주입점
59
59
  // hooks: { decisions?: Map<filename, 'skip'|'backup'|'template'> } — 기존 파일(changed) 충돌 결정.
60
60
  // 미지정 파일은 'skip'(현행 force 동작 100% 유지). 대화형 수집은 copyWorkflowsInteractive 참조.
61
- // 반환: {copied, skipped, templateAdded, optionalCopied}
61
+ // 반환: {copied, skipped, templateAdded, optionalCopied, backupAdded}
62
62
  export function copyWorkflows(context, payloadRoot, targetRoot = ".", hooks = {}) {
63
63
  const { types = [], paths = new Map(), includeNexus = false, includeSecretBackup = false, repoName = "", resolvers = {}, envValues = new Map(), envUseDefaults = true } = context;
64
64
  const decisions = hooks.decisions instanceof Map ? hooks.decisions : new Map();
@@ -66,7 +66,7 @@ export function copyWorkflows(context, payloadRoot, targetRoot = ".", hooks = {}
66
66
  const projectTypesDir = join(payloadRoot, PAYLOAD.workflowsDir);
67
67
  if (!exists(projectTypesDir)) throw new Error("패키지 구조 오류 — payload/workflows 폴더를 찾지 못했습니다.");
68
68
 
69
- const counters = { copied: 0, skipped: 0, templateAdded: 0, optionalCopied: 0 };
69
+ const counters = { copied: 0, skipped: 0, templateAdded: 0, optionalCopied: 0, backupAdded: 0 };
70
70
  const deployValues = new Map(); // Map<type, Map<key,value>> — deploy 블록용 ask 값
71
71
  counters.deployValues = deployValues;
72
72
  const srcText = makeSrcText(context.branches || null);
@@ -124,6 +124,7 @@ function applyDecision(decision, srcDir, workflowsDir, filename, counters, srcTe
124
124
  renameSync(dst, dst + ".bak");
125
125
  writeText(dst, srcText(src));
126
126
  counters.copied++;
127
+ counters.backupAdded++;
127
128
  return;
128
129
  }
129
130
  if (decision === "template") {
@@ -213,7 +214,7 @@ function copyWorkflowsForType(type, projectTypesDir, workflowsDir, ctx, counters
213
214
  counters.skipped++;
214
215
  continue;
215
216
  }
216
- if (existsSync(dst)) renameSync(dst, dst + ".bak");
217
+ if (existsSync(dst)) { renameSync(dst, dst + ".bak"); counters.backupAdded++; }
217
218
  writeText(dst, body);
218
219
  counters.optionalCopied++;
219
220
  counters.copied++;
package/src/index.js CHANGED
@@ -311,6 +311,7 @@ export async function run(argv, {
311
311
  mode: opts.mode, types, version, branches,
312
312
  includeCodeRabbit: context.includeCodeRabbit === true,
313
313
  counters: { workflows: result?.workflows?.copied ?? 0 },
314
+ gitignoreUpdated: result?.gitignoreUpdated === true,
314
315
  }, cwd);
315
316
  return 0;
316
317
  }
package/src/ui/summary.js CHANGED
@@ -8,7 +8,7 @@ import { listYamlFiles } from "../core/fsutil.js";
8
8
  const SEPARATOR = "────────────────────────────────────────";
9
9
 
10
10
  export function printSummary(ctx, targetRoot = ".") {
11
- const { mode, types = [], version = "", counters = {}, branches = null, includeCodeRabbit = false } = ctx || {};
11
+ const { mode, types = [], version = "", counters = {}, branches = null, includeCodeRabbit = false, gitignoreUpdated = false } = ctx || {};
12
12
  const err = (s = "") => process.stderr.write(`${s}\n`);
13
13
  // 색상은 TTY일 때만 (.sh YELLOW/CYAN/NC 등가)
14
14
  const isTty = !!process.stderr.isTTY;
@@ -32,12 +32,11 @@ export function printSummary(ctx, targetRoot = ".") {
32
32
  err(" ✅ 버전 관리 시스템 (version.yml)");
33
33
  err(" ✅ README.md 자동 버전 업데이트");
34
34
  err(" ✅ GitHub Actions 워크플로우 (AI 릴리스 자동화 포함)");
35
- err(" ✅ .gitignore 필수 항목");
35
+ if (gitignoreUpdated) err(" ✅ .gitignore 백업 파일 제외 항목 (*.bak/*.template.yaml)");
36
36
  break;
37
37
  case "version":
38
38
  err(" ✅ 버전 관리 시스템 (version.yml)");
39
39
  err(" ✅ README.md 자동 버전 업데이트");
40
- err(" ✅ .gitignore 필수 항목");
41
40
  break;
42
41
  case "workflows":
43
42
  err(" ✅ GitHub Actions 워크플로우 (AI 릴리스 자동화 포함)");