@decencia/ch-cli 1.7.0 → 1.8.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/agent/2t-decencia-channel-issue-coder.md +1 -1
- package/agent/2t-decencia-channel-pr-merger.md +1 -1
- package/agent/2t-decencia-channel-terraformer.md +1 -1
- package/dist/client.d.ts +8 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +68 -1
- package/dist/client.js.map +1 -1
- package/dist/commands/db-schema.d.ts.map +1 -1
- package/dist/commands/db-schema.js +16 -2
- package/dist/commands/db-schema.js.map +1 -1
- package/dist/commands/db-tables.d.ts.map +1 -1
- package/dist/commands/db-tables.js +7 -5
- package/dist/commands/db-tables.js.map +1 -1
- package/dist/commands/dev-status.d.ts.map +1 -1
- package/dist/commands/dev-status.js +24 -6
- package/dist/commands/dev-status.js.map +1 -1
- package/dist/commands/prd.d.ts.map +1 -1
- package/dist/commands/prd.js +16 -2
- package/dist/commands/prd.js.map +1 -1
- package/dist/commands/qna.d.ts.map +1 -1
- package/dist/commands/qna.js +46 -6
- package/dist/commands/qna.js.map +1 -1
- package/dist/commands/sprints.d.ts.map +1 -1
- package/dist/commands/sprints.js +25 -6
- package/dist/commands/sprints.js.map +1 -1
- package/dist/config.d.ts +15 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +42 -0
- package/dist/config.js.map +1 -1
- package/dist/index.js +14 -2
- package/dist/index.js.map +1 -1
- package/dist/output.d.ts +13 -0
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +58 -9
- package/dist/output.js.map +1 -1
- package/dist/utils.d.ts +41 -2
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +89 -8
- package/dist/utils.js.map +1 -1
- package/package.json +2 -2
- package/skill/2t-decencia-channel-change-manager/SKILL.md +1 -1
- package/skill/2t-decencia-channel-change-propagation-v2/SKILL.md +1 -1
- package/skill/2t-decencia-channel-cli-v2/SKILL.md +1 -1
- package/skill/2t-decencia-channel-db-schema-v2/SKILL.md +1 -1
- package/skill/2t-decencia-channel-github-issue/SKILL.md +1 -1
- package/skill/2t-decencia-channel-orchestrator/SKILL.md +1 -1
- package/skill/2t-decencia-channel-prd-v2/SKILL.md +1 -1
- package/skill/2t-decencia-channel-project-bootstrap/SKILL.md +1 -1
- package/skill/2t-decencia-channel-spec-v2/SKILL.md +1 -1
- package/skill/2t-decencia-channel-sprint-builder-v2/SKILL.md +1 -1
- package/skill/2t-decencia-channel-sprint-runner/SKILL.md +94 -79
- package/skill/2t-decencia-channel-sqa-v2/SKILL.md +1 -1
- package/skill/2t-decencia-channel-work-status-v2/SKILL.md +1 -1
package/dist/utils.js
CHANGED
|
@@ -34,16 +34,35 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.parseCommaSeparated = parseCommaSeparated;
|
|
37
|
+
exports.normalizeContent = normalizeContent;
|
|
37
38
|
exports.readJsonFile = readJsonFile;
|
|
39
|
+
exports.readContentFile = readContentFile;
|
|
40
|
+
exports.readContentStdin = readContentStdin;
|
|
41
|
+
exports.readContentInput = readContentInput;
|
|
38
42
|
const fs = __importStar(require("fs"));
|
|
39
43
|
const path = __importStar(require("path"));
|
|
40
44
|
function parseCommaSeparated(value) {
|
|
41
45
|
return value.split(',').map((v) => v.trim()).filter(Boolean);
|
|
42
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* content류(자유 텍스트/마크다운) 문자열을 정규화합니다.
|
|
49
|
+
* - 선행 BOM() 제거
|
|
50
|
+
* - CRLF(\r\n) / 고립 CR(\r) → LF(\n) 정규화 (PowerShell Out-File·Windows 호환)
|
|
51
|
+
*
|
|
52
|
+
* 업로드되는 본문은 항상 이 함수를 거쳐 일관된 LF 줄바꿈으로 전송합니다.
|
|
53
|
+
*/
|
|
54
|
+
function normalizeContent(raw) {
|
|
55
|
+
let text = raw;
|
|
56
|
+
// BOM 제거
|
|
57
|
+
if (text.charCodeAt(0) === 0xfeff) {
|
|
58
|
+
text = text.slice(1);
|
|
59
|
+
}
|
|
60
|
+
// CRLF/CR → LF 정규화
|
|
61
|
+
return text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
62
|
+
}
|
|
43
63
|
/**
|
|
44
64
|
* JSON 파일을 읽어 파싱합니다.
|
|
45
|
-
* - BOM() 제거
|
|
46
|
-
* - CRLF/CR을 LF로 정규화 (PowerShell Out-File 호환)
|
|
65
|
+
* - BOM() 제거 + CRLF/CR을 LF로 정규화 (PowerShell Out-File 호환)
|
|
47
66
|
* - 파일 미존재/파싱 실패 시 명확한 에러 throw
|
|
48
67
|
*/
|
|
49
68
|
function readJsonFile(filePath, label) {
|
|
@@ -59,12 +78,7 @@ function readJsonFile(filePath, label) {
|
|
|
59
78
|
catch (e) {
|
|
60
79
|
throw new Error(`${tag}파일 읽기 실패: ${resolved} (${e?.message ?? e})`);
|
|
61
80
|
}
|
|
62
|
-
|
|
63
|
-
if (raw.charCodeAt(0) === 0xfeff) {
|
|
64
|
-
raw = raw.slice(1);
|
|
65
|
-
}
|
|
66
|
-
// CRLF/CR → LF 정규화
|
|
67
|
-
raw = raw.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
81
|
+
raw = normalizeContent(raw);
|
|
68
82
|
try {
|
|
69
83
|
return JSON.parse(raw);
|
|
70
84
|
}
|
|
@@ -72,4 +86,71 @@ function readJsonFile(filePath, label) {
|
|
|
72
86
|
throw new Error(`${tag}JSON 파싱 실패: ${resolved} (${e?.message ?? e})`);
|
|
73
87
|
}
|
|
74
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* 파일 경로에서 content를 읽어 정규화합니다.
|
|
91
|
+
* - 파일 미존재/읽기 실패 시 명확한 에러 throw
|
|
92
|
+
* - normalizeContent로 BOM/CRLF 정규화
|
|
93
|
+
*/
|
|
94
|
+
function readContentFile(filePath, label) {
|
|
95
|
+
const resolved = path.resolve(filePath);
|
|
96
|
+
const tag = label ? `${label} ` : '';
|
|
97
|
+
if (!fs.existsSync(resolved)) {
|
|
98
|
+
throw new Error(`${tag}파일을 찾을 수 없습니다: ${resolved}`);
|
|
99
|
+
}
|
|
100
|
+
let raw;
|
|
101
|
+
try {
|
|
102
|
+
raw = fs.readFileSync(resolved, 'utf-8');
|
|
103
|
+
}
|
|
104
|
+
catch (e) {
|
|
105
|
+
throw new Error(`${tag}파일 읽기 실패: ${resolved} (${e?.message ?? e})`);
|
|
106
|
+
}
|
|
107
|
+
return normalizeContent(raw);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* stdin(파이프)에서 content를 동기적으로 읽어 정규화합니다.
|
|
111
|
+
* TTY(대화형 터미널)면 입력이 없는 것으로 보고 null을 반환합니다(무한 대기 방지).
|
|
112
|
+
*/
|
|
113
|
+
function readContentStdin() {
|
|
114
|
+
if (process.stdin.isTTY) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
let raw;
|
|
118
|
+
try {
|
|
119
|
+
raw = fs.readFileSync(0, 'utf-8');
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
if (raw.length === 0) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
return normalizeContent(raw);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* content류 입력을 우선순위에 따라 해석합니다.
|
|
131
|
+
*
|
|
132
|
+
* 우선순위: --file > --content > stdin(파이프)
|
|
133
|
+
* - --file 지정 시: 파일을 읽어 정규화(미존재/읽기 실패는 throw).
|
|
134
|
+
* - --content 지정 시: 그 문자열을 정규화.
|
|
135
|
+
* - 둘 다 없으면 stdin(파이프) 입력을 시도하고, 그것도 없으면 undefined.
|
|
136
|
+
*
|
|
137
|
+
* 어떤 경로든 결과 content는 BOM 제거 + CRLF→LF 정규화되어
|
|
138
|
+
* Windows 인자길이 한계로 인한 무음 절단을 방지합니다.
|
|
139
|
+
*
|
|
140
|
+
* @param label 에러 메시지에 붙일 옵션명(예: '--file')
|
|
141
|
+
* @returns 정규화된 content 문자열, 입력이 없으면 undefined
|
|
142
|
+
*/
|
|
143
|
+
function readContentInput(opts, label = '--file') {
|
|
144
|
+
if (opts.file !== undefined) {
|
|
145
|
+
return readContentFile(opts.file, label);
|
|
146
|
+
}
|
|
147
|
+
if (opts.content !== undefined) {
|
|
148
|
+
return normalizeContent(opts.content);
|
|
149
|
+
}
|
|
150
|
+
const stdin = readContentStdin();
|
|
151
|
+
if (stdin !== null) {
|
|
152
|
+
return stdin;
|
|
153
|
+
}
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
75
156
|
//# sourceMappingURL=utils.js.map
|
package/dist/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGA,kDAEC;
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decencia/ch-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.1",
|
|
4
4
|
"description": "Decencia Communication Channel CLI",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"build": "node scripts/sync-skill-version.js && tsc",
|
|
16
16
|
"dev": "tsc --watch",
|
|
17
17
|
"start": "node dist/index.js",
|
|
18
|
-
"test": "npm run build && node test/
|
|
18
|
+
"test": "npm run build && node test/run-all.js",
|
|
19
19
|
"sync-skill-version": "node scripts/sync-skill-version.js",
|
|
20
20
|
"prepublishOnly": "npm run build"
|
|
21
21
|
},
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: 2t-decencia-channel-change-manager
|
|
3
3
|
description: 기존 소통채널 프로젝트의 기능 추가/수정을 사용자와 대화로 명확화 → 전파 스킬로 연관 PRD/spec/DB/SQA 일괄 갱신 → 변경으로 생긴 새 할일을 GitHub 이슈로 발행하는 오케스트레이터 스킬. (1)대화로 변경 내용 확정 → (2)[게이트] 영향 범위 확인 후 전파 적용 → (3)[게이트] 할일 목록 확인 후 이슈 발행. "이 기능 바꿀래"·"기획 수정"·"이거 추가해줘(기존 프로젝트)" 류 요청 시.
|
|
4
4
|
allowed-tools: Bash(ch:*) Bash(git:*) Bash(gh:*) Read Write
|
|
5
|
-
version: 1.
|
|
5
|
+
version: 1.8.1
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
<!-- ch-version-gate -->
|
|
@@ -8,7 +8,7 @@ description: |
|
|
|
8
8
|
(2) Task에 description/points/completedAt을 기록할 때,
|
|
9
9
|
(3) ch db-tables 명령(list/get/create/update/delete)을 사용할 때,
|
|
10
10
|
(4) Spec ID를 JIRA 스타일(`LOGIN-001`)로 직접 지정할 때 (`ch specs create --id`).
|
|
11
|
-
version: 1.
|
|
11
|
+
version: 1.8.1
|
|
12
12
|
---
|
|
13
13
|
|
|
14
14
|
<!-- ch-version-gate -->
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: 2t-decencia-channel-github-issue
|
|
3
3
|
description: gh CLI로 현재 repo에 GitHub 이슈를 작성한다. 이슈 1개=유형 1개(feat/fix/bug), 표준 본문 양식(작업내용·관련 명세 백링크·완료조건)으로 발행. "이슈 만들어줘"/"깃헙 이슈로 등록해줘" 요청 시, 또는 소통채널 기획·명세 작업 후 처리할 일들을 GitHub 이슈로 옮길 때 사용. 멱등성/중복방지·확인단계는 다루지 않음(요청대로 바로 생성).
|
|
4
4
|
allowed-tools: Bash(gh:*) Bash(git:*) Read Write
|
|
5
|
-
version: 1.
|
|
5
|
+
version: 1.8.1
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
<!-- ch-version-gate -->
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: 2t-decencia-channel-orchestrator
|
|
3
3
|
description: 소통채널 작업의 단일 진입점(디스패처). 사용자 요청을 듣고 7종(bootstrap·terraformer·change-manager·github-issue·issue-coder·pr-merger·sprint-runner) 중 적절한 곳으로 라우팅하고, 필요하면 여러 단계를 순차 오케스트레이션한다. 대화형 스킬은 메인 세션에서 Skill로, 자율 에이전트는 Agent로 위임. "소통채널 작업 해줘"·"이거 어떻게 처리하지?"처럼 무엇부터 할지 모를 때, 또는 신규기획/코드편입/기획변경/이슈/구현/머지/스프린트실행 어디로든 시작할 때.
|
|
4
4
|
allowed-tools: Skill Agent Read Bash(ch:*) Bash(gh:*) Bash(git:*)
|
|
5
|
-
version: 1.
|
|
5
|
+
version: 1.8.1
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
<!-- ch-version-gate -->
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: 2t-decencia-channel-project-bootstrap
|
|
3
3
|
description: 신규 기획을 사용자와 대화하며 step-by-step으로 소통채널 프로젝트로 만드는 오케스트레이터 스킬. (1)대화로 PRD 완성 → 승인 → (2)페이지명세(spec)+DB스키마 → 승인 → (3)SQA 시트. 각 단계 사용자 승인 게이트, 작성 디테일은 기존 2t-decencia-channel-*-v2 스킬을 적극 재사용. "새 기획 만들어줘"·"프로젝트 처음부터 세팅" 류 요청 시.
|
|
4
4
|
allowed-tools: Bash(ch:*) Bash(git:*) Read Write
|
|
5
|
-
version: 1.
|
|
5
|
+
version: 1.8.1
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
<!-- ch-version-gate -->
|
|
@@ -10,7 +10,7 @@ description: |
|
|
|
10
10
|
(2) UI 메타데이터·비즈니스 로직·DB 참조를 함께 다룰 때,
|
|
11
11
|
(3) Task를 JIRA 스타일(points·assignee·description·completedAt)로 잘게 쪼갤 때,
|
|
12
12
|
(4) 신규 spec ID를 `LOGIN-001` 같은 JIRA 스타일로 부여하고 싶을 때 (`ch specs create --id`).
|
|
13
|
-
version: 1.
|
|
13
|
+
version: 1.8.1
|
|
14
14
|
---
|
|
15
15
|
|
|
16
16
|
<!-- ch-version-gate -->
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: 2t-decencia-channel-sprint-runner
|
|
3
3
|
description: 특정 Sprint를 끝까지 실행(run)하는 오케스트레이터 스킬. Sprint의 spec을 시드로 이슈 목록을 분해·발행(github-issue)하고, GitHub milestone에 묶인 이슈를 상태별로 분류해 issue-coder(병렬)·pr-merger(직렬)로 전진시킨 뒤, 사람확인이 필요한 곳에서 멈춘다. 사람이 확인·체크 후 재호출하면 멱등하게 이어간다(반복 루프). "스프린트 돌려줘"·"sprint N 실행"·"이 스프린트 개발 진행해줘" 류 요청 시.
|
|
4
4
|
allowed-tools: Skill Agent Read Bash(ch:*) Bash(gh:*) Bash(git:*)
|
|
5
|
-
version: 1.
|
|
5
|
+
version: 1.8.1
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
<!-- ch-version-gate -->
|
|
@@ -22,116 +22,131 @@ ch check
|
|
|
22
22
|
|
|
23
23
|
# 2t-decencia-channel-sprint-runner — Sprint 실행 오케스트레이터
|
|
24
24
|
|
|
25
|
-
하나의 Sprint를
|
|
26
|
-
이 스킬은 **라우터/오케스트레이터**다 — 실제 발행은 `github-issue` 스킬, 구현은 `issue-coder` 에이전트, 머지는 `pr-merger` 에이전트가 한다. **중복 서술하지 않고 위임만** 한다.
|
|
25
|
+
이 스킬은 하나의 Sprint를 이슈 발행부터 구현, 머지, 사람 확인, 반복까지 끝까지 진행시키는 오케스트레이터다. 직접 코드를 짜거나 머지하지는 않고, 발행은 github-issue 스킬에, 구현은 issue-coder에, 머지는 pr-merger에 맡긴다.
|
|
27
26
|
|
|
28
|
-
핵심
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
## 핵심 성질
|
|
28
|
+
|
|
29
|
+
이 스킬은 자기 상태를 저장하지 않는다. 이슈 open/close, PR, milestone, `sprint-<id>` 브랜치까지 모든 진실은 GitHub에 있고, 스킬은 호출될 때마다 그 상태를 읽어 한 걸음씩 전진시킨다. 그래서 같은 Sprint로 몇 번을 다시 불러도 안전하다. 로컬 worktree는 상태가 아니라 원격을 내려받아 보는 복사본이다.
|
|
30
|
+
|
|
31
|
+
사람 확인이 끼는 대화형이라 메인 세션에서 직접 돌되, 구현·머지 같은 자율 작업은 `Agent`로 위임한다.
|
|
31
32
|
|
|
32
33
|
## 입력
|
|
33
|
-
- **Sprint 식별자**: 소통채널 `sprintId`(권장) 또는 GitHub `milestone` 이름.
|
|
34
|
-
- 실행 cwd = 대상 코드 repo (`.ch-project`로 projectId 확보).
|
|
35
34
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
-
|
|
35
|
+
입력은 Sprint 식별자 하나다. 소통채널 `sprintId`를 권장하며 GitHub `milestone` 이름으로도 된다. 작업 디렉터리는 대상 코드 repo여야 하고, 그 안의 `.ch-project`에서 projectId를 얻는다.
|
|
36
|
+
|
|
37
|
+
## 전제 (시작할 때 확인한다)
|
|
38
|
+
|
|
39
|
+
- `gh auth status`와 `ch auth status`로 인증을 확인한다. 하나라도 로그인이 안 돼 있으면 멈추고 `gh auth login` 또는 `ch auth login`을 안내한다.
|
|
40
|
+
- `.ch-project`에서 projectId를 확보한다.
|
|
41
|
+
- 기본 브랜치 이름은 하드코딩하지 않고 감지한다. `gh repo view --json defaultBranchRef -q .defaultBranchRef.name`으로 구하고, 실패하면 `git symbolic-ref --short refs/remotes/origin/HEAD`에서 떼어 쓰며, 그래도 없으면 `main`을 먼저 그다음 `master`를 쓴다. 그래서 repo가 어느 이름을 쓰든 동작한다.
|
|
42
|
+
- 이 스킬은 사람이 로그인한 로컬 `gh`로 동작하므로 PAT가 필요 없다(PAT는 ch-api 전용).
|
|
43
|
+
|
|
44
|
+
## 공통 규약 (다른 컴포넌트와 공유하므로 깨면 안 된다)
|
|
45
|
+
|
|
46
|
+
아래 약속은 web 대시보드와 issue-coder, pr-merger가 함께 따르는 것이라 임의로 바꾸면 안 된다.
|
|
41
47
|
|
|
42
|
-
## 공통 규약 (다른 컴포넌트와 공유 — 깨지면 안 됨)
|
|
43
48
|
| 규약 | 값 |
|
|
44
49
|
|---|---|
|
|
45
50
|
| Sprint ↔ 이슈 묶음 | **GitHub `milestone`** (sprint 1개 = milestone 1개) |
|
|
46
51
|
| Sprint 통합 브랜치 | **`sprint-<id>`** (원격) — 이 sprint의 모든 이슈 PR이 모이는 base 브랜치. 원격이 정본이고 기본 브랜치로 곧장 머지하지 않는다 |
|
|
47
|
-
| 이슈 ↔ spec | 이슈 본문 `## 관련
|
|
48
|
-
| 사람확인 항목 | **소통채널 DB가
|
|
49
|
-
| 이슈 ↔ spec **status** |
|
|
50
|
-
|
|
51
|
-
## 브랜치와 worktree 운영
|
|
52
|
-
|
|
53
|
-
이 sprint의 모든 이슈는 `sprint-<id>`라는 통합 브랜치 하나에 모은다. 이 브랜치는 원격에 있는 것이 정본이며, 어떤 이슈도 기본 브랜치로 곧장 머지하지 않는다.
|
|
52
|
+
| 이슈 ↔ spec | 이슈 본문 `## 관련 명세`의 specid 백링크 (없어도 된다 = 인프라 등 비-spec 작업) |
|
|
53
|
+
| 사람확인 항목 | **소통채널 DB가 정본** — issue-coder가 `ch github checklist set`으로 등록하고, 사람이 웹 실행 탭에서 체크·메모하며, pr-merger가 `ch github checklist get`으로 close를 판단한다 (GitHub 코멘트가 아니다) |
|
|
54
|
+
| 이슈 ↔ spec **status** | 착수는 issue-coder가 `developing`, PR은 issue-coder가 `testing`, completed는 러너가 롤업한다 (그 spec에 백링크된 이슈가 전부 DONE일 때만) |
|
|
54
55
|
|
|
55
|
-
|
|
56
|
+
## 브랜치와 worktree의 큰 그림
|
|
56
57
|
|
|
57
|
-
|
|
58
|
+
이 Sprint의 모든 이슈는 통합 브랜치 `sprint-<id>` 하나에 모인다. 원격의 이 브랜치가 정본이고, 어떤 이슈도 기본 브랜치로 곧장 머지하지 않는다. 모든 이슈가 모인 뒤에야 사람이 검토하고 한 번에 기본 브랜치로 머지한다. 로컬 worktree는 이 브랜치를 내려받아 테스트·검토하는 복사본이며, 언제든 다시 만들 수 있다.
|
|
58
59
|
|
|
59
60
|
---
|
|
60
61
|
|
|
61
62
|
## 절차
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
아래 단계를 순서대로 진행한다.
|
|
65
|
+
|
|
66
|
+
### 1. 이 Sprint의 할 일 목록을 확정한다
|
|
67
|
+
|
|
68
|
+
`ch sprints get <id>`로 이 Sprint에 묶인 spec 목록을 가져온다. 이건 출발점일 뿐이라 완전하지 않다. spec은 기획이라 인프라 구축 같은 실행 작업이 빠져 있으므로, spec에 없는 실행 작업을 사용자와 짧게 의논해 채운다. 모호하면 추측하지 말고 한 번 물어 확정한다. 결과물은 이 Sprint를 끝낼 완전한 할 일 목록이다.
|
|
69
|
+
|
|
70
|
+
### 2. 통합 브랜치와 통합 worktree를 만든다 (이슈 작업을 시작하기 전에 반드시 한다)
|
|
71
|
+
|
|
72
|
+
이 셋업을 끝내기 전에는 어떤 issue-coder도 띄우지 않는다. 건너뛰면 이슈가 기본 브랜치에서 갈라져 곧장 기본 브랜치로 향하는 PR이 생겨 통합이 깨진다.
|
|
73
|
+
|
|
74
|
+
- **milestone 확보**: `gh api repos/{owner}/{repo}/milestones`로 조회하고, 없으면 Sprint 이름(또는 `sprint-<id>`)으로 만든다.
|
|
75
|
+
- **통합 브랜치 `sprint-<id>` 확보**: 원격에 있으면 그대로 쓰고, 없으면 감지한 기본 브랜치에서 만들어 push한다. `BASE=$(gh repo view --json defaultBranchRef -q .defaultBranchRef.name)`로 기본 브랜치를 구한 뒤 `git fetch origin "$BASE" && git branch sprint-<id> "origin/$BASE" && git push -u origin sprint-<id>`를 실행한다. 이후 모든 이슈 PR의 base가 된다.
|
|
76
|
+
- **통합 worktree 확보**: 없으면 `git worktree add ../<repo>-wt/sprint-<id> sprint-<id>`로 만든다. 이 worktree는 통합 스모크·dev 서버·사람 검토에 쓰며 Sprint가 끝날 때까지 유지한다.
|
|
77
|
+
|
|
78
|
+
### 3. 아직 이슈로 만들지 않은 할 일만 발행한다
|
|
79
|
+
|
|
80
|
+
다시 불러도 이슈를 중복으로 만들면 안 되므로, 발행 전에 `gh issue list --milestone "<m>" --state all`로 기존 이슈를 모으고, 1단계의 각 할 일이 이미 있는지 제목이나 `## 관련 명세`의 specid로 대조한다.
|
|
68
81
|
|
|
69
|
-
|
|
70
|
-
- 이 Sprint의 **milestone 확보**: `gh api repos/{owner}/{repo}/milestones`로 조회, 없으면 생성(이름 = Sprint명/`sprint-<id>`).
|
|
71
|
-
- 이 sprint의 **통합 브랜치 `sprint-<id>`를 확보한다.** 원격에 없으면 감지한 기본 브랜치에서 만들어 push하고, 이미 있으면 그대로 쓴다: `BASE=$(gh repo view --json defaultBranchRef -q .defaultBranchRef.name)` 로 구한 뒤 `git fetch origin "$BASE" && git branch sprint-<id> "origin/$BASE" && git push -u origin sprint-<id>`. 이 브랜치가 이후 모든 이슈 PR의 base가 된다.
|
|
72
|
-
- 이 sprint의 **통합 worktree를 확보한다.** 없으면 `git worktree add ../<repo>-wt/sprint-<id> sprint-<id>`로 만든다. 이 worktree는 통합 스모크와 dev 서버, 사람 검토에 쓰며 sprint가 끝날 때까지 유지한다.
|
|
73
|
-
- **멱등 체크(중복 발행 방지)**: `gh issue list --milestone "<m>" --state all` 로 기존 이슈 수집 → 각 할일이 **이미 이슈로 있는지**(제목 또는 `## 관련 명세` specid로 매칭) 확인.
|
|
74
|
-
- **없는 것만** 발행: `2t-decencia-channel-github-issue` 스킬을 **`Skill`로 호출**(표준 양식: 작업내용·관련명세 백링크·완료조건). 발행 후 **각 이슈에 milestone 지정**(`gh issue edit <n> --milestone "<m>"`).
|
|
75
|
-
- ⚠️ github-issue 스킬은 중복방지를 안 한다 → **무엇을 발행할지(=없는 것만)는 이 스킬이 책임진다.**
|
|
82
|
+
아직 없는 할 일만 `2t-decencia-channel-github-issue` 스킬(`Skill`로 호출)로 발행한다. 표준 양식(작업 내용·관련 명세 백링크·완료 조건)을 따르고, 발행 뒤 `gh issue edit <n> --milestone "<m>"`로 milestone에 묶는다. github-issue 스킬은 중복을 막지 않으므로, 무엇을 발행할지(없는 것만)는 이 스킬이 정한다.
|
|
76
83
|
|
|
77
|
-
###
|
|
78
|
-
|
|
84
|
+
### 4. 각 이슈가 지금 어느 단계인지 분류한다
|
|
85
|
+
|
|
86
|
+
`gh issue list --milestone "<m>" --state all`로 이슈를 모으고, 각 이슈의 연결 PR과 CI를 보고 아래 표로 상태를 판정한다. 이 모드에서 모든 이슈 PR의 base는 기본 브랜치가 아니라 `sprint-<id>`이므로, CI·mergeable·머지 대상이 모두 이 브랜치 기준이다.
|
|
79
87
|
|
|
80
88
|
| 상태 | 판별 | 다음 동작 |
|
|
81
89
|
|---|---|---|
|
|
82
|
-
| `TODO` | open
|
|
83
|
-
| `IN_PR` green | open PR
|
|
84
|
-
| `IN_PR` red/conflict | CI red
|
|
85
|
-
| `AWAIT_HUMAN` | PR
|
|
86
|
-
| `FEEDBACK` | 미체크 항목에
|
|
87
|
-
| `DONE` |
|
|
90
|
+
| `TODO` | open이고 연결된 PR이 없다 | issue-coder에 위임 (병렬) |
|
|
91
|
+
| `IN_PR` green | open PR이 있고 `gh pr checks`가 green이며 충돌이 없다 | pr-merger에 위임 (직렬) |
|
|
92
|
+
| `IN_PR` red/conflict | CI가 red이거나 충돌이 모호하다 | 블로커이므로 사람에게 보고 (머지 금지) |
|
|
93
|
+
| `AWAIT_HUMAN` | PR이 머지됐고 이슈는 open이며 체크리스트에 미체크 항목이 남아 있다 (`ch github checklist get`) | 사람 게이트이므로 멈춘다 |
|
|
94
|
+
| `FEEDBACK` | 미체크 항목에 수정 방향 note가 달렸거나 이슈가 재오픈됐다 | issue-coder에 다시 위임 (병렬, note 반영) |
|
|
95
|
+
| `DONE` | 이슈가 close됐다 | 완료로 센다 |
|
|
96
|
+
|
|
97
|
+
### 5. 코딩과 머지를 진행시킨다
|
|
98
|
+
|
|
99
|
+
이슈를 무조건 다 병렬로 돌리지 말고, 서로 겹치지 않는 것만 병렬로 돌려 머지 충돌을 줄인다. 두 이슈가 겹치는지는 다음으로 판단한다.
|
|
100
|
+
|
|
101
|
+
- 이슈 본문이 다른 이슈를 가리킨다("#345 재사용", "#X 의존").
|
|
102
|
+
- 두 이슈가 같은 spec(specid)을 백링크한다.
|
|
103
|
+
- 두 이슈가 같은 모듈·파일·DB 테이블을 건드린다.
|
|
104
|
+
|
|
105
|
+
겹치지 않는 이슈는 이번 회차에 병렬로 돌린다. 의존이 있으면 선행 이슈만 돌리고 후행은 보류했다가, 선행이 `sprint-<id>`에 머지된 다음 회차에 띄운다. 그러면 후행이 갱신된 브랜치에서 분기해 앞 작업을 재사용하므로 중복과 충돌이 없다. 애매하면 사용자에게 한 번 물어 순서를 정한다. 이 분류는 충돌을 없애는 규칙이 아니라 줄이는 최적화이며, 남는 충돌은 pr-merger가 해결한다.
|
|
106
|
+
|
|
107
|
+
병렬 그룹의 이슈(TODO·FEEDBACK)를 각각 issue-coder에 위임한다. 위임 프롬프트에 base가 `sprint-<id>`임을 반드시 명시하고, 위임 후 PR의 base가 맞는지 확인한다. 여러 개면 한 메시지에 Agent를 여러 개 띄운다.
|
|
108
|
+
|
|
109
|
+
green인 PR은 pr-merger에 하나씩 직렬로 위임한다. 한 번 머지하면 `sprint-<id>`가 바뀌므로 다음 PR을 다시 평가하고, 동시에 머지하지 않는다. issue-coder가 PR을 만들면 다음 회차에 `IN_PR`로 잡혀 머저로 넘어간다.
|
|
88
110
|
|
|
89
|
-
|
|
111
|
+
### 6. 통합 worktree를 최신으로 유지하고, 사람에게 넘기기 전에 스모크 검사를 한 번 돌린다
|
|
90
112
|
|
|
91
|
-
|
|
92
|
-
- **병렬 — `Agent`**: `TODO`와 `FEEDBACK` 상태의 이슈를 각각 `2t-decencia-channel-issue-coder`에 위임한다. 이때 위임 프롬프트에 base 브랜치가 `sprint-<id>`임을 명시해, 이슈가 그 브랜치에서 분기하고 PR도 그 브랜치를 향하게 한다. 여러 개면 한 메시지에 Agent를 여러 개 띄워 동시에 실행한다.
|
|
93
|
-
- **직렬 — `Agent`**: `IN_PR(green)` 상태의 이슈를 `2t-decencia-channel-pr-merger`에 한 번에 하나씩 위임한다. 머지하면 `sprint-<id>`가 바뀌므로 다음 PR의 mergeability를 다시 평가하며, 레이스를 막기 위해 동시에 머지하지 않는다. 머저는 PR을 그 base 브랜치에 모으기만 하고 기본 브랜치로 올리지 않는다.
|
|
94
|
-
- **머지 직후 동기화와 스모크 검사**: pr-merger가 이슈를 `sprint-<id>`에 머지하면, 스킬은 통합 worktree에서 `git fetch origin sprint-<id>` 후 `git reset --hard origin/sprint-<id>`로 원격과 맞춘 뒤 Agent에게 스모크 검사를 맡긴다. 스모크 검사는 빌드, dev 서버 기동, 핵심 경로 한두 개 확인까지만 한다. 검사가 실패하면 진행을 멈추고, 어느 머지가 깨뜨렸는지를 사람에게 보고한다.
|
|
95
|
-
- 코더가 PR을 만들면 다음 호출 때 `IN_PR`로 잡혀 머저로 넘어간다(단계 사이 배리어는 GitHub 상태가 대신함).
|
|
113
|
+
머지할 때마다 통합 worktree를 원격에 맞춰 둔다. 그 worktree에서 `git fetch origin sprint-<id>` 후 `git reset --hard origin/sprint-<id>`로 로컬을 원격에 일치시킨다. 여기선 코드를 짜지 않고 원격을 비추기만 하므로 강제로 맞춰도 안전하다. **사람에게 확인이나 검토를 요청하기 직전에는 이 동기화를 반드시 한 번 더 한다. 그래야 그때까지 머지된 모든 변경이 통합 worktree에 들어가 있고, 사람은 언제나 완전히 통합·동기화된 상태만 본다.**
|
|
96
114
|
|
|
97
|
-
|
|
98
|
-
- milestone 이슈들의 `## 관련 명세` 백링크를 모아 **spec → 이슈 집합** 역인덱스를 만든다.
|
|
99
|
-
- 각 spec에 대해: 백링크한 이슈가 **전부 DONE(close)** 이면 `ch specs set-status <specid> completed`.
|
|
100
|
-
- 하나라도 미완(TODO/IN_PR/AWAIT_HUMAN/FEEDBACK)이면 **올리지 않는다**(부분완료 spec은 testing/developing 유지).
|
|
101
|
-
- 멱등: 이미 completed면 skip. 백링크 이슈가 0개인 spec은 대상 아님.
|
|
115
|
+
스모크 검사는 머지마다 하지 않고, 이번 회차의 코딩·머지를 끝내고 사람에게 넘기기 직전에 한 번만 한다. 이 스킬은 git까지만 쓸 수 있어 빌드·dev 서버 실행은 Agent에 맡긴다. 검사는 빌드가 되는지, dev 서버가 뜨는지, 핵심 경로 한두 개가 도는지까지만 가볍게 보고 전체 기능을 다시 검증하지는 않는다. 실패하면 멈추고 어느 머지가 무엇을 깼는지 사람에게 보고한다.
|
|
102
116
|
|
|
103
|
-
###
|
|
104
|
-
더 자동 전진할 게 없으면 멈추고 **현황을 요약 보고**한다:
|
|
105
|
-
- ✅ **DONE** n개
|
|
106
|
-
- ⏳ **AWAIT_HUMAN** m개 — 이슈 링크 + 미체크 항목(사람확인 체크리스트) 바로 확인할 수 있게
|
|
107
|
-
- ⛔ **블로커** k개 — CI red/모호한 충돌(어느 이슈·PR인지)
|
|
108
|
-
- 안내: **"확인·체크(또는 피드백) 후 다시 이 스킬을 같은 Sprint로 호출하면 이어서 진행합니다."**
|
|
117
|
+
### 7. spec의 status를 롤업한다 (전역 시야가 있는 러너만 할 수 있다)
|
|
109
118
|
|
|
110
|
-
|
|
111
|
-
- 사람이 확인·체크하거나 피드백을 남긴 뒤 **재호출** → 1~5 반복.
|
|
112
|
-
- 이미 끝난 건(DONE, 머지된 PR, 발행된 이슈)은 자연히 **skip**된다(상태 SSOT = GitHub). 새로 생긴 `FEEDBACK`만 다시 흐른다.
|
|
113
|
-
- **개발 단계의 종료 조건**: milestone의 모든 이슈가 `DONE` 상태가 되고(`sprint-<id>`로 머지되고 close됨), 백링크된 spec이 모두 `completed`로 롤업되면 개발 단계가 끝난다. 그다음 §7의 최종 게이트로 넘어간다.
|
|
119
|
+
이슈들의 `## 관련 명세` 백링크를 모아 spec마다 걸린 이슈들을 역으로 정리한다. 어떤 spec에 백링크된 이슈가 전부 DONE(close)이면 `ch specs set-status <specid> completed`로 올린다. 하나라도 안 끝났으면(TODO·IN_PR·AWAIT_HUMAN·FEEDBACK) 올리지 않고 둔다. 이미 completed면 건너뛰고, 백링크된 이슈가 없는 spec은 대상이 아니다.
|
|
114
120
|
|
|
115
|
-
###
|
|
121
|
+
### 8. 더 진행할 것이 없으면 멈추고 보고한다
|
|
116
122
|
|
|
117
|
-
|
|
123
|
+
더 전진시킬 것이 없으면 멈춘다. 보고하기 전에 통합 worktree를 `git fetch origin sprint-<id> && git reset --hard origin/sprint-<id>`로 원격에 다시 맞춰, 사람이 보는 상태가 그때까지 머지된 전체와 일치하게 한다. 그다음 현황을 요약 보고한다. 완료(DONE) 개수, 사람 확인 대기(AWAIT_HUMAN) 이슈와 각각의 링크·미체크 항목, 막힌(블로커) 이슈와 그 이유(어느 이슈·PR인지)를 적는다. 그리고 사용자가 확인·피드백 후 같은 Sprint로 다시 부르면 이어서 진행한다고 안내한다.
|
|
118
124
|
|
|
119
|
-
|
|
125
|
+
### 9. 다시 불리면 이어서 진행한다 (멱등)
|
|
126
|
+
|
|
127
|
+
사용자가 확인·피드백 후 다시 부르면 1~8단계를 되풀이한다. 이미 끝난 일(DONE 이슈·머지된 PR·발행된 이슈)은 GitHub 상태를 다시 읽으면서 자연히 건너뛰고, 새로 생긴 FEEDBACK만 다시 흐른다. milestone의 모든 이슈가 DONE(=`sprint-<id>`로 머지·close)이 되고 백링크 spec이 모두 completed면 개발 단계가 끝난 것이며, 10단계로 넘어간다.
|
|
128
|
+
|
|
129
|
+
### 10. 모든 이슈가 모이면 사람이 검토하고 최종 머지한다 (사람 게이트)
|
|
130
|
+
|
|
131
|
+
모든 이슈가 `sprint-<id>`에 모이면, 먼저 통합 worktree를 origin/sprint-<id>에 동기화해 모든 머지가 반영됐는지 확인한 뒤 dev 서버를 띄워 사용자가 통합 결과를 검토하게 한다. 최종 머지 전에 기본 브랜치를 `sprint-<id>`에 먼저 머지해 충돌을 미리 푼다. 검토 중 새 할 일이 나오면 별도 채널을 만들지 말고 해당 이슈의 체크리스트 note나 milestone 새 이슈로 흘린다.
|
|
132
|
+
|
|
133
|
+
어디로 머지할지는 사용자가 정해 직접 실행한다. 보통 기본 브랜치로 머지하고 이 머지가 곧 자동 배포로 이어지므로, 스킬은 특별한 지시가 없는 한 직접 누르지 않는다. 머지 뒤 `sprint-<id>` 브랜치와 통합 worktree, 남은 이슈 worktree를 정리한다.
|
|
120
134
|
|
|
121
135
|
---
|
|
122
136
|
|
|
123
|
-
##
|
|
124
|
-
- **issue-coder = 병렬**(독립 이슈, worktree 격리).
|
|
125
|
-
- **pr-merger = 직렬**(main 변동 → 다음 PR 재평가, 동시 머지 금지).
|
|
126
|
-
- 한 호출에서 **코딩(병렬)을 먼저 띄우고, 이미 green인 PR을 머지(직렬)** 하는 식으로 섞어도 되지만, **같은 이슈에 코더와 머저를 동시에 걸지 않는다.**
|
|
137
|
+
## 병렬과 직렬 규칙 (다시 확인한다)
|
|
127
138
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
-
|
|
133
|
-
-
|
|
134
|
-
-
|
|
139
|
+
issue-coder는 독립 이슈를 각자 worktree에서 다루므로 병렬로 돌린다. pr-merger는 머지마다 통합 브랜치가 바뀌므로 직렬로 돌리고 동시에 머지하지 않는다. 한 호출에서 코딩(병렬)과 green PR 머지(직렬)를 섞어도 되지만, 같은 이슈에 coder와 merger를 동시에 걸지는 않는다.
|
|
140
|
+
|
|
141
|
+
## 경계와 주의
|
|
142
|
+
|
|
143
|
+
- 이 스킬은 일을 나누고 분류하고 위임하고 보고하는 데까지만 한다. 구현·머지·리뷰의 내용은 각 에이전트가 책임지며, 재SQA나 전면 재리뷰는 하지 않는다.
|
|
144
|
+
- 이슈 close나 기본 브랜치 push는 하지 않는다. issue-coder는 PR까지만 하고, 이슈 close는 pr-merger가 조건부로(사람 확인 항목이 남으면 open 유지) 한다.
|
|
145
|
+
- 막혔을 때 무한히 시도하지 않는다. CI red·모호한 충돌·불분명한 요구 같은 블로커는 스스로 풀려 하지 말고 사람에게 넘긴다.
|
|
146
|
+
- 배포는 기본 브랜치 머지 시 GitHub Actions가 자동으로 한다. 스킬은 수동 배포를 하지 않고, 기본 브랜치로 올리는 최종 머지도 특별한 지시가 없는 한 사용자에게 맡긴다.
|
|
147
|
+
- 매 단계마다 지금 어느 worktree의 어느 브랜치에서 무엇을 하는지 사용자에게 먼저 알린다. 통합 worktree와 여러 이슈 worktree, `sprint-<id>`·이슈·기본 브랜치를 오가므로 위치를 분명히 해야 사용자가 헷갈리지 않는다.
|
|
148
|
+
- 공통 함정도 지킨다. ch CLI 본문은 마크다운으로 쓰고 CRLF를 정제하며, spec 필드는 단수·복수를 함께 쓰고, `--json`이나 `specs get`이 윈도우에서 빈 출력이면 PowerShell `Out-File`로 우회한다(위임 대상이 이미 준수).
|
|
135
149
|
|
|
136
150
|
## 다른 컴포넌트와의 관계
|
|
137
|
-
|
|
151
|
+
|
|
152
|
+
web 대시보드의 "실행" 탭과 ch-api의 github 모듈은 같은 규약(milestone=Sprint, 같은 체크리스트 포맷)을 공유하는 관측·사람 게이트 UI다. 이 스킬이 손발이고 대시보드가 눈이며, 코드 공유 없이 GitHub 상태로만 맞물린다.
|