Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/workflows/build-macos.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Compiles the macOS app on push/PR (no signing). Artifact: kkamppak.app.zip

name: Build (macOS)

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
build:
runs-on: macos-14
steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Build kkamppak.app (unsigned)
run: |
set -euo pipefail
PROJ="apps/kkamppak/kkamppak.xcodeproj"
xcodebuild -project "$PROJ" -scheme kkamppak -configuration Release -destination 'platform=macOS' CODE_SIGNING_ALLOWED=NO build
APP=$(find ~/Library/Developer/Xcode/DerivedData -path '*/Build/Products/Release/kkamppak.app' -print -quit)
test -n "$APP" -a -d "$APP"
echo "BUILT_APP=$APP" >> "$GITHUB_ENV"
(cd "$(dirname "$APP")" && zip -r "$GITHUB_WORKSPACE/kkamppak.app.zip" kkamppak.app)

- name: Upload kkamppak.app.zip
uses: actions/upload-artifact@v4
with:
name: kkamppak-app-release-unsigned
path: kkamppak.app.zip
27 changes: 20 additions & 7 deletions PLANS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,32 @@

### Release artifacts

- **Outcome:** GitHub Releases에 `.app` zip 또는 빌드 산출물 업로드 흐름 문서화
- **Why this is accepted:** README의 “Releases에서 받기”와 실제 배포를 맞추기 위함
- **Preconditions:** 서명·아카이브 방침 결정
- **Outcome:** GitHub Releases에 `.app` zip 업로드 (또는 CI 아티팩트를 릴리스에 붙이기). README **방법 B**와 맞출 것.
- **Why this is accepted:** 사용자가 소스 빌드 없이 받을 수 있게.
- **Preconditions:** 태그·릴리스 노트; (선택) Developer ID 서명 시 GitHub secret 연동.
- **Related ids:** —

### 문서·검증

- **Outcome:** 스모크 테스트·제거 절차·Raycast 예시가 README·`docs/`에 정리됨.
- **Status:** 진행 중 (`docs/smoke-test.md`, `docs/uninstall.md`).

### CI 빌드

- **Outcome:** PR·`main` 푸시 시 macOS runner에서 **서명 없이** `xcodebuild`로 컴파일 검증, `kkamppak.app.zip` 아티팩트.
- **Workflow:** `.github/workflows/build-macos.yml`

## Sequencing

### Near term

- **Initiative:** AppIcon 에셋 채우기
- **Why now:** Dock·응용 프로그램 폴더에서 식별성 향상
- **Initiative:** 첫 GitHub Release 게시 (unsigned zip이라도) + README와 동기화
- **Initiative:** (선택) Release 워크플로로 태그 시 자동 zip 첨부

### Mid term

- **Initiative:** 선택적 GitHub Actions로 Xcode 빌드 (macOS runner)
- **Why later:** 서명·시크릿 정리 후
- **Initiative:** Developer ID 서명 + (필요 시) 공증은 제품 정책 바뀔 때만 검토 — 현재 SPEC은 Notarization 비목표

### 보류 (운영자 전용)

- **Initiative:** AppIcon 에셋 채우기 — Dock 식별용; 별도 요청 시 진행
17 changes: 13 additions & 4 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,45 @@ Repo operating model: [REPO.md](REPO.md). Product summary: [SPEC.md](SPEC.md).

## Install

1. Grab the latest build from **[Releases](https://github.com/devuterian/kkamppak/releases)** (or **build from source** below).
2. Copy **kkamppak.app** into **Applications**.
**Option A — build from source (always available)**

1. Build `kkamppak.app` in Xcode as described in [`docs/build.md`](docs/build.md).
2. Copy it into **Applications** and run it.
3. On **first launch**, enter your admin password once (installs helper script, LaunchDaemon, and a narrow `sudoers` rule).
4. After that, pick a mode without typing your password again.

**Option B — release binary (when published)**

If **[Releases](https://github.com/devuterian/kkamppak/releases)** has a zip or other artifact, download it and install the same way. If nothing is there yet, use **Option A**.

**If macOS blocks the app:** unsigned or ad-hoc builds may need **Right-click → Open** once, or approval under **System Settings → Privacy & Security**. This project assumes **no Notarization** is required by design.

---

## Usage

1. Run the app from Dock, Raycast, or a script.
1. Run the app from Dock, Raycast, or a script. (Raycast **script command** example: `open -a kkamppak`)
2. The alert shows the **current mode** (from `pmset -g custom`).
3. Choose **기본 설정으로** (default) or **Remote Ready로**. The button matching the current mode is disabled.
4. A **result** alert appears; **OK** quits the app.

**Remote Ready** sets `sleep` / `displaysleep` / `disksleep` to **0**. **Default** uses a **1 / 10 / 10** (minutes) preset. If AC and Battery blocks differ, the UI shows **custom**.
**Remote Ready** sets `sleep` / `displaysleep` / `disksleep` to **0**. **Default** uses the app’s **1 / 10 / 10** (minutes) preset (this may not match every Mac’s factory defaults). If AC and Battery blocks differ, the UI shows **custom**.

---

## Limitations

- Does not **guarantee** battery life, thermals, or security posture. Remote Ready trades power savings for staying awake.
- The passwordless path is limited to the **installed apply script**; if that path changes, you may need to re-run the installer flow.
- The installed **LaunchDaemon** (`com.kkamppak.helper`) is a placeholder (`RunAtLoad` false, no real job). Removal steps: [`docs/uninstall.md`](docs/uninstall.md).

---

## Source & contributing

- Build: [`docs/build.md`](docs/build.md)
- Smoke test: [`docs/smoke-test.md`](docs/smoke-test.md)
- Uninstall (helper & sudoers): [`docs/uninstall.md`](docs/uninstall.md)
- Commit hooks & trailers: [`CONTRIBUTING.md`](CONTRIBUTING.md)
- Behavior summary: [`SPEC.md`](SPEC.md)

Expand Down
17 changes: 13 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,45 @@

## 설치하기

1. **[Releases에서 최신 아티팩트](https://github.com/devuterian/kkamppak/releases)** 를 받아요. (또는 아래 **소스에서 빌드**)
2. **kkamppak.app**을 **응용 프로그램** 폴더에 넣어요.
**방법 A — 소스에서 빌드 (항상 가능)**

1. [`docs/build.md`](docs/build.md)대로 Xcode로 `kkamppak.app`을 만듭니다.
2. **응용 프로그램** 폴더에 넣고 실행합니다.
3. **첫 실행**에서 관리자 암호를 한 번 입력해요. (도구 스크립트·LaunchDaemon·`sudoers` 한 줄이 깔려요.)
4. 그다음부터는 암호 없이 모드만 고르면 돼요.

**방법 B — 릴리즈 바이너리 (있을 때)**

[Releases](https://github.com/devuterian/kkamppak/releases)에 올라온 **zip 등**이 있으면 받아서 같은 방식으로 Applications에 넣고 실행하면 됩니다. 아직 없으면 **방법 A**를 쓰면 돼요.

**보안 경고가 뜨면:** 직접 빌드하거나 서명이 없으면 **우클릭 → 열기**로 한 번 실행하거나, **시스템 설정 → 개인 정보 보호 및 보안**에서 허용해 주면 돼요. 이 프로젝트는 기획상 **공증(Notarization) 없이** 배포해도 된다고 가정해요.

---

## 쓰는 법

1. Dock·Raycast 등에서 앱을 실행해요.
1. Dock·Raycast 등에서 앱을 실행해요. (Raycast **스크립트 커맨드** 예: `open -a kkamppak`)
2. **현재 모드**가 안내문에 나와요. (`pmset -g custom` 기준)
3. **기본 설정으로** / **Remote Ready로** 중 하나를 골라요. 지금 모드와 같은 버튼은 비활성화돼요.
4. 적용이 끝나면 **결과 알림**이 뜨고, **OK** 누르면 앱이 꺼져요.

**Remote Ready**는 `sleep` / `displaysleep` / `disksleep`을 0으로 맞춰요. **기본**은 1 / 10 / 10(분) 프리셋이에요. AC와 배터리 설정이 서로 다르면 **사용자 지정**으로 보여요.
**Remote Ready**는 `sleep` / `displaysleep` / `disksleep`을 0으로 맞춰요. **기본**은 앱이 정한 **1 / 10 / 10(분)** 프리셋이에요(맥 기본값과 항상 같지는 않을 수 있어요). AC와 배터리 설정이 서로 다르면 **사용자 지정**으로 보여요.

---

## 이런 건 못해요

- **배터리 수명·발열·보안**까지 대신 판단해 주진 않아요. Remote Ready는 전원을 덜 아끼는 쪽이에요.
- `sudoers`에 **적용 스크립트 한 줄**만 열어 두므로, 그 파일이 바뀌면 다시 설치 흐름이 필요할 수 있어요.
- 설치 시 등록되는 **LaunchDaemon**(`com.kkamppak.helper`)은 기획상 자리만 잡아 두는 형태예요(`RunAtLoad` false, 실제 작업 없음). 지우는 법은 [`docs/uninstall.md`](docs/uninstall.md).

---

## 소스 코드·기여

- 빌드: [`docs/build.md`](docs/build.md)
- 스모크 테스트: [`docs/smoke-test.md`](docs/smoke-test.md)
- 제거(헬퍼·sudoers): [`docs/uninstall.md`](docs/uninstall.md)
- 커밋 메시지·훅: [`CONTRIBUTING.md`](CONTRIBUTING.md)
- 동작 요약: [`SPEC.md`](SPEC.md)

Expand Down
11 changes: 10 additions & 1 deletion SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
- **Name:** kkamppak
- **Canonical repo:** https://github.com/devuterian/kkamppak
- **Project id:** `kkamppak`
- **Last updated:** 2026-04-09
- **Last updated:** 2026-04-10

## Thesis

Expand All @@ -17,6 +17,15 @@ macOS에서 전원 모드를 **대화상자(NSAlert)만**으로 바꾸는 작은
- `pmset -g custom` 파싱으로 현재 모드 표시 (AC·배터리 블록이 같을 때만 프리셋 라벨)
- 최초 실행 시 관리자 권한으로 헬퍼 설치: 적용 스크립트, LaunchDaemon 등록, 해당 스크립트만 NOPASSWD `sudoers`

## Power presets

- **Remote Ready:** `pmset -a`로 `sleep`, `displaysleep`, `disksleep`을 **0**(분)으로 설정.
- **기본(앱 프리셋):** 위 세 값을 **1 / 10 / 10**(분)으로 설정. Apple 기본 전원 프로파일과 동일하다고 가정하지 않음; UI·문서에서 “앱이 정한 기본”임을 구분.

## LaunchDaemon (설치 시)

- `com.kkamppak.helper`: 기획 요건(등록) 충족용. 현재 plist는 `RunAtLoad` false이며 실제 주기 작업 없음. 제거 절차는 `docs/uninstall.md`.

## Invariants

- UI는 AppKit `NSAlert` 중심 (별도 설정 창 없음)
Expand Down
18 changes: 10 additions & 8 deletions STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,31 @@

## Snapshot

- **Last updated:** 2026-04-09
- **Last updated:** 2026-04-10
- **Overall posture:** `active`
- **Current focus:** macOS AppKit 앱 + repo-template 레이아웃 정리
- **Highest-priority blocker:** 없음 (CI·로컬 훅은 커밋 trailer 정책 준수 필요)
- **Current focus:** 문서·CI 빌드로 설치 경로 정리; 릴리스는 운영자가 GitHub에서 게시하면 README 방법 B와 연결
- **Highest-priority blocker:** 없음 (커밋은 provenance trailer 또는 명시적 migration/bootstrap 예외)
- **Related decisions:** —

## Current state summary

Xcode 프로젝트는 `apps/kkamppak/kkamppak.xcodeproj`에 있으며, Swift 앱이 `pmset` 기반 스크립트로 전원 프리셋을 적용한다. 저장소 루트는 [repo-template](https://github.com/LPFchan/repo-template) 스타일의 SPEC/STATUS/PLANS/INBOX·skills·records·커밋 검사를 포함한다.
Xcode 프로젝트는 `apps/kkamppak/kkamppak.xcodeproj`에 있으며, Swift 앱이 `pmset` 기반 스크립트로 전원 프리셋을 적용한다. 저장소 루트는 SPEC/STATUS/PLANS/INBOX·skills·records·커밋 검사를 포함한다. PR·`main`에 대해 **서명 없는** macOS 빌드 워크플로가 `.github/workflows/build-macos.yml`에 있다.

## Active phases or tracks

### App delivery

- **Goal:** 직접 빌드·배포 가능한 `.app` 유지
- **Goal:** 빌드 검증(CI) + 문서로 설치·제거·스모크 경로 명확화
- **Status:** `in progress`
- **Exit criteria:** README의 설치·사용 흐름과 코드 동작 일치
- **Exit criteria:** README와 `docs/`가 “소스 빌드 / 릴리스(있을 때)”를 일관되게 설명
- **Related ids:** —

## Recent changes to project reality

- **2026-04-09:** 앱 소스를 `apps/kkamppak/` 아래로 이동, repo-template 스캐폴드 및 커밋 표준 도입
- **2026-04-09:** 앱 소스 `apps/kkamppak/`, repo 운영 문서·커밋 표준 도입
- **2026-04-10:** `docs/smoke-test.md`, `docs/uninstall.md`, README 정리, SPEC에 프리셋·LaunchDaemon 설명, macOS CI 빌드 워크플로

## Immediate next steps

- Releases 아티팩트 또는 빌드 파이프라인은 운영자가 필요 시 추가
- 운영자: [Releases](https://github.com/devuterian/kkamppak/releases)에 zip 첫 업로드 시 README **방법 B** 문구와 실제 링크 맞추기
- 로컬 개발: `./scripts/install-hooks.sh`로 커밋 훅 켜 두기 (`CONTRIBUTING.md`)
4 changes: 1 addition & 3 deletions apps/kkamppak/kkamppak/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
return .custom
}

private enum BlockMode { case `default`, remoteReady, custom }

private func classifyPmsetBlock(_ block: String) -> BlockMode {
private func classifyPmsetBlock(_ block: String) -> PowerMode {
let sleepM = parsePmsetInt(key: "sleep", in: block)
let displayM = parsePmsetInt(key: "displaysleep", in: block)
let diskM = parsePmsetInt(key: "disksleep", in: block)
Expand Down
4 changes: 3 additions & 1 deletion docs/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@
2. 스킴 **kkamppak**을 선택합니다.
3. **Product → Build** (⌘B) 또는 **Archive**로 `.app`을 만듭니다.

로컬에서 zip으로 묶을 때는 Release 산출물 폴더에서 예를 들어 `zip -r kkamppak.app.zip kkamppak.app` 을 실행하면 됩니다.

## 산출물

- 빌드 결과: Xcode가 지정한 DerivedData 경로의 `kkamppak.app`
- 앱 번들 **Resources**에 `install.sh`, `kkamppak-apply.sh`, `com.kkamppak.helper.plist`가 포함됩니다.

## 참고

- 아이콘: `Assets.xcassets`의 AppIcon에 실제 PNG를 넣으면 Dock에 표시됩니다.
- 공증(Notarization)은 이 프로젝트 범위 밖으로 두었습니다. 배포 시 Gatekeeper 안내는 루트 [README.md](../README.md)를 참고하세요.
- 동작 확인: [`smoke-test.md`](smoke-test.md)
33 changes: 33 additions & 0 deletions docs/smoke-test.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# 스모크 테스트 (kkamppak)

실제 Mac에서 **한 번씩** 확인할 때 쓰는 짧은 체크리스트예요.

## 준비

- [ ] Xcode로 `apps/kkamppak/kkamppak.xcodeproj` 빌드하거나, 배포용 `.app` 준비
- [ ] 테스트 맥에서 로그인 세션(GUI) 사용

## 첫 실행·설치

- [ ] 앱 실행 → **설치** 선택 → 관리자 암호 입력 → 오류 없이 끝남
- [ ] `/usr/local/lib/kkamppak/kkamppak-apply.sh` 존재
- [ ] `/Library/LaunchDaemons/com.kkamppak.helper.plist` 존재
- [ ] `/private/etc/sudoers.d/kkamppak` 존재 (해당 사용자·스크립트 경로만 NOPASSWD)

## 모드 전환

- [ ] 알림에 **현재 모드** 표시됨 (`pmset -g custom`과 대략 일치)
- [ ] 현재 모드와 같은 버튼 비활성화
- [ ] 다른 모드 선택 → **결과 알림** → OK 시 앱 종료
- [ ] 터미널에서 `pmset -g custom`으로 `sleep` / `displaysleep` / `disksleep` 값이 기대와 맞는지 확인
- Remote Ready: AC·배터리 블록 모두 해당 키가 `0` 근처
- 기본 프리셋: 앱이 쓰는 **1 / 10 / 10(분)** ([SPEC.md](../SPEC.md) 참고)

## 재실행

- [ ] 두 번째 실행부터는 설치 단계 없이 바로 모드 선택 알림만 뜸
- [ ] `sudo` 암호 없이 적용되는지 확인 (설치된 스크립트 경로가 그대로일 때)

## 제거

- [ ] [`uninstall.md`](uninstall.md) 순서대로 지운 뒤 헬퍼·sudoers가 사라졌는지 확인
33 changes: 33 additions & 0 deletions docs/uninstall.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# 제거하기 (kkamppak 헬퍼)

앱을 **응용 프로그램에서 지우는 것만**으로는 시스템에 깔린 헬퍼가 남을 수 있어요. 완전히 지울 때는 아래를 **터미널**에서 실행하세요. (관리자 암호 필요)

## 1. LaunchDaemon 내리기·plist 삭제

```bash
sudo launchctl bootout system /Library/LaunchDaemons/com.kkamppak.helper.plist 2>/dev/null || true
sudo rm -f /Library/LaunchDaemons/com.kkamppak.helper.plist
```

## 2. 설치 스크립트 디렉터리 삭제

```bash
sudo rm -rf /usr/local/lib/kkamppak
```

## 3. sudoers 항목 삭제

```bash
sudo rm -f /private/etc/sudoers.d/kkamppak
```

## 4. 앱 번들

**응용 프로그램**에서 `kkamppak.app`을 휴지통으로 내면 됩니다.

## 확인

```bash
test ! -f /usr/local/lib/kkamppak/kkamppak-apply.sh && echo "apply script gone"
test ! -f /private/etc/sudoers.d/kkamppak && echo "sudoers drop-in gone"
```
7 changes: 7 additions & 0 deletions records/agent-worklogs/LOG-20260409-001-repo-remodel.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,10 @@ Recorded by agent: cursor-background
- 운영자 지시문 원문 보관: `docs/operator-prompt-repo-template-adoption.md`
- `recreate-prompt.md` 추가, 루트 `skills/`를 업스트림 스캐폴드와 동기화, `REPO.md`·`AGENTS.md`·`CONTRIBUTING.md`에 안내 링크.
- 중복이던 로컬 `scaffold/` 제거 — 복사용 원본은 [LPFchan/repo-template](https://github.com/LPFchan/repo-template/tree/main/scaffold)만 유지.

## Follow-up 2026-04-10

- `docs/smoke-test.md`, `docs/uninstall.md` 추가; README·README.en 설치 흐름(소스 우선 / 릴리스 선택)·Raycast 예시·LaunchDaemon 설명.
- `SPEC.md`에 전원 프리셋·LaunchDaemon 역할 명시.
- `PLANS.md`·`STATUS.md` 갱신(AppIcon은 보류 섹션).
- `.github/workflows/build-macos.yml` — PR·main용 서명 없는 Xcode 빌드 + zip 아티팩트.
Loading