# pi-patty-bg-tasks

<p align="center">
  <a href="README.md">English</a> · <strong>한국어</strong> · <a href="README.zh.md">中文</a>
</p>

<p align="center">
  <strong>긴 명령에 에이전트가 묶일 이유는 없습니다 — 알아서 백그라운드로 넘기고, 멈춤 없이 계속 달리세요.</strong>
</p>

<p align="center">
  <a href="https://www.npmjs.com/package/pi-patty-bg-tasks"><img alt="npm" src="https://img.shields.io/npm/v/pi-patty-bg-tasks?color=cb3837&label=npm&logo=npm"></a>&nbsp;
  <img alt="Pi v0.37+" src="https://img.shields.io/badge/Pi-v0.37%2B-5b50f0">&nbsp;
  <img alt="dependencies: zero" src="https://img.shields.io/badge/dependencies-zero-3fb950">&nbsp;
  <img alt="tmux: not required" src="https://img.shields.io/badge/tmux-not_required-3fb950">&nbsp;
  <img alt="license: MIT" src="https://img.shields.io/badge/license-MIT-blue">
</p>

**빌드 도는 동안 에이전트가 멍하니 기다리고 있을 이유는 없습니다.** Claude Code의 백그라운드 작업 경험을 그대로 Pi에 옮겨왔습니다. 오래 걸리는 명령을 던지면 세션 전체를 붙잡고 늘어지는 대신, 슬그머니 백그라운드로 빠지고 에이전트는 하던 일을 계속합니다. 120초가 지나면 알아서 백그라운드로, 급하면 Ctrl+Shift+B로 그 자리에서, 출력 캡처에 정체 감지에 제대로 된 작업 관리자까지 — 익스텐션 하나에 다 담았습니다.

## 설치

```
pi install npm:pi-patty-bg-tasks
```

GitHub에서 바로 받아도 됩니다.

```
pi install git:github.com/patty-io/pi-patty-bg-tasks
```

필요한 건 Pi v0.37 이상, 그게 전부입니다. **외부 의존성도 없고 tmux도 안 씁니다.** 백그라운드 잡은 그냥 평범한 Node.js 자식 프로세스로 돌아가고, 출력은 곧장 파일 디스크립터로 흘려보냅니다. 따로 설치할 것도, 신경 써서 돌볼 것도 없습니다.

## 이게 왜 필요하냐면

**이제 세션이 멈춰 서는 일은 없습니다.** 개발 서버, 테스트 스위트, 빌드 — 120초 넘게 붙잡고 있는 녀석은 뭐든 조용히 백그라운드로 넘어갑니다. 에이전트는 그 사실만 슬쩍 전달받고, 스피너만 쳐다보는 대신 곧장 다음 일로 넘어갑니다. 더 빨리 치우고 싶으면? 아무 명령이나, 아무 때나 직접 백그라운드로 보내면 됩니다.

**Claude Code처럼 느껴지는 이유는, 실제로 Claude Code를 본떠 만들었기 때문입니다.** 백그라운드와 포그라운드를 오가는 그 흐름 전체를 — Ctrl+Shift+B로 백그라운드, 출력 캡처, 완료 알림, 정체 감지까지 — Claude Code 구현 위에 그대로 얹었습니다. 메시지 형식도 같고, 터미널에 그대로 녹아드는 아이콘도 같고, "에이전트는 절대 멈추지 않는다"는 그 감각도 똑같습니다. 손에 익은 그 동작, 여기 그대로 있습니다.

**구색만 갖춘 게 아니라, 진짜 작업 관리자입니다.** `/bg-list`를 치면 인터랙티브 작업 관리자가 열립니다. 잡 목록을 훑고, 출력을 들여다보고, 폭주하는 녀석은 붙잡아 죽이고, 필요하면 attach해서 결과가 나올 때까지 기다릴 수 있습니다.

## 빠르게 시작하기

```
# 에이전트가 긴 명령을 실행 — 120초 뒤 자동으로 백그라운드
bash({ command: "npm run build" })

# 기다릴 것 없이 처음부터 백그라운드로
bash({ command: "npm run dev", run_in_background: true })

# 아예 던져두고 신경 끄고 싶을 땐
bash_bg({ command: "npm run dev", name: "devserver" })

# 지금 뭐가 돌고 있는지 확인
jobs({ action: "list" })

# 모든 잡의 출력을 한 번에 grep
jobs({ action: "search", pattern: "error|warning" })

# 작업 하나를 통째로 백그라운드 에이전트에게 넘기기
agent_bg({ prompt: "auth 모듈 리팩터링" })
```

명령이 돌고 있을 때 **Ctrl+Shift+B**를 누르면 그 자리에서 바로 백그라운드로 넘어갑니다 — 명령이 몇 초 이상 돌면 입력창 아래에 `(ctrl+shift+b to run in background)` 힌트가 흐릿하게 떠줍니다. 에이전트는 알림을 받고, 손가락을 키에서 떼기도 전에 이미 다음 일을 하고 있습니다.

## 도구

### bash (오버라이드)

내장 bash 도구에 생존 본능을 심었습니다. 명령은 평소대로 실행되지만, 120초를 넘기는 순간 조용히 백그라운드로 미끄러져 들어갑니다. 결정 요청도, 강제 턴 지시도 없습니다 — 도구 결과 자체(`Command running in background with ID: …`)가 출력이 어디로 가는지 에이전트에게 알려줍니다.

| 파라미터 | 설명 |
|---------|------|
| `command` | 실행할 셸 명령 |
| `timeout` | 타임아웃 직접 지정 (초 단위, 기본값: 120) |
| `run_in_background` | 포그라운드 실행과 자동 백그라운드 타이머를 건너뛰고 명령을 곧장 백그라운드로 시작 |

### bash_bg

처음부터 오래 걸릴 게 뻔할 때 씁니다. 포그라운드에서 눈치 보는 시간도, 타임아웃을 기다리는 시간도 없이 명령을 곧바로 백그라운드로 띄웁니다.

| 파라미터 | 설명 |
|---------|------|
| `command` | 실행할 셸 명령 |
| `name` | 잡에 붙일 사람이 알아보기 쉬운 라벨 (선택) |
| `timeout` | 타임아웃 (초, 선택); 넘으면 자동 백그라운드가 불가능한 명령(예: `sleep`)만 종료하고, 나머지는 계속 실행됩니다 |
| `notify` | 완료 알림 전송 (기본값: true) |

### jobs

백그라운드에서 돌아가는 모든 것을 위한 관제탑입니다. 목록 보기, 출력 읽기, 종료, attach, 검색, 정리, 통계까지 한곳에서.

| 액션 | 설명 |
|------|------|
| `list` | 실행 중인 잡과 최근 끝난 잡을 모두 표시 |
| `output` | 특정 잡의 로그 끝부분 읽기 |
| `kill` | 실행 중인 잡 종료 |
| `attach` | 잡이 끝날 때까지 기다렸다가 출력 반환 |
| `search` | 모든 잡 로그를 정규식으로 검색 |
| `cleanup` | 완료/실패한 잡을 비우고 디스크 회수 |
| `stats` | 집계 지표: 총 시작 수, 실행 중, 완료, 실패, 평균 소요 시간 |

### agent_bg

나 대신 일할 동료를 하나 복제한다고 보면 됩니다. 현재 세션에서 뽑아낸 연속성 프롬프트를 들고 분리된 `pi -p` 프로세스를 띄운 다음, 그 진행 상황을 실시간으로 흘려보내 줍니다.

| 파라미터 | 설명 |
|---------|------|
| `prompt` | 백그라운드 에이전트에게 맡길 작업 설명 |
| `cwd` | 작업 디렉터리 (기본값: 현재) |

### monitor

한 번 기다리는 대신 이벤트를 스트리밍합니다. `bash_bg`/`run_in_background`가 완료 시 **한 번** 알려준다면, `monitor`는 프로세스를 **실시간 이벤트 스트림**으로 바꿉니다 — stdout 한 줄(또는 WebSocket 프레임)마다 알림 하나가 에이전트의 턴으로 바로 전달되고, 에이전트는 그동안 계속 일합니다. 이것이 Claude Code가 둘로 나눈 스트리밍 절반입니다: 일회성 "끝나면 알려줘"는 `run_in_background`가, 이벤트마다 "X가 일어날 때마다 알려줘"는 `monitor`가 맡습니다.

```js
// 모든 에러 줄을 무기한 알림
monitor({ command: "tail -f deploy.log | grep --line-buffered -E 'ERROR|Traceback'", description: "deploy.log 에러" })

// CI 체크가 들어올 때마다 내보내고, 실행이 끝나면 종료
monitor({ command: "…종료되는 폴링 루프…", description: "PR 123 CI 체크" })

// WebSocket 피드 구독 — 각 텍스트 프레임이 이벤트
monitor({ ws: { url: "wss://events.example.com/stream" }, description: "배포 이벤트", persistent: true })
```

| 파라미터 | 설명 |
|---------|------|
| `command` | 셸 스크립트. stdout 한 줄이 이벤트 하나. `ws`와 상호 배타적. |
| `ws` | WebSocket 소스 `{ url, protocols? }`. 텍스트 프레임 하나가 이벤트 하나. `command`와 상호 배타적. |
| `description` | 모든 알림에 표시됨(구체적으로). **필수.** |
| `persistent` | 세션 전체 동안 실행(타임아웃 없음). `jobs action='kill'`로 중지. 기본값 `false`. |
| `timeout_ms` | 워치를 종료할 데드라인(기본 `300000`, 최대 `3600000`). `persistent`면 무시. |

모니터는 백그라운드 도구와 같은 잡 레지스트리·사이드바(`◉` 표시)·`jobs` 매니저를 공유합니다 — stdout만 이벤트 스트림이고(stderr는 별도 `.err` 파일에 캡처), 출력은 줄 단위 버퍼링이므로 `grep --line-buffered`/`awk fflush()`를 쓰고 `head`는 절대 쓰지 마세요. 이벤트를 쏟아내는 모니터는 자동으로 중지되니 더 좁은 필터로 다시 시작하면 됩니다. `ws` 소스는 전역 `WebSocket`(Node 22+)이 있는 런타임이 필요하며, 없으면 `websocat` 같은 `command`를 쓰세요.

> **지속 모니터와 디스크:** `persistent`가 아닌 모니터의 출력 로그는 상한이 있습니다(출력이 너무 커지면 종료됨). 하지만 `persistent` 모니터는 세션 내내 도는 것을 전제로 하므로 로그에 크기 상한이 **없습니다** — 장기 `tail -f`는 firehose 대신 필터링된 스트림에 걸고, 끝나면 `jobs action='kill'`로 중지하세요.

## 키보드 단축키

손은 키보드 위에 그대로 두세요.

| 단축키 | 동작 |
|-------|------|
| **Ctrl+Shift+B** | 실행 중인 모든 포그라운드 명령을 백그라운드로 — 에이전트는 계속 작업 (Claude Code의 Ctrl+B에 해당; pi가 Ctrl+B를 에디터 커서 이동에 예약해 둠). extended-keys가 없는 tmux에서는 `/bg`를 사용하세요. |
| **Ctrl+Shift+J** | 백그라운드 작업 관리자 열기 |
| **Shift+Down** | 백그라운드 작업 관리자 열기 |
| **Ctrl+Shift+X** | 가장 최근에 실행 중인 잡 종료 |

## 커맨드

슬래시가 편하다면? 들어가는 문만 다를 뿐, 할 수 있는 건 똑같습니다.

| 커맨드 | 설명 |
|-------|------|
| `/bg` | 현재 프로세스를 백그라운드로 (Ctrl+Shift+B와 동일) |
| `/bg-list` | 인터랙티브 백그라운드 작업 관리자 열기 |
| `/bg-version` | 로드된 익스텐션 버전/경로 표시 (리로드 진단용) |

## 동작 원리

마법 같은 건 없습니다. 깔끔한 상태 머신 하나가 전부입니다.

```
명령 시작 (Node.js child_process.spawn 직접 호출)
  → 2초 안에 끝났나?        결과를 즉시 반환
  → 120초까지 돌고 있나?    자동 백그라운드 → 도구 결과에 새 작업 ID가 담김
  → Ctrl+Shift+B를 눌렀나?   즉시 백그라운드 → 에이전트는 계속 진행

백그라운드 잡 실행 중
  → 출력을 파일 디스크립터로 /tmp/pi-bg/<id>.log에 캡처
  → 정체 감지: 출력이 멈춘 채 끝부분이 인터랙티브 프롬프트처럼 보이면 에이전트에 경고
  → 과대 출력 감지: 출력이 한도를 넘어서면 잡을 종료
  → 완료 시: 개별 <task-notification>이 턴 중간에 상태 + 출력 경로와 함께 도착
```

백그라운드 잡은 stdout/stderr을 로그 파일 디스크립터에 곧장 연결한 detached Node.js
자식 프로세스로 돌아갑니다 — Claude Code가 쓰는 바로 그 패턴입니다. tmux도, 외부 프로세스
관리자도, 명령과 로그 사이를 가로막는 무언가도 없습니다. 백그라운드 잡은 한 번에 최대
**16개**까지 동시에 돌고, 17번째를 요청하면 자리가 빌 때까지 정중하게 거절합니다.
레지스트리는 순수하게 인메모리입니다: 세션 종료 — 종료 사유를 가리지 않는 모든 종료 — 는
실행 중인 작업을 전부 죽이고, 다음 세션에서 되살아나는 건 없습니다. `/tmp/pi-bg`의 로그
파일은 OS가 알아서 치우도록 그냥 둡니다.

겉에 드러나지 않는 내부 구조도 이에 맞춰 정리했습니다. 세 가지 작업 종류(shell, agent, monitor)가 하나의 통합 인메모리 레지스트리에 살고 단일 알림 엔진을 공유합니다. 스폰은 자식의 `close`가 아니라 `exit` 이벤트를 기다리므로, 출력 디스크립터를 물려받은 데몬화된 손자 프로세스가 잡을 붙잡고 늘어지는 일이 없습니다. 끝난 작업은 저마다 `<task-notification>`을 정확히 한 번씩 보냅니다 — `jobs output`/`attach`로 끝난 잡의 출력을 읽으면 읽음 처리되어 대기 중인 알림이 억제되고, 완료됐지만 읽지 않은 잡은 `jobs list`와 사이드바에 `, unread`로 표시됩니다.

## 협조적 스티어링 (Claude Code 동등성)

백그라운드로 넘길 수 있는 포그라운드 명령이 돌아가는 도중에 메시지를 입력하면, Pi가 그 텍스트를 스티어링으로 큐에 넣기 **전에** 익스텐션이 먼저 끼어듭니다.

1. 지금 돌고 있는 포그라운드 명령이 백그라운드로 미끄러져 들어갑니다 (출력은 계속 캡처되니 잃는 건 없습니다).
2. 현재 에이전트 턴은 중단됩니다.
3. 에이전트가 idle 상태가 되는 순간, 입력한 메시지가 새 사용자 턴으로 곧장 다시 올라옵니다.

이게 정확히 Claude Code가 동작하는 방식입니다. 중단 가능한 도구가 도는 도중에 입력을 넣으면, 그 도구를 멈추고 새 턴을 시작합니다. 메시지가 오래 걸리는 호출 뒤에 줄 서서 갇혀 있는 일은 없습니다. 폴링도 없고, 차례를 기다릴 일도 없습니다.

**적용 범위:** 이 익스텐션이 직접 관리하는 `bash` 도구에만 해당합니다. 익스텐션이 감싸지 않는 다른 오래 걸리는 도구는 Pi 기본 스티어링(큐에 쌓였다가 다음 턴이 시작될 때 전달)으로 처리됩니다.

## 상태 바

라이브 pill 위젯이 실행 중인 잡을 소요 시간과 명령 미리보기까지 곁들여 늘 눈앞에 띄워 둡니다. 완료/실패 개수는 상태 줄에 같이 따라붙습니다. 전체 그림을 보고 싶을 땐 Shift+Down이나 `/bg-list`로 작업 관리자를 열면 됩니다.

## 릴리스

### 2.0.0 — Claude Code 동등성 재아키텍처

백그라운드 엔진을 Claude Code의 실제 구현에 맞춰 처음부터 끝까지 다시 세웠습니다.

**호환성을 깨는 변경**
- **`job_decide`가 사라졌습니다.** 자동 백그라운드 타임아웃이 울리면 명령이 조용히 백그라운드로 미끄러져 들어갑니다 — 도구 결과 자체(`Command running in background with ID: …`)가 곧 알림입니다. 결정 프롬프트도, 강제 턴 지시도 없습니다.
- **작업 ID 형식이 바뀌었습니다.** 순차 `job-<pid>-<n>` 대신 종류별 랜덤 ID입니다: `b…`(shell), `m…`(monitor), `a…`(agent)에 base36 8자(예: `b7f3k9a2x1`).
- **세션 간 복원이 없습니다.** 백그라운드 작업은 세션 재시작/리로드를 넘어 살아남지 못합니다 — 어떤 사유의 세션 종료든 실행 중인 작업을 전부 죽입니다. 레지스트리는 순수 인메모리이고, `/tmp/pi-bg/*.log` 파일은 OS가 치우도록 그냥 둡니다 (24시간 오래된 로그 청소도 사라졌습니다).
- **완료 알림이 개별 `<task-notification>` 메시지**로 턴 중간에(pi의 steer 모드) 전달됩니다 — 에이전트가 도구 호출 사이에서 바로 반응합니다. 턴 경계 합산 요약과 유휴 합산 창은 사라졌습니다.

**엔진**
- `notified` 래치(CC의 `markTaskNotified`)로 정확히 한 번만 전달합니다: `jobs output`/`attach`로 끝난 잡을 읽으면 대기 중인 알림이 억제되고, 완료됐지만 읽지 않은 잡은 `jobs list`와 사이드바에 `, unread`로 표시됩니다. 알림이 나간 종료 잡은 라이브 목록에서 빠지고, 최근 것은 recent-terminal 링에 남습니다.
- shell/monitor/agent 세 종류가 하나의 통합 인메모리 레지스트리에 살고(`jobs list` 각 줄에 `[shell|agent|monitor]` 태그), 알림 엔진은 하나(`src/notify.ts`)이며, 스폰은 `exit` 이벤트를 써서 데몬화된 손자 프로세스가 잡을 붙잡지 못하게 했습니다.
- Ctrl+Shift+B(와 `/bg`)가 이제 가장 최근 것뿐 아니라 실행 중인 포그라운드 명령 **전부**를 백그라운드로 보냅니다.
- 사용자에게 보이는 문자열을 전부 CC 그대로 맞췄고(수동 백그라운드, `jobs kill`, 알 수 없는 ID 오류, 완료 요약, 정체 경고), 타임아웃 종료는 로그에 `Command timed out after Ns`를 남겨 모델이 타임아웃 종료와 실패를 구분할 수 있게 했습니다.

### 1.1.2 — 알림은 벽이 아니라 요약 한 줄로

- **백그라운드 알림이 턴 경계에서 합쳐집니다.** 긴 에이전트 턴 동안 끝난 잡과 모니터가 더는 답변 뒤에 `[job-finished]` / `[bg-monitor-event]` 줄을 벽처럼 쏟아내지 않습니다. 모아 두었다가 턴이 끝날 때(`agent_end`) **요약 한 줄**로 내보냅니다 — *"4 background events — 1 completed (job-19), 1 failed (job-30 exit 1); 2 monitors ended (API health, port 4000)."* 에이전트가 유휴 상태면 짧은 대기창으로 합칩니다. 모니터 *스트림* 이벤트(지켜보는 매칭 줄)는 실시간 그대로입니다.
- 비정상적으로 끝난 턴이 남긴 알림을 비워 내는 가드가 있어, 전달 안 된 채 묶여 있는 알림이 없습니다.

### 1.1.1 — 동등성 수정, 데이터 손실 방지, 실시간 진행 표시

- **사이드바에 실시간 진행 표시.** 실행 중인 잡의 알약이 이제 명령 대신 **최신 출력 줄**을 (매초 갱신) 보여줍니다 — 긴 폴링/빌드의 진행을 한눈에 (`◉ qdrant: {"indexed":8540629,"status":"grey"} (2m10s)`). ANSI/제어 시퀀스는 제거되어 위젯이 깔끔하게 유지되고 이스케이프 주입도 막습니다.
- **`sleep` 잡이 더는 어슬렁대지 않습니다.** 순진한 `sleep N` 대기는 (`cd x; sleep 600; check`처럼 중간에 끼어 있거나, 줄바꿈으로 나뉘었거나, 백그라운드여도) 이제 `bash`와 `bash_bg` 양쪽에서 차단되고, 작업이 끝날 때 함께 끝나는 도구로 안내합니다: `jobs attach`, `monitor` 도구, 또는 준비되면 빠져나오는 `until` 루프. 진짜 폴링 루프 안의 sleep은 절대 차단하지 않습니다.
- **취소에 대한 Claude Code 동등성 (CC 소스로 검증).** **Esc**를 누르면 실행 중인 포그라운드 명령이 종료되고(의도적 취소), 새 메시지 입력·**Ctrl+Shift+B**·자동 백그라운드 타임아웃은 대신 백그라운드로 옮깁니다 — CC의 `user-cancel` vs `interrupt` 동작 그대로입니다. 긴 작업은 취소를 무시하는 게 아니라 타임아웃 시 자동 백그라운드 + `run_in_background`로 보호합니다.

### 1.1.7 — Ctrl+Shift+B (pi 예약 키바인딩 수정)

- **백그라운드 단축키가 Ctrl+B에서 Ctrl+Shift+B로 바뀌었습니다.** 최신 pi는 `ctrl+b`를 에디터 커서 왼쪽 이동(`tui.editor.cursorLeft`) 내장 키로 예약하고, 익스텐션이 이 키를 차지하면 시작할 때 "extension shortcut conflict" 진단을 출력합니다. 이 키를 등록하면 pi 에디터의 커서 왼쪽 이동도 조용히 깨졌습니다. 나머지는 그대로입니다: 힌트 문구는 `(ctrl+shift+b to run in background)`로 바뀌었고, `/bg`는 어디서나 동작합니다 (extended-keys 없는 tmux에서는 `/bg`를 쓰세요).

### 1.1.0 — monitor 도구 & 완료 알림 합치기

- **새 `monitor` 도구** — Claude Code가 둘로 나눈 것 중 스트리밍 절반입니다. stdout 한 줄(또는 WebSocket 프레임)이 발생하는 즉시 알림 하나가 됩니다. 이벤트마다 알림이 필요한 스트림(`tail -f | grep`, 폴링 루프, 파일 워치, ws 피드)에 쓰고, 일회성 "끝나면 알려줘"는 `run_in_background`가 맡습니다. `command`/`ws` 소스, `persistent` 워치, `timeout_ms` 데드라인, 줄 단위 정확한 추적, firehose 자동 중지를 지원합니다. 사이드바와 `jobs` 매니저에 `◉` 표시로 나타납니다.
- **`[job-finished]` 줄의 벽이 사라졌습니다** — 백그라운드 잡 여러 개가 한꺼번에 끝나도, 잡마다 한 줄씩 쏟아내는 대신 하나의 요약 알림으로 합쳐집니다.
- 내부: `MonitorSession`을 `MonitorSource` 심(command + ws 어댑터) 뒤로 분리해 스트리밍/종료 라이프사이클을 단위 테스트할 수 있게 했습니다. 새 의존성은 0 (ws는 런타임의 전역 `WebSocket`, Node 22+ 사용).

### 1.0.2 — Ctrl+B 동등성 & 더 친절한 jobs

- 이제 **Ctrl+B**가 기본 백그라운드 단축키입니다 (Ctrl+Shift+B는 별칭으로 유지). 명령이 도는 동안 입력창 아래에 `(ctrl+b to run in background)` 힌트가 흐릿하게 떠 Claude Code와 동일합니다. tmux 안에서는 "(twice)" 안내가 함께 표시됩니다.
- **`jobs attach`가 대기하는 동안 잡의 실시간 출력을 스트리밍**합니다 (이전엔 조용했습니다). 문구도 "Following … live output"으로 바뀌었고, 떼어내도 잡은 계속 실행됩니다.
- **사이드바 알약이 실시간으로 갱신**됩니다 — 그려진 시점 값에서 멈추지 않고 소요 시간이 매초 올라갑니다.
- 잡 완료/타임아웃 알림이 **간결**해졌습니다 (한 줄 에이전트 follow-up + UI 토스트) — 박스형 스팸 없이 에이전트가 계속 상황을 압니다.

### 1.0.1 — Claude Code 동등성 (첫 1.x 배포)

큰 한 방입니다. 백그라운드 엔진을 Claude Code 아키텍처에 맞춰 바닥부터 다시 짰고, 외부 의존성은 0입니다. npm에 올라가는 첫 1.x이며, 동등성 재작성과 함께 정확성·성능을 단단히 다지는 작업을 한 묶음으로 담았습니다.

**호환성을 깨는 변경**
- **tmux를 걷어냈습니다.** 이제 백그라운드 잡은 파일 디스크립터로 출력을 캡처하는 Node.js `child_process.spawn` 프로세스로 직접 돌아갑니다. tmux는 더 이상 쓰지도, 필요하지도 않습니다 — 따로 설치할 게 없어졌습니다.
- **기본 자동 백그라운드 타임아웃이 120초로** 바뀌었습니다 (이전엔 15초), Claude Code와 동일합니다. 다르게 쓰고 싶으면 `timeout`을 직접 넘기면 됩니다.
- 백그라운드 로그가 `/tmp/pi-bg-<id>.log`에서 전용 디렉터리 `/tmp/pi-bg/<id>.log`로 옮겨졌습니다.

**주요 기능**
- `bash` 도구에 `run_in_background: true` 파라미터 추가.
- `agent_bg`가 이제 진행 상황을 실시간으로 스트리밍하고 `pi` 바이너리 경로를 직접 찾아냅니다 (표준 `$PATH` 밖에 설치된 경우에도 동작).
- 협조적 스티어링이 메시지를 다음 턴(follow-up)으로 전달합니다 — 폴링 루프 없음.
- 백그라운드 잡 동시 실행 상한 16개.
- 코드와 UI를 영어 전용으로 정리.

**버그 수정 및 내부 정리 (재작성 후 보강)**
- 협조적 스티어링이 방금 자기가 백그라운드로 보낸 그 명령을 더 이상 죽이지 않습니다.
- 스폰 실패(`ENOENT`/`EMFILE`/`EAGAIN`)가 에이전트를 죽이지 않고 매끄럽게 처리됩니다.
- 네 갈래로 흩어져 있던 스폰 경로를 `startBackgroundJob` 서비스 함수 하나로 합쳤고, 포그라운드 정리를 `finally`로 옮겨 어느 종료 경로에서도 잡이 붕 떠버리지 않게 했습니다.
- 로그 검색을 잡별로 동시에 돌리고, 오래된 로그 청소도 상한을 둔 비동기 방식으로 바꿨습니다.

### 0.3.1 및 그 이전

tmux 기반 백그라운드 잡, 15초 자동 백그라운드, 협조적 스티어링, 인터랙티브 작업 관리자.

## 개발

```
git clone https://github.com/patty-io/pi-patty-bg-tasks.git
cd pi-patty-bg-tasks
pnpm install
pnpm check    # 타입 체크
pnpm test     # 테스트 실행
```

Node.js 22 이상, pnpm 10 이상이 필요합니다. tmux를 비롯한 외부 의존성은 없습니다 — clone 받은 그대로 바로 돌아갑니다.

## 기여하기

PR 환영합니다. 순서는 이렇습니다.

1. 저장소 포크
2. 기능 브랜치 생성 (`git checkout -b feat/my-feature`)
3. `pnpm check`와 `pnpm test`가 통과하는지 확인
4. [컨벤셔널 커밋](https://www.conventionalcommits.org/)으로 커밋
5. `main`을 대상으로 PR 열기

## 라이선스

[MIT](LICENSE) © Patty

## 저자

**Patty** · [GitHub](https://github.com/patty-io)
