selfish-pipeline 1.0.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.
@@ -0,0 +1,114 @@
1
+ # /selfish:security — 보안 스캔
2
+
3
+ > 코드베이스의 보안 취약점을 탐지하고 보고한다.
4
+ > OWASP Top 10 기준으로 검사한다. **읽기 전용** — 코드를 수정하지 않는다.
5
+
6
+ ## 인자
7
+
8
+ - `$ARGUMENTS` — (선택) 스캔 범위 (파일/디렉토리 경로, 또는 "full" 전체 스캔)
9
+ - 미지정 시: 현재 브랜치의 변경 파일만 스캔
10
+
11
+ ## 설정 로드
12
+
13
+ 프로젝트 루트의 `CLAUDE.md` 또는 `.claude/CLAUDE.md`에서 다음 설정을 읽어 `config` 변수에 할당:
14
+
15
+ ```
16
+ config.framework = 프로젝트에서 사용하는 프레임워크
17
+ (예: "Next.js", "Nuxt", "SvelteKit", "Express", "NestJS")
18
+ → CLAUDE.md에 명시된 프레임워크 기준. 없으면 "알 수 없음"으로 가정.
19
+ config.auditCmd = 의존성 감사 명령어
20
+ (예: "yarn audit", "npm audit", "pnpm audit")
21
+ → package.json의 packageManager 필드 또는 lockfile 기준으로 추론.
22
+ ```
23
+
24
+ ## 실행 절차
25
+
26
+ ### 1. 스캔 범위 결정
27
+
28
+ - `$ARGUMENTS` = 경로 → 해당 경로만
29
+ - `$ARGUMENTS` = "full" → `src/` 전체
30
+ - 미지정 → `git diff --name-only HEAD` 변경 파일
31
+
32
+ ### 2. Agent Teams (파일 10개 초과 시)
33
+
34
+ 넓은 범위 스캔 시 병렬 에이전트:
35
+ ```
36
+ Task("Security scan: src/features/", subagent_type: general-purpose)
37
+ Task("Security scan: src/shared/api/", subagent_type: general-purpose)
38
+ ```
39
+
40
+ ### 3. 보안 검사 항목
41
+
42
+ #### A. Injection (A03:2021)
43
+ - `dangerouslySetInnerHTML` 사용처
44
+ - 사용자 입력이 직접 DOM/URL/쿼리에 삽입되는 곳
45
+ - `eval()`, `new Function()` 사용
46
+
47
+ #### B. Broken Authentication (A07:2021)
48
+ - 토큰/인증 정보 하드코딩
49
+ - 인증 없이 접근 가능한 API 라우트
50
+ - 세션 관리 취약점
51
+
52
+ #### C. Sensitive Data Exposure (A02:2021)
53
+ - `.env` 값이 클라이언트에 노출 (프레임워크별 클라이언트 노출 변수 (예: {config.framework} 환경의 공개 환경변수) 확인)
54
+ - console.log에 민감 정보 출력
55
+ - 에러 메시지에 내부 정보 노출
56
+
57
+ #### D. Security Misconfiguration (A05:2021)
58
+ - CORS 설정
59
+ - CSP 헤더
60
+ - 불필요한 디버그 모드
61
+
62
+ #### E. XSS (A03:2021)
63
+ - React의 기본 이스케이핑을 우회하는 패턴
64
+ - URL 파라미터를 검증 없이 렌더링
65
+ - iframe/script 동적 삽입
66
+
67
+ #### F. Dependencies (A06:2021)
68
+ - 알려진 취약점 있는 패키지 (의존성 감사 도구 결과)
69
+ - 오래된 의존성
70
+
71
+ ### 4. 결과 출력
72
+
73
+ ```markdown
74
+ ## 보안 스캔 결과
75
+
76
+ ### 요약
77
+ | 심각도 | 개수 |
78
+ |--------|------|
79
+ | 🔴 Critical | {N} |
80
+ | 🟠 High | {N} |
81
+ | 🟡 Medium | {N} |
82
+ | 🔵 Low | {N} |
83
+
84
+ ### 발견사항
85
+
86
+ #### 🔴 SEC-{NNN}: {제목}
87
+ - **카테고리**: {OWASP 코드}
88
+ - **파일**: {경로}:{라인}
89
+ - **설명**: {취약점 상세}
90
+ - **영향**: {악용 시 영향}
91
+ - **완화**: {수정 방법}
92
+
93
+ ### 의존성 감사
94
+ {{config.auditCmd} 결과 요약 — 실행 가능한 경우}
95
+
96
+ ### 권장 조치
97
+ {우선순위 순으로 수정 제안}
98
+ ```
99
+
100
+ ### 5. 최종 출력
101
+
102
+ ```
103
+ 🔒 보안 스캔 완료
104
+ ├─ 범위: {파일 수}개 파일
105
+ ├─ 발견: 🔴 {N} / 🟠 {N} / 🟡 {N} / 🔵 {N}
106
+ └─ 권장: {가장 시급한 조치}
107
+ ```
108
+
109
+ ## 주의사항
110
+
111
+ - **읽기 전용**: 코드를 수정하지 않음. 보안 이슈 보고만 수행.
112
+ - **오탐 최소화**: React의 기본 XSS 방어를 고려. 실제 위험한 패턴만 보고.
113
+ - **민감 정보 주의**: 스캔 결과에 실제 토큰/비밀번호 값을 포함하지 않음.
114
+ - **컨텍스트 고려**: {config.framework} 환경에서의 보안 특수성 반영.
@@ -0,0 +1,123 @@
1
+ # /selfish:spec — 기능 명세서 생성
2
+
3
+ > 자연어 기능 설명을 구조화된 명세서(spec.md)로 변환한다.
4
+ > 외부 스크립트 없이 순수 프롬프트로 동작한다.
5
+
6
+ ## 인자
7
+
8
+ - `$ARGUMENTS` — (필수) 기능 설명 자연어 텍스트
9
+
10
+ ## 설정 로드
11
+
12
+ **반드시** `.claude/selfish.config.md`를 먼저 읽는다. 설정 파일이 없으면 중단.
13
+
14
+ ## 실행 절차
15
+
16
+ ### 1. Feature 디렉토리 설정
17
+
18
+ 1. **현재 브랜치** 확인 → `BRANCH_NAME`
19
+ 2. **Feature 이름** 결정:
20
+ - `$ARGUMENTS`에서 핵심 키워드 2-3개 추출
21
+ - kebab-case로 변환 (예: "사용자 인증 추가" → `user-auth`)
22
+ 3. **디렉토리 생성**: `specs/{feature-name}/`
23
+ 4. 이미 존재하면 사용자에게 확인: "기존 spec을 덮어쓰시겠습니까?"
24
+
25
+ ### 2. 코드베이스 탐색
26
+
27
+ spec 작성 전 현재 프로젝트 구조를 파악한다:
28
+
29
+ 1. `{config.architecture}` 계층별 주요 디렉토리 확인
30
+ 2. 기능 설명과 관련된 기존 코드 탐색 (Grep/Glob)
31
+ 3. 관련 타입 정의, API, 컴포넌트 파악
32
+
33
+ ### 3. Spec 작성
34
+
35
+ `specs/{feature-name}/spec.md`를 생성한다:
36
+
37
+ ```markdown
38
+ # Feature Spec: {기능명}
39
+
40
+ > 생성일: {YYYY-MM-DD}
41
+ > 브랜치: {BRANCH_NAME}
42
+ > 상태: Draft
43
+
44
+ ## 개요
45
+ {기능의 목적과 배경을 2-3문장으로}
46
+
47
+ ## User Stories
48
+
49
+ ### US1: {스토리 제목} [P1]
50
+ **설명**: {사용자 관점의 기능 설명}
51
+ **우선순위 근거**: {왜 이 순서인지}
52
+ **독립 테스트**: {이 스토리만으로 테스트 가능한지}
53
+
54
+ #### 수용 시나리오
55
+ - [ ] Given {전제}, When {행동}, Then {결과}
56
+ - [ ] Given {전제}, When {행동}, Then {결과}
57
+
58
+ ### US2: {스토리 제목} [P2]
59
+ {같은 형식}
60
+
61
+ ## 요구사항
62
+
63
+ ### 기능 요구사항
64
+ - **FR-001**: {요구사항}
65
+ - **FR-002**: {요구사항}
66
+
67
+ ### 비기능 요구사항
68
+ - **NFR-001**: {성능/보안/접근성 등}
69
+
70
+ ### 핵심 엔티티
71
+ | 엔티티 | 설명 | 관련 기존 코드 |
72
+ |--------|------|----------------|
73
+ | {이름} | {설명} | {경로 또는 "신규"} |
74
+
75
+ ## 성공 기준
76
+ - **SC-001**: {측정 가능한 성공 지표}
77
+ - **SC-002**: {측정 가능한 성공 지표}
78
+
79
+ ## Edge Cases
80
+ - {엣지 케이스 1}
81
+ - {엣지 케이스 2}
82
+
83
+ ## 제약사항
84
+ - {기술적/비즈니스 제약}
85
+
86
+ ## [NEEDS CLARIFICATION]
87
+ - {불확실한 항목 — 있으면 기록, 없으면 섹션 제거}
88
+ ```
89
+
90
+ ### 4. Critic Loop (1회)
91
+
92
+ 작성 후 **자기비판 루프**를 1회 수행한다:
93
+
94
+ ```
95
+ === CRITIC PASS 1/1 ===
96
+ [COMPLETENESS] 모든 User Story에 수용 시나리오가 있는가? 누락된 요구사항은?
97
+ [MEASURABILITY] 성공 기준이 주관적이지 않고 측정 가능한가?
98
+ [INDEPENDENCE] 구현 세부사항(코드, 라이브러리명)이 섞이지 않았는가?
99
+ [EDGE_CASES] 최소 2개 이상 식별했는가? 빠진 경계 조건은?
100
+ ```
101
+
102
+ - **FAIL 항목 발견 시**: spec.md 자동 수정 → 수정 사항 사용자에게 고지
103
+ - 예: `⚠ COMPLETENESS: US3에 수용 시나리오 누락. 추가 중...`
104
+ - **ALL PASS**: `✓ Critic 통과` 한 줄 표시
105
+ - FAIL → 수정 → 재검증까지 완료한 후 다음 단계로 진행
106
+
107
+ ### 5. 최종 출력
108
+
109
+ ```
110
+ 📝 Spec 생성 완료
111
+ ├─ specs/{feature-name}/spec.md
112
+ ├─ User Stories: {개수}개
113
+ ├─ 요구사항: FR {개수}개, NFR {개수}개
114
+ ├─ 미해결: {[NEEDS CLARIFICATION] 개수}개
115
+ └─ 다음 단계: /selfish:clarify (미해결 시) 또는 /selfish:plan
116
+ ```
117
+
118
+ ## 주의사항
119
+
120
+ - spec에는 **구현 방법을 쓰지 않는다**. "Zustand로 관리" 같은 표현은 plan.md의 몫.
121
+ - 기존 코드와 관련된 엔티티는 **실제 경로**를 명시.
122
+ - `$ARGUMENTS`가 비어있으면 사용자에게 기능 설명 요청.
123
+ - 한 spec에 너무 많은 기능을 담지 않는다. User Story 5개 초과 시 분리 제안.
@@ -0,0 +1,111 @@
1
+ # /selfish:tasks — 태스크 분해
2
+
3
+ > plan.md를 기반으로 실행 가능한 태스크 목록(tasks.md)을 생성한다.
4
+ > Critic Loop 1회로 커버리지를 검증한다.
5
+
6
+ ## 인자
7
+
8
+ - `$ARGUMENTS` — (선택) 추가 제약 또는 우선순위 지시
9
+
10
+ ## 설정 로드
11
+
12
+ **반드시** `.claude/selfish.config.md`를 먼저 읽는다. 설정 파일이 없으면 중단.
13
+
14
+ ## 실행 절차
15
+
16
+ ### 1. 컨텍스트 로드
17
+
18
+ 1. `specs/{feature}/` 에서 로드:
19
+ - **plan.md** (필수) — 없으면 중단: "/selfish:plan을 먼저 실행하세요."
20
+ - **spec.md** (필수)
21
+ - **research.md** (있으면)
22
+ 2. plan.md에서 추출:
23
+ - Phase 구분
24
+ - File Change Map
25
+ - 아키텍처 결정사항
26
+
27
+ ### 2. 태스크 분해
28
+
29
+ plan.md의 Phase별로 태스크를 분해한다.
30
+
31
+ #### 태스크 형식 (필수)
32
+
33
+ ```markdown
34
+ - [ ] T{NNN} {[P]} {[US*]} {설명} `{파일 경로}`
35
+ ```
36
+
37
+ | 구성요소 | 필수 | 설명 |
38
+ |----------|------|------|
39
+ | `T{NNN}` | O | 3자리 순차 ID (T001, T002, ...) |
40
+ | `[P]` | X | 병렬 실행 가능 (다른 [P] 태스크와 파일 겹침 없음) |
41
+ | `[US*]` | X | User Story 라벨 (spec.md의 US1, US2, ...) |
42
+ | 설명 | O | 명확한 작업 설명 (동사로 시작) |
43
+ | 파일 경로 | O | 주요 작업 대상 파일 (백틱으로 감쌈) |
44
+
45
+ #### Phase 구조
46
+
47
+ ```markdown
48
+ # Tasks: {기능명}
49
+
50
+ ## Phase 1: Setup
51
+ {타입 정의, 설정, 디렉토리 구조}
52
+
53
+ ## Phase 2: Core
54
+ {핵심 비즈니스 로직, store, API}
55
+
56
+ ## Phase 3: UI
57
+ {컴포넌트, 인터랙션}
58
+
59
+ ## Phase 4: Integration & Polish
60
+ {연동, 에러 처리, 최적화}
61
+ ```
62
+
63
+ #### 분해 원칙
64
+
65
+ 1. **1 태스크 = 1 파일** 원칙 (가능한 한)
66
+ 2. **같은 파일 = 순차**, **다른 파일 = [P] 후보**
67
+ 3. **의존성 명시**: 의존하는 태스크가 있으면 설명에 `(after T{NNN})` 추가
68
+ 4. **테스트 태스크**: 테스트 가능한 단위마다 검증 태스크 포함
69
+ 5. **Phase 게이트**: 각 Phase 끝에 `{config.gate}` 검증 태스크 추가
70
+
71
+ ### 3. Critic Loop (1회)
72
+
73
+ | 기준 | 검증 내용 |
74
+ |------|-----------|
75
+ | **COVERAGE** | plan.md의 File Change Map 모든 파일이 태스크에 포함되었는가? spec.md의 모든 FR-*이 커버되는가? |
76
+
77
+ FAIL 시: 누락 항목 추가 후 통과.
78
+
79
+ ### 4. 커버리지 매핑
80
+
81
+ ```markdown
82
+ ## 커버리지 매핑
83
+ | 요구사항 | 태스크 |
84
+ |----------|--------|
85
+ | FR-001 | T003, T007 |
86
+ | FR-002 | T005, T008 |
87
+ | NFR-001 | T012 |
88
+ ```
89
+
90
+ 모든 FR-*/NFR-*가 최소 1개 태스크에 매핑되어야 함.
91
+
92
+ ### 5. 최종 출력
93
+
94
+ `specs/{feature}/tasks.md`에 저장 후:
95
+
96
+ ```
97
+ 📋 태스크 생성 완료
98
+ ├─ specs/{feature}/tasks.md
99
+ ├─ 태스크: {전체 수}개 (병렬 가능: {[P] 수}개)
100
+ ├─ Phase: {Phase 수}개
101
+ ├─ 커버리지: FR {매핑률}%, NFR {매핑률}%
102
+ ├─ Critic: 1회 완료
103
+ └─ 다음 단계: /selfish:analyze (선택) 또는 /selfish:implement
104
+ ```
105
+
106
+ ## 주의사항
107
+
108
+ - **구현 코드를 쓰지 않는다**: 태스크 설명만 작성. 실제 코드는 /selfish:implement의 몫.
109
+ - **과도한 분해 금지**: 한 줄짜리 변경을 별도 태스크로 만들지 않음.
110
+ - **파일 경로 정확성**: 실제 프로젝트 구조에 기반한 경로 사용 (추측 금지).
111
+ - **[P] 마커 신중히**: 진짜 독립적인 태스크만 [P] 표시. 의심스러우면 순차.
@@ -0,0 +1,47 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "matcher": "startup|resume|compact",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "\"${CLAUDE_PLUGIN_ROOT}/scripts/session-start-context.sh\""
10
+ }
11
+ ]
12
+ }
13
+ ],
14
+ "PreCompact": [
15
+ {
16
+ "matcher": "*",
17
+ "hooks": [
18
+ {
19
+ "type": "command",
20
+ "command": "\"${CLAUDE_PLUGIN_ROOT}/scripts/pre-compact-checkpoint.sh\""
21
+ }
22
+ ]
23
+ }
24
+ ],
25
+ "PostToolUse": [
26
+ {
27
+ "matcher": "Edit|Write",
28
+ "hooks": [
29
+ {
30
+ "type": "command",
31
+ "command": "\"${CLAUDE_PLUGIN_ROOT}/scripts/track-selfish-changes.sh\""
32
+ }
33
+ ]
34
+ }
35
+ ],
36
+ "Stop": [
37
+ {
38
+ "hooks": [
39
+ {
40
+ "type": "command",
41
+ "command": "\"${CLAUDE_PLUGIN_ROOT}/scripts/selfish-stop-gate.sh\""
42
+ }
43
+ ]
44
+ }
45
+ ]
46
+ }
47
+ }
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "selfish-pipeline",
3
+ "version": "1.0.0",
4
+ "description": "Claude Code 전용 자동화 파이프라인 플러그인 — spec → plan → tasks → implement → review → clean",
5
+ "bin": {
6
+ "selfish-pipeline": "./bin/cli.mjs"
7
+ },
8
+ "type": "module",
9
+ "files": [
10
+ "bin/",
11
+ "commands/",
12
+ "hooks/",
13
+ "scripts/",
14
+ "templates/",
15
+ ".claude-plugin/",
16
+ "LICENSE",
17
+ "README.md",
18
+ "MIGRATION.md"
19
+ ],
20
+ "author": {
21
+ "name": "jhlee0409",
22
+ "email": "relee6203@gmail.com"
23
+ },
24
+ "license": "MIT",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/jhlee0409/selfish-pipeline"
28
+ },
29
+ "homepage": "https://github.com/jhlee0409/selfish-pipeline#readme",
30
+ "bugs": {
31
+ "url": "https://github.com/jhlee0409/selfish-pipeline/issues"
32
+ },
33
+ "engines": {
34
+ "node": ">=18"
35
+ },
36
+ "keywords": [
37
+ "claude-code",
38
+ "claude-code-plugin",
39
+ "plugin",
40
+ "pipeline",
41
+ "automation",
42
+ "spec",
43
+ "plan",
44
+ "implement",
45
+ "review",
46
+ "critic-loop"
47
+ ]
48
+ }
@@ -0,0 +1,67 @@
1
+ #!/bin/bash
2
+ # Pre-Compact Hook: 컨텍스트 압축 전 자동 체크포인트
3
+ # 세션 중단/압축 시 진행 상태가 소실되는 것을 방지
4
+ #
5
+ # Gap 해결: OMC의 자동 상태 저장 → 물리적 스크립트로 강제
6
+
7
+ PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
8
+ # Auto-memory 디렉토리를 프로젝트 경로에서 동적 파생
9
+ PROJECT_PATH=$(cd "$PROJECT_DIR" 2>/dev/null && pwd || echo "$PROJECT_DIR")
10
+ ENCODED_PATH=$(echo "$PROJECT_PATH" | sed 's|/|-|g')
11
+ MEMORY_DIR="$HOME/.claude/projects/$ENCODED_PATH/memory"
12
+ CHECKPOINT="$MEMORY_DIR/checkpoint.md"
13
+
14
+ # memory 디렉토리 없으면 생성
15
+ mkdir -p "$MEMORY_DIR"
16
+
17
+ # 현재 git 상태 수집
18
+ BRANCH=$(cd "$PROJECT_DIR" 2>/dev/null && git branch --show-current 2>/dev/null || echo "unknown")
19
+ MODIFIED=$(cd "$PROJECT_DIR" 2>/dev/null && git diff --name-only 2>/dev/null | head -20)
20
+ STAGED=$(cd "$PROJECT_DIR" 2>/dev/null && git diff --cached --name-only 2>/dev/null | head -20)
21
+
22
+ # selfish pipeline 활성 상태 확인
23
+ PIPELINE_FLAG="$PROJECT_DIR/.claude/.selfish-active"
24
+ PIPELINE_FEATURE=""
25
+ if [ -f "$PIPELINE_FLAG" ]; then
26
+ PIPELINE_FEATURE=$(cat "$PIPELINE_FLAG")
27
+ fi
28
+
29
+ # tasks.md 진행 상태 확인
30
+ TASKS_DONE=0
31
+ TASKS_TOTAL=0
32
+ if [ -n "$PIPELINE_FEATURE" ] && [ -d "$PROJECT_DIR/specs/$PIPELINE_FEATURE" ]; then
33
+ TASKS_FILE="$PROJECT_DIR/specs/$PIPELINE_FEATURE/tasks.md"
34
+ if [ -f "$TASKS_FILE" ]; then
35
+ TASKS_DONE=$(grep -cE '\[x\]' "$TASKS_FILE" 2>/dev/null || echo 0)
36
+ TASKS_TOTAL=$(grep -cE '\[(x| )\]' "$TASKS_FILE" 2>/dev/null || echo 0)
37
+ fi
38
+ fi
39
+
40
+ # checkpoint.md 작성
41
+ cat > "$CHECKPOINT" << EOF
42
+ # Auto Checkpoint (Pre-Compact)
43
+ > 자동 생성: $(date '+%Y-%m-%d %H:%M:%S')
44
+ > 트리거: context compaction
45
+
46
+ ## Git 상태
47
+ - 브랜치: $BRANCH
48
+ - 수정된 파일: $(echo "$MODIFIED" | wc -l | tr -d ' ')개
49
+ $(echo "$MODIFIED" | sed 's/^/ - /' | head -10)
50
+
51
+ ## Staged 파일
52
+ $(echo "$STAGED" | sed 's/^/ - /' | head -10)
53
+
54
+ ## Pipeline 상태
55
+ - 활성: $([ -f "$PIPELINE_FLAG" ] && echo "Yes ($PIPELINE_FEATURE)" || echo "No")
56
+ - 태스크 진행: $TASKS_DONE/$TASKS_TOTAL
57
+
58
+ ## 복원 명령
59
+ \`\`\`
60
+ /selfish.resume
61
+ \`\`\`
62
+ EOF
63
+
64
+ # stdout으로 context 주입 (Claude가 압축 후 이 정보를 볼 수 있음)
65
+ echo "Auto-checkpoint saved to memory/checkpoint.md (branch: $BRANCH, pipeline: ${PIPELINE_FEATURE:-inactive})"
66
+
67
+ exit 0
@@ -0,0 +1,84 @@
1
+ #!/bin/bash
2
+ # Pipeline Management: selfish 파이프라인 상태 플래그 관리
3
+ # 다른 hook 스크립트들이 참조하는 플래그 파일을 관리
4
+ #
5
+ # 사용법:
6
+ # .claude/hooks/selfish-pipeline-manage.sh start <feature-name>
7
+ # .claude/hooks/selfish-pipeline-manage.sh phase <phase-name>
8
+ # .claude/hooks/selfish-pipeline-manage.sh ci-pass
9
+ # .claude/hooks/selfish-pipeline-manage.sh end
10
+ # .claude/hooks/selfish-pipeline-manage.sh status
11
+
12
+ PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
13
+ FLAG_DIR="$PROJECT_DIR/.claude"
14
+ PIPELINE_FLAG="$FLAG_DIR/.selfish-active"
15
+ PHASE_FLAG="$FLAG_DIR/.selfish-phase"
16
+ CI_FLAG="$FLAG_DIR/.selfish-ci-passed"
17
+ CHANGES_LOG="$FLAG_DIR/.selfish-changes.log"
18
+
19
+ mkdir -p "$FLAG_DIR"
20
+
21
+ case "$1" in
22
+ start)
23
+ FEATURE="${2:?Feature name required}"
24
+ echo "$FEATURE" > "$PIPELINE_FLAG"
25
+ echo "spec" > "$PHASE_FLAG"
26
+ rm -f "$CI_FLAG" "$CHANGES_LOG"
27
+
28
+ # Safety snapshot
29
+ cd "$PROJECT_DIR"
30
+ git tag -f "selfish/pre-auto" 2>/dev/null
31
+
32
+ echo "Pipeline started: $FEATURE (safety tag: selfish/pre-auto)"
33
+ ;;
34
+
35
+ phase)
36
+ PHASE="${2:?Phase name required}"
37
+ case "$PHASE" in
38
+ spec|plan|tasks|implement|review|clean)
39
+ echo "$PHASE" > "$PHASE_FLAG"
40
+ rm -f "$CI_FLAG" # 새 Phase에서는 CI 초기화
41
+ echo "Phase: $PHASE"
42
+ ;;
43
+ *)
44
+ echo "Invalid phase: $PHASE (valid: spec|plan|tasks|implement|review|clean)" >&2
45
+ exit 1
46
+ ;;
47
+ esac
48
+ ;;
49
+
50
+ ci-pass)
51
+ date +%s > "$CI_FLAG"
52
+ echo "CI passed at $(date '+%H:%M:%S')"
53
+ ;;
54
+
55
+ end)
56
+ FEATURE=""
57
+ [ -f "$PIPELINE_FLAG" ] && FEATURE=$(cat "$PIPELINE_FLAG")
58
+ rm -f "$PIPELINE_FLAG" "$PHASE_FLAG" "$CI_FLAG" "$CHANGES_LOG"
59
+
60
+ # Safety tag 정리 (성공 완료 시)
61
+ cd "$PROJECT_DIR"
62
+ git tag -d "selfish/pre-auto" 2>/dev/null
63
+
64
+ echo "Pipeline ended: ${FEATURE:-unknown}"
65
+ ;;
66
+
67
+ status)
68
+ if [ -f "$PIPELINE_FLAG" ]; then
69
+ echo "Active: $(cat "$PIPELINE_FLAG")"
70
+ [ -f "$PHASE_FLAG" ] && echo "Phase: $(cat "$PHASE_FLAG")"
71
+ [ -f "$CI_FLAG" ] && echo "CI: passed ($(cat "$CI_FLAG"))"
72
+ [ -f "$CHANGES_LOG" ] && echo "Changes: $(wc -l < "$CHANGES_LOG") files"
73
+ else
74
+ echo "No active pipeline"
75
+ fi
76
+ ;;
77
+
78
+ *)
79
+ echo "Usage: $0 {start|phase|ci-pass|end|status} [args]" >&2
80
+ exit 1
81
+ ;;
82
+ esac
83
+
84
+ exit 0
@@ -0,0 +1,49 @@
1
+ #!/bin/bash
2
+ # Stop Gate Hook: 파이프라인 활성 중 CI 미통과 시 중단 차단
3
+ # Claude가 CI를 건너뛰고 "완료"라고 말하는 것을 물리적으로 방지
4
+ #
5
+ # Gap 해결: "프롬프트는 강제가 아님" → exit 2로 물리적 차단
6
+
7
+ PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
8
+ PIPELINE_FLAG="$PROJECT_DIR/.claude/.selfish-active"
9
+ CI_FLAG="$PROJECT_DIR/.claude/.selfish-ci-passed"
10
+ PHASE_FLAG="$PROJECT_DIR/.claude/.selfish-phase"
11
+
12
+ # 파이프라인이 활성이 아니면 → 통과
13
+ if [ ! -f "$PIPELINE_FLAG" ]; then
14
+ exit 0
15
+ fi
16
+
17
+ FEATURE=$(cat "$PIPELINE_FLAG")
18
+
19
+ # Phase 파일이 있으면 현재 Phase 확인
20
+ CURRENT_PHASE=""
21
+ if [ -f "$PHASE_FLAG" ]; then
22
+ CURRENT_PHASE=$(cat "$PHASE_FLAG")
23
+ fi
24
+
25
+ # Spec/Plan/Tasks Phase (1-3)는 CI 불필요 → 통과
26
+ case "$CURRENT_PHASE" in
27
+ spec|plan|tasks)
28
+ exit 0
29
+ ;;
30
+ esac
31
+
32
+ # Implement/Review/Clean Phase (4-6)에서는 CI 필수
33
+ if [ ! -f "$CI_FLAG" ]; then
34
+ echo "SELFISH GATE: yarn ci가 실행되지 않았습니다. 파이프라인 '$FEATURE' Phase '$CURRENT_PHASE'에서 CI 게이트를 통과해야 합니다. yarn ci를 실행한 후 .claude/.selfish-ci-passed에 timestamp를 기록하세요." >&2
35
+ exit 2
36
+ fi
37
+
38
+ # CI가 10분 이내에 통과했는지 확인 (stale 방지)
39
+ CI_TIME=$(cat "$CI_FLAG")
40
+ NOW=$(date +%s)
41
+ if [ -n "$CI_TIME" ] && [ "$CI_TIME" -gt 0 ] 2>/dev/null; then
42
+ DIFF=$((NOW - CI_TIME))
43
+ if [ "$DIFF" -gt 600 ]; then
44
+ echo "SELFISH GATE: CI 결과가 오래되었습니다 (${DIFF}초 전). yarn ci를 다시 실행하세요." >&2
45
+ exit 2
46
+ fi
47
+ fi
48
+
49
+ exit 0