forge
포지(forge) — 셀프호스팅 Forgejo 이슈/PR/라벨/코멘트 작업면. 힣 에이전트가 코드 면에서 일하는 공유 손. botment의 자식 패턴(가든 댓글면 → 코드 댓글면 endpoint swap). 'forge', 'forgejo', '포지', 'issue', 'pull request', '이슈', '풀리퀘', '코드 댓글', '라벨'.
ソース情報
- リポジトリ
- junghan0611/forge-config
- ソースの最終更新活動
- 2026年9月4日 05:31
- 検出された SKILL.md の言語
- 韓国語
- スター
- 2
- フォーク
- 0
インストール方法
デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。
ソースファイルを確認
インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。
SKILL.md を表示中
SKILL.md
ソースの指示 · 読み取り専用プレビュー- name
- forge
- description
- 포지(forge) — 셀프호스팅 Forgejo 이슈/PR/라벨/코멘트 작업면. 힣 에이전트가 코드 면에서 일하는 공유 손. botment의 자식 패턴(가든 댓글면 → 코드 댓글면 endpoint swap). 'forge', 'forgejo', '포지', 'issue', 'pull request', '이슈', '풀리퀘', '코드 댓글', '라벨'.
- user_invocable
- true
# forge — Forgejo 코드 작업면
힣 에이전트가 Forgejo 이슈/라벨/코멘트를 *공유 작업면*으로 쓰는 스킬.
**botment의 자식 패턴**: 가든 댓글(remark42) → 코드 댓글(Forgejo) endpoint swap.
## SSOT
CLI 본체는 [`forge-config`](https://github.com/junghan0611/forge-config) repo 안에 산다.
agent-config은 이 SKILL.md만 들고, 실 명령은 외부 절대경로로 호출한다.
```
~/repos/gh/forge-config/bin/forge
```
이 스킬에서 다루는 동사/플래그/env 변수가 실제 동작과 어긋나면 **bin/forge가
정답**이다. 의문 시 `~/repos/gh/forge-config/AGENTS.md` 와 `NEXT.md` 확인.
운영 인스턴스 (2026-05-27 기준):
| profile | URL | 용도 |
|---------|-----|------|
| `oracle` | `https://forge.junghanacs.com` | 개인 / 공개 정원 페어 (`repos/gh/*`) |
| `work` | `https://<work-forge-host>/forge` | 회사 / mirror 운영면 (`repos/work/*`) |
## 핵심 원칙
1. **공장 모델 거부** — 병렬 에이전트 수가 아니라 *공유 컨텍스트와 자취*가 핵심
2. **단일 신원** — Forgejo 사용자 `glg-bot` 하나, footer 서명으로 모델/호스트 식별
3. **사람 게이트 merge** — v1에서 자동 merge 없음
4. **닫힌 계** — 시크릿은 `~/.env.local` + `pass`. repo 에는 변수명만
5. **운영 머신 ≠ forge 인스턴스** — thinkpad 에서 양쪽 forge 다 굴린다 (세션이 thinkpad 에 남아야 andenken 임베딩됨). cwd 가 어느 forge 인지 결정한다.
6. **auto-fix는 완료가 아니라 검증 루프 시작** — minor/bounded patch candidate 뒤에 1회독 direct fix/test, 2회독 주변 동형 패턴 전수조사, 3회독 독립 리뷰, follow-up issue 생성까지 남긴다.
## 환경 변수 — profile 시스템
`bin/forge` 가 호출 시점에 **profile** 을 자동 결정해서 prefix 변수를 풀어쓴다.
에이전트는 변수만 박혀있는지만 확인하면 된다.
### Profile 결정 우선순위 (높을수록 우선)
1. `--forge oracle|work` 플래그
2. `FORGE_PROFILE` env (`oracle` / `work`)
3. **cwd 패턴 — 명시 anchor 만**:
- `*/repos/work/*` → `work`
- `*/repos/gh/*` → `oracle`
- **그 외 (`~`, `~/org/`, `~/Downloads/` 등)** → 에러. 명시 요구.
4. Legacy env value fallback (URL/TOKEN 만) — profile prefixed 가 비어있을 때 unprefixed `FORGE_URL`/`FORGE_TOKEN` 참조. host-scoped switching 결과 (oracle/work 에 직접 ssh 들어갔을 때만 의미). `FORGE_REPO` 는 fallback 없음 (인스턴스 간 leak 방지).
→ thinkpad 에서 `cd ~/repos/gh/foo && forge list-open` 하면 자동 oracle.
→ `cd ~/repos/work/bar && forge state 3` 하면 자동 work.
→ `cd ~ && forge list-open` 은 **silent oracle default 금지** — 명시 요구. mutating 사고 방지.
→ `forge --forge work list-open glg-bot/<work-repo>` 로 어디서든 명시 override.
### Mutating 명령 stderr observability
`comment` / `label-add` / `label-remove` / `label-set` 호출 시 stderr 에 한 줄 노출:
```
[forge] profile=oracle repo=glg-bot/sandbox url=https://forge.junghanacs.com
```
→ 잘못된 인스턴스/repo 에 write 하기 전에 즉시 인지. `FORGE_PROFILE` env 가 셸에 오래 남아 cwd 보다 우선될 때의 사고도 같은 표면으로 잡힌다.
### Profile-prefixed env (`~/.env.local` SSOT)
| 변수 | 필수 | 비고 |
|------|------|------|
| `ORACLE_FORGE_URL` / `_TOKEN` / `_USER` | ✅ | oracle profile 원천 |
| `WORK_FORGE_URL` / `_TOKEN` / `_USER` | ✅ | work profile 원천 |
| `ORACLE_FORGE_REPO` | ⭕ | default `glg-bot/sandbox` |
| `WORK_FORGE_REPO` | ⭕ | default 없음 — 인자로 명시 강제 |
| `FORGE_MODEL` | ⭕ | footer 의 모델 부분 (없으면 `unknown`) |
> `bin/forge` 는 profile 토큰이 env 에 없으면 **`~/.env.local` 을 스스로 source**
> 한다. pi 는 env 를 자동 주입하므로 sourcing 을 건너뛰고, Claude Code / 맨 셸
> 처럼 주입이 없는 하네스에서도 `source ~/.env.local` 선행 없이 바로 돈다.
> 토큰이 이미 있으면(주입/명시 override) 덮지 않는다. 경로는 `FORGE_ENV_FILE` 로 교체.
### 머신별 default profile — `~/.current-forge-profile`
각 머신이 "어느 forge 의 *직접 접속 호스트* 인지" 박는다. 클라이언트 머신은 비워둔다.
| 머신 | 정체성 | 파일 |
|---|---|---|
| oracle | "oracle forge 의 호스트" | `echo oracle > ~/.current-forge-profile` |
| 회사 머신 | "work forge 의 호스트" | `echo work > ~/.current-forge-profile` |
| thinkpad / laptop / nuc 등 | 양쪽의 **클라이언트** (호스트 아님) | **없음** — cwd 로 매번 결정 |
`.env.local` 의 case 분기가 이 파일을 입력으로 unprefixed `FORGE_URL`/`TOKEN`/`USER` 를 set 한다. 클라이언트 머신은 case 매칭 실패 → bin/forge 의 cwd 패턴 결정에 자연 위임.
> 참고: `FORGE_BOT_FOOTER` env 는 **무시된다**. footer 형식은 정책이라
> 매 호출마다 `bin/forge` 가 자동 조립한다 — 깨진 부모 셸 env 가 발현되지
> 않도록 닫아둔 표면. 모델은 `FORGE_MODEL` 로만 customize.
### footer 자동 조립
```
— glg-bot [<FORGE_MODEL or "unknown"> / <~/.current-device or "unknown">]
```
세션 시작 시 모델만 박아두면 footer 가 정확해진다:
```bash
export FORGE_MODEL="claude-opus-4-8"
# thinkpad 에서 oracle forge 코멘트 → "— glg-bot [claude-opus-4-8 / thinkpad]"
# 같은 thinkpad 에서 work forge 코멘트 → 동일 footer (작업 머신 기준)
```
env 미설치 시 `bin/forge` 가 친절한 에러를 던지므로 그것을 따라가면 된다
(`FORGE_URL is required (profile=<name> — set <NAME>_FORGE_URL in ~/.env.local)`).
## 발견성 — namespace 와 처음 만난 repo
각 forge 인스턴스의 봇 namespace 는 **`glg-bot/*`** (oracle / work 양쪽 동일). 처음 다루는 repo 라 *어느 path 에 박지* 모르면 GitHub remote 의 `teamgoqual/*` / `junghan0611/*` 를 그대로 추측하지 말 것 — forge namespace 와 안 맞는다.
발견 recipe:
```bash
forge --forge work repos # work forge 의 glg-bot/* 실재 목록
forge --forge oracle repos # oracle forge 의 glg-bot/* 실재 목록
forge --forge work repos <other> # 다른 namespace 도 명시 가능
```
운영 사실 (자취):
- oracle forge → `glg-bot/forge-config`, `glg-bot/sandbox`
- work forge → `glg-bot/voscli`, `glg-bot/incidentcli`, `glg-bot/sandbox`
GitHub repo 의 `<owner>/<name>` 에서 `<name>` 만 떼서 `glg-bot/<name>` 매핑하는 게 자연 fallback 이지만, 실재하지 않을 수 있다 — `forge repos` 로 먼저 확인한 뒤 매칭.
## API — v2 동사 9개
| 명령 | 인자 | 동작 |
|------|------|------|
| `repos` | `[OWNER]` | 현 profile 의 봇 namespace (기본 `<FORGE_USER>` = `glg-bot`) 아래 실재 repo 목록. **처음 만난 forge 의 발견 자리** |
| `list-open` | `[REPO]` | 열린 이슈 목록 (제목 + 라벨 + 코멘트 수). REPO 생략 시 default repo |
| `state` | `ISSUE` | 이슈 상태 + 라벨 + 최근 코멘트 3개 |
| `comment` | `ISSUE BODY` 또는 `ISSUE --body-file PATH|-` | 코멘트 작성. footer 자동 부착. **multi-line / child 결과는 `--body-file` 사용** |
| `label-add` | `ISSUE LABEL` | 라벨 이름으로 ID 조회 후 부착 |
| `label-remove` | `ISSUE LABEL` | 라벨 이름으로 ID 조회 후 제거 |
| `label-set` | `ISSUE STATUS-LABEL` | 상태 라벨군(`agent:ready/running/done/blocked`, `human:needs-review`)을 하나로 교체. `ci:failed` 같은 신호 라벨은 보존. forgebot 루프의 `agent:done` = 1차 검토/분류 완료, 구현 완료 아님 |
| `close` | `ISSUE` | Forgejo open/closed **state** 를 `closed` 로. lifecycle 라벨과 직교 — `agent:done`(분류 완료) ≠ `closed`(해결/철회, 추적 종료). 이유 `comment` 먼저, 그다음 close. **규약**(코드 가드 아님): 해결-close(shipped tag/commit + 재현 안 됨 확인) = 담당자 자율 ✅ / 철회-close(won't-fix·중복·설계무효·우선순위 철회 = 가치판단) = GLG 또는 `human:needs-review` 경유 |
| `reopen` | `ISSUE` | 닫힌 이슈 state 를 `open` 으로 되돌림 (close 의 역) |
| `issue-create` | `[REPO] TITLE BODY [OPTIONS]` 또는 `[REPO] TITLE --body-file PATH [OPTIONS]` | 이슈 생성. footer 자동 부착. atomic 라벨 (`--labels`) + Mattermost thread bridge (`--mm-channel/--mm-root-id/--mm-account`) 옵션. **multi-line BODY 는 `--body-file PATH` (또는 `-` = stdin) 필수** — inline BODY 는 single-line 만 |
| `auto-fix-template` | `ISSUE` | auto-fix 회독 루프용 표준 코멘트 골격 출력. `schema/report_id/session_key/issue_updated_at/lifecycle/labels/provider_model/forge_config_commit` snapshot marker 포함. 출력 → 파일 저장 → 채운 뒤 `comment --body-file` |
| `doctor-labels` | `[REPO]` | repo 가 forge v2 + auto-fix lane 에 필요한 lifecycle/signal labels (`agent:*`, `human:needs-review`, `ci:failed`, `auto-fix`) 를 갖췄는지 read-only 점검. missing 이 있으면 non-zero exit |
`ISSUE` / `REPO` 인자 형식:
- `1` → default repo 의 #1
- `glg-bot/sandbox#1` → 명시된 repo 의 #1
- `owner/repo` (REPO) / `repo` (`<FORGE_USER>/repo` 로 확장)
`issue-create` — sweeper 의 일차 입력 자리:
```bash
# atomic 라벨 — 라벨 부착이 별도 호출이 아니라 생성과 동시
forge --forge work issue-create glg-bot/<work-repo> \
"Bug: foo 안 됨" "운영팀 보고..." \
--labels agent:ready
# multi-line BODY — 파일 또는 stdin 사용 (inline 인자에 \n 넣으면 positional 파서가 깨짐)
forge --forge work issue-create glg-bot/<work-repo> \
"Feature: weekly/monthly VOC range report" \
--body-file /tmp/voc-issue.md \
--labels agent:ready
cat <<'EOF' | forge --forge work issue-create glg-bot/<work-repo> \
"Feature: weekly/monthly VOC range report" --body-file - --labels agent:ready
## 배경
지난 분기 데이터 ...
## 요구
- 주간/월간 범위 ...
EOF
```
### Mattermost thread bridge — `--mm-channel/--mm-root-id/--mm-account`
봇이 Mattermost thread 에서 받은 요청으로 이슈 생성할 때, *원래 thread* 로 lifecycle 자취가 돌아가게 metadata 박을 자리. forge agent 가 이슈 처리 후 OpenClaw message tool 의 `replyTo` 로 같은 thread 에 답장하기 위함. (`replyToId` 아님 — 실제 schema 는 `target: "channel:<channel_id>"`, `replyTo: "<root_id>"`.)
```bash
forge --forge work issue-create glg-bot/voscli \
"Bug: foo 안 됨" --body-file - --labels agent:ready \
--mm-channel cidABC123 --mm-root-id ridXYZ789
# --mm-account default = "forgebot"
```
자취 두 자리에 박힘:
- **issue body 끝** — `<!-- openclaw:mm {"channel_id":"...","root_id":"...","account":"..."} -->` HTML comment. 렌더된 마크다운에 사람한테 안 보임, 봇이 read 시 회수
- **로컬 SQLite SSOT** — `~/.openclaw/state/forge-mm-links.sqlite`. key `<profile>:<repo>#<issue_num>` → `{channel_id, root_id, account_id, created_at}`. `sqlite3` 없으면 WARN 한 줄 + 계속 (body metadata 가 canonical)
검증:
- `--mm-channel` 과 `--mm-root-id` 는 **둘 다 박혀야** 한다. 한쪽만 박으면 ERROR exit 2 — 부분 metadata 가 thread bridge 동작 침묵 깨짐 자리
추가 동사(`read`, `pr ...`)는 v2 이후 운영 누적 후 forge-config 측에서 박는다. 여기서 동사를 임의로 늘리지 말 것 (SSOT 어긋남).
## git push 인증 — `git-credential-forge`
`bin/forge` 는 **REST 작업면**(issue/label/comment)이다. forge repo 에 *코드를
push* 하는 건 별개 레이어 — git 의 HTTPS 인증이다. 이 자리를 채우는 게
sibling 바이너리 `bin/git-credential-forge`.
핵심: REST 와 **같은 `~/.env.local` profile 토큰**(`ORACLE_FORGE_TOKEN` /
`WORK_FORGE_TOKEN`)을 git 의 native credential 기계로 잇는다. 토큰-in-URL 도,
askpass 도, 분신마다 재발명도 없다 — forge 서버는 oracle/work 에 있어도
`git push` 는 어느 **클라이언트(thinkpad/nuc/laptop)** 에서나 그냥 동작한다.
- **generic / host 자동판별** — url-scope 없이 helper *하나*. git 이 모든 https
credential 요청에 부르면, helper 가 들어온 host(+git 이 주면 path-prefix)를
env.local 의 `ORACLE/WORK_FORGE_URL` 과 매칭해 맞는 토큰을 emit, forge 가
아니면 침묵해서 git 이 다음 helper(github 등)로 넘어간다. → **어떤 committed
파일에도 forge host literal 이 안 박힌다** (work host 가 identity term 이라 중요).
- **path 격리** — work forge 는 `<work-host>/forge` 처럼 path prefix 아래 있고,
그 host 는 다른 https git 을 서빙할 수 있다. `credential.useHttpPath true` 면
helper 가 forge path prefix 를 요구해서, 같은 host 의 비-forge 서비스가 work
토큰을 못 받는다. oracle forge 는 전용 host(prefix 없음) → host-only 가 정답.
- SSOT 는 `~/.env.local`. helper 는 런타임에만 토큰을 emit — 디스크에 안 쓴다
(`~/.git-credentials` 평문 복제 없음).
### 설치 — 기기마다 두 줄 (machine-local `~/.gitconfig`, nixos 아님)
```bash
git config --global credential.helper ~/repos/gh/forge-config/bin/git-credential-forge
git config --global credential.useHttpPath true # path 격리 (특히 work host)
```
`~/.gitconfig`(writable, machine-local)에 들어간다. home-manager 가 잡은
`~/.config/git/config`(nix 심링크, read-only)는 안 건드린다 — git 이 둘 다 읽어
머지한다. **nixos rebuild 불필요** (설치 자리 = `~/.gitconfig`, 결정 2026-06-02).
> work forge host 는 identity term 이지만, helper 가 host 를 env.local 에서 런타임
> 추출하므로 이 두 줄에도 forge-config repo 에도 host 가 안 박힌다. env.local 은
> 기기 간 동기화(클라이언트 + 양쪽 forge 호스트 모두 `ORACLE_*`+`WORK_*`)라
GitHubで見るこの SKILL.md は非常に大きいため、SkillsMP では最初のセクションだけを表示しています。 GitHubで見る