agent-work-loop 0.6.46 → 0.6.48

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.
Files changed (45) hide show
  1. package/README.md +77 -57
  2. package/dist/{brief-SGMFXOY2.js → brief-ODMUS5B3.js} +4 -4
  3. package/dist/{changelog-VNJ4EQ74.js → changelog-P35RSRT3.js} +4 -4
  4. package/dist/{chunk-FK3Q7UY2.js → chunk-2NETKDCV.js} +2 -2
  5. package/dist/{chunk-XWMS6Z5E.js → chunk-AVIUMBJZ.js} +3 -3
  6. package/dist/{chunk-MLTY3EE6.js → chunk-FLVRMRHX.js} +1 -1
  7. package/dist/{chunk-PUINOFOR.js → chunk-FPNV5L3Y.js} +3 -3
  8. package/dist/{chunk-ZEX5ZF6C.js → chunk-HW2OSIB3.js} +1 -1
  9. package/dist/{chunk-SJG22DAK.js → chunk-IPNBHVGX.js} +30 -1
  10. package/dist/{chunk-ZRKOZQKP.js → chunk-ITZ52UYF.js} +4 -4
  11. package/dist/{chunk-WWZIKKHP.js → chunk-IUXE7EG3.js} +4 -4
  12. package/dist/{chunk-AGFQC2UO.js → chunk-JY3DXRXV.js} +8 -8
  13. package/dist/{chunk-DA27NBCS.js → chunk-KKFXZ3NV.js} +6 -6
  14. package/dist/{chunk-EHJYX57P.js → chunk-OQJIRLKN.js} +1 -1
  15. package/dist/{chunk-VVZOIFL4.js → chunk-RTALSPLK.js} +1 -1
  16. package/dist/{chunk-VE2GVNNS.js → chunk-RTZLAXTZ.js} +16 -5
  17. package/dist/{chunk-IVEV5L7P.js → chunk-TCIRO4GU.js} +1 -1
  18. package/dist/{chunk-SSSOO2GH.js → chunk-UFVGXPJB.js} +4 -4
  19. package/dist/cli.js +44 -42
  20. package/dist/{commit-GPQATPEA.js → commit-7ONX22OG.js} +25 -7
  21. package/dist/{config-QTEKLFZP.js → config-KNSNSKU7.js} +2 -2
  22. package/dist/{doctor-JNUZJC6Z.js → doctor-UYO4Z6VC.js} +7 -7
  23. package/dist/{evolve-N3S32P5I.js → evolve-KSA5W6F3.js} +6 -6
  24. package/dist/{feedback-log-UCNJ2GVT.js → feedback-log-BIIFYX6D.js} +4 -4
  25. package/dist/{gotchas-PDG2ZZFQ.js → gotchas-US2HUCK2.js} +6 -6
  26. package/dist/{hold-recheck-UNAILFN5.js → hold-recheck-NYW2AGYK.js} +5 -5
  27. package/dist/{init-3B4ZRGWY.js → init-PEKMQY7D.js} +1 -1
  28. package/dist/{lane-ETMPPVXP.js → lane-647Q33RO.js} +14 -14
  29. package/dist/{loop-summary-CODZMRUY.js → loop-summary-BRRJ6V2U.js} +5 -5
  30. package/dist/{metrics-ZR7BPATB.js → metrics-C3UUN5U5.js} +3 -3
  31. package/dist/{record-TSB22DIO.js → record-MBGID3SX.js} +4 -4
  32. package/dist/{remove-VJ7VBKTZ.js → remove-46IZDWJ7.js} +14 -14
  33. package/dist/{review-CGALGS5X.js → review-XBOUKZTN.js} +12 -12
  34. package/dist/{rules-QLUXNSCN.js → rules-JKR4DO6F.js} +7 -7
  35. package/dist/{state-6IIH7SW2.js → state-QVDADIXS.js} +3 -3
  36. package/dist/{status-2LPWVCHT.js → status-BJ4B6NKL.js} +5 -5
  37. package/dist/{update-CKGPHO5B.js → update-UXULNKWR.js} +1 -1
  38. package/dist/{verify-7NQ3DOFY.js → verify-7KWUZCN7.js} +9 -9
  39. package/dist/{version-check-5BX3SO7G.js → version-check-Y3KBDUQ2.js} +4 -4
  40. package/dist/{work-3YHL3P6U.js → work-T5S4FNFZ.js} +13 -13
  41. package/engine/skills/claude/awl-pipeline/SKILL.md +1 -0
  42. package/engine/skills/claude/awl-pipeline-exec/SKILL.md +7 -4
  43. package/engine/skills/claude/awl-pipeline-review/SKILL.md +6 -3
  44. package/engine/version.json +1 -1
  45. package/package.json +1 -1
package/README.md CHANGED
@@ -1,13 +1,32 @@
1
1
  # agent-work-loop
2
2
 
3
- `agent-work-loop`(명령어: `awl`)은 AI 에이전트가 같은 실패를 두 번 반복하지 않도록 돕는 도구입니다. 에이전트가 작업하며 남긴 기록, 검증 결과, 완료 조건, 규칙, 교훈, 막힘을 파일로 관리합니다.
3
+ [![npm version](https://img.shields.io/npm/v/agent-work-loop.svg)](https://www.npmjs.com/package/agent-work-loop)
4
+ [![CI](https://github.com/kh1012/agent-work-loop/actions/workflows/ci.yml/badge.svg)](https://github.com/kh1012/agent-work-loop/actions/workflows/ci.yml)
5
+ [![release](https://img.shields.io/github/v/tag/kh1012/agent-work-loop?label=release&sort=semver)](https://github.com/kh1012/agent-work-loop/tags)
6
+ [![license](https://img.shields.io/npm/l/agent-work-loop.svg)](LICENSE)
4
7
 
5
- README는 **실용 문서**입니다. 설치부터 루프까지, 손으로 따라 있는 순서만 다룹니다. "왜 이렇게 만들었는가"(하네스 개념, 게이트 위치, narrative 실증 사례)는 [`docs/presentation/storyline.md`](docs/presentation/storyline.md)에 따로 정리했습니다. 명령어 하나하나의 자세한 사용법은 [`docs/presentation/commands.md`](docs/presentation/commands.md)를 참고하세요.
8
+ `agent-work-loop`(명령어: `awl`)은 AI 에이전트가 같은 실패를 반복하지 않도록 돕는 도구입니다. 에이전트가 작업하며 남긴 기록, 검증 결과, 완료 조건, 규칙, 교훈, 막힘을 파일로 관리합니다.
6
9
 
7
10
  **awl 자체는 판단하지 않습니다.** LLM을 호출하지 않고, 파일과 상태만 결정적으로 다룹니다. 무엇을 배우고 어떤 규칙을 세울지는 이미 설치된 에이전트(Claude Code, Codex)가 스킬로 판단합니다.
8
11
 
12
+ 이 README는 **실용 문서**입니다. 설치부터 첫 루프까지, 손으로 따라 할 수 있는 순서만 다룹니다. "왜 이렇게 만들었는가"(하네스 개념, 게이트 위치, narrative 실증 사례)는 [`docs/presentation/storyline.md`](docs/presentation/storyline.md)에, 명령어 하나하나의 자세한 사용법은 [`docs/presentation/commands.md`](docs/presentation/commands.md)에 정리했습니다.
13
+
9
14
  크로스 환경을 처음부터 전제로 합니다. macOS와 Windows, Claude Code와 Codex를 모두 지원하는 것을 목표로 합니다(Windows는 아직 macOS만큼 검증되지 않았습니다. [알려진 위험](docs/decisions.md)을 참고하세요).
10
15
 
16
+ ## 목차
17
+
18
+ - [언제 쓰나](#언제-쓰나)
19
+ - [설치](#설치)
20
+ - [5분 시작](#5분-시작)
21
+ - [LLM과 함께 실제로 쓰는 시나리오](#llm과-함께-실제로-쓰는-시나리오)
22
+ - [명령어 요약](#명령어-요약)
23
+ - [설정(config): 검증 명령과 모노레포](#설정config-검증-명령과-모노레포)
24
+ - [무엇이 어디에 쌓이나](#무엇이-어디에-쌓이나)
25
+ - [워크아이템 병행](#워크아이템-병행)
26
+ - [오케스트레이션 파이프라인: 여러 레인을 나란히](#오케스트레이션-파이프라인-여러-레인을-나란히)
27
+ - [문제 해결](#문제-해결)
28
+ - [기여하기](#기여하기)
29
+
11
30
  ---
12
31
 
13
32
  ## 언제 쓰나
@@ -35,32 +54,26 @@ yarn global add agent-work-loop
35
54
 
36
55
  > **pnpm 함정**: pnpm으로 전역 설치하면 `awl` 명령을 찾을 수 없다는 에러가 날 수 있습니다. pnpm의 전역 bin 디렉토리가 PATH에 없어서입니다. `pnpm setup`을 한 번 실행하고 터미널을 새로 열면 해결됩니다.
37
56
 
38
- > **팀 내부 설치(지금 당장 필요하면)**: 이 README를 작성하는 시점 기준으로 npm 레지스트리의 `agent-work-loop`는 아직 `0.0.0`(이름 예약용 placeholder)만 배포돼 있어 `npm install -g agent-work-loop`로는 동작하는 CLI가 설치되지 않습니다. 지금 바로 써야 한다면 이 저장소를 클론해 직접 빌드·링크하세요.
39
- > ```bash
40
- > git clone <이 저장소> && cd agent-work-loop
41
- > pnpm install && pnpm run build
42
- > pnpm link --global # 또는: npm link
43
- > ```
44
- > `dist/cli.js`가 `awl` bin으로 등록됩니다. 정식 npm 배포가 되면 이 문단은 지웁니다.
57
+ 저장소를 직접 빌드해서 쓰고 싶다면(기여하거나 배포 최신 상태를 바로 써보고 싶을 때):
45
58
 
46
- ### 업데이트 하기
47
-
48
- ```
49
- 업데이트 하기:
50
- 1. npm i -g agent-work-loop@latest npm에 배포된 최신 버전을 받습니다
51
- 2. awl update 설치된 패키지로 전역 엔진(~/.awl/engine)을 갱신합니다
59
+ ```bash
60
+ git clone https://github.com/kh1012/agent-work-loop.git && cd agent-work-loop
61
+ pnpm install && pnpm run build
62
+ pnpm link --global # 또는: npm link
52
63
  ```
53
64
 
54
- pnpm이나 yarn을 쓴다면 1번만 그 도구로 바꿉니다:
65
+ `dist/cli.js`가 `awl` bin으로 등록됩니다.
55
66
 
56
- ```bash
57
- pnpm add -g agent-work-loop@latest
58
- yarn global add agent-work-loop@latest
67
+ ### 업데이트 하기
68
+
69
+ ```
70
+ 1. npm i -g agent-work-loop@latest npm에 배포된 최신 버전을 받습니다
71
+ 2. awl update 설치된 패키지로 전역 엔진(~/.awl/engine)을 갱신합니다
59
72
  ```
60
73
 
61
- `awl update`(옵션 없음)는 `awl update --global`과 같습니다. 전역 엔진만 갱신하고 어떤 프로젝트도 건드리지 않습니다. 대부분은 이 두 줄이면 끝입니다.
74
+ pnpm이나 yarn을 쓴다면 1번만 그 도구로 바꿉니다. `awl update`(옵션 없음)는 `awl update --global`과 같습니다. 전역 엔진만 갱신하고 어떤 프로젝트도 건드리지 않습니다. 대부분은 이 두 줄이면 끝입니다.
62
75
 
63
- **여러 프로젝트를 관리하는 입장이라면** 등록된 프로젝트들의 로컬 스킬(`.claude/skills`, `AGENTS.md`, `.awl/config.json`)까지 한 번에 맞추고 싶을 수 있습니다:
76
+ **여러 프로젝트를 관리하는 입장이라면** 등록된 프로젝트들의 로컬 스킬(`.claude/skills`, `AGENTS.md`, `.awl/config.json`)까지 한 번에 맞추고 싶을 수 있습니다.
64
77
 
65
78
  ```bash
66
79
  npm i -g agent-work-loop@latest
@@ -71,12 +84,12 @@ awl update --all
71
84
 
72
85
  이렇게 갱신한 프로젝트를 커밋해서 push하면, 그 저장소를 쓰는 다른 사람들은 다음에 pull할 때 최신 스킬을 받습니다. **다만 자동으로 알림이 가는 구조는 아닙니다.** 각자 컴퓨터의 `~/.awl/engine`이 낡았는지는 그 사람이 `awl doctor`/`awl version-check`를 실행하거나 `/awl-loop`·`/awl-pipeline`을 실행할 때(버전 확인이 맨 처음 단계로 들어있습니다) 노란 경고로 보게 됩니다.
73
86
 
74
- 설치가 됐는지 확인합니다:
87
+ 설치가 됐는지 확인합니다.
75
88
 
76
89
  ```bash
77
90
  $ awl doctor
78
91
 
79
- +- Agent Work Loop · 진단 ------------------------------------------------------------------+
92
+ + Agent Work Loop · 진단
80
93
  | 환경
81
94
  | |-- Node: v22.22.2 [ok]
82
95
  | |-- 플랫폼: darwin arm64 [ok]
@@ -92,10 +105,10 @@ $ awl doctor
92
105
  |
93
106
  | 에이전트
94
107
  | |-- Claude Code: 없음
95
- | |-- Codex: 없음
108
+ | `-- Codex: 없음
96
109
  |
97
110
  | [x] 문제 2개. awl init 을 실행하세요.
98
- +-------------------------------------------------------------------------------------------+
111
+ +
99
112
  ```
100
113
 
101
114
  터미널이 유니코드/색을 지원하면(Windows Terminal, VS Code 터미널 등) 자동으로 감지해 상자 그림과 색을 씁니다. 지원 안 되면 위처럼 ASCII로 자동 전환됩니다. `doctor`는 아무것도 고치지 않습니다. 점검만 합니다.
@@ -111,22 +124,26 @@ $ cd my-project
111
124
  $ awl init
112
125
  ```
113
126
 
114
- 질문 없이 감지된 값으로 바로 진행하려면 `awl init --yes`를 씁니다. 실제 출력(스크래치 프로젝트, `--yes`):
127
+ 질문 없이 감지된 값으로 바로 진행하려면 `awl init --yes`를 씁니다. 실제 출력:
115
128
 
116
129
  ```
117
- +- 설정 완료 ---------------------------------------------------------------+
118
- | ~/.awl 생성됨 |
119
- | ~/.awl/engine 0.6.18 |
120
- | .awl/config.json 생성됨 <- 커밋하세요. 팀원은 이 파일을 씁니다 |
121
- | .awl/state.json gitignore 에 추가함 [ok] git push 차단 설치 |
122
- | 규칙 0개 · 교훈 0개 · 등록된 프로젝트 1개 · 1세대 |
123
- +---------------------------------------------------------------------------+
124
-
125
- +- 다음 단계 ----------------------------------------------------+
126
- | Claude Code 를 열고 이렇게 말하세요. |
127
- | |
128
- | /awl-loop 페이지 편집기에 여백 시스템을 넣고 싶어 |
129
- +----------------------------------------------------------------+
130
+ + 설정 완료
131
+ | [v0.6.46]
132
+ | ~/.awl 생성됨
133
+ | ~/.awl/engine 0.6.46
134
+ | .awl/config.json 생성됨 <- 커밋하세요. 팀원은 파일을 씁니다
135
+ | .awl/state.json gitignore 추가함 [ok] git push 차단 설치
136
+ | 규칙 0개 · 교훈 0개 · 등록된 프로젝트 1개 · 1세대
137
+ +
138
+
139
+ + 다음 단계
140
+ | Claude Code 를 열고 이렇게 말하세요.
141
+ |
142
+ | /awl-loop 페이지 편집기에 여백 시스템을 넣고 싶어
143
+ |
144
+ | /awl-pipeline <레인명> --gl 을 실행해보세요.
145
+ | (격리된 작업 세션이 생성되며, 자율 모드로 실행됩니다.)
146
+ +
130
147
  ```
131
148
 
132
149
  대화형 모드(`--yes` 없이)는 3단계 화면(1/3 주 언어 → 2/3 검증 명령어 → 3/3 규칙과 이 프로젝트의 성격)을 방향키(또는 번호)로 물은 뒤, 설치할 에이전트를 고르는 스킬 화면을 보여줍니다.
@@ -150,6 +167,13 @@ $ awl init
150
167
  awl status # 지금 어디까지 왔는지 한눈에
151
168
  ```
152
169
 
170
+ 명령어 전체와 스킬(`/awl-loop`·`/awl-pipeline`) 부연설명은 CLI 안에서도 바로 볼 수 있습니다.
171
+
172
+ ```bash
173
+ awl --examples # 자주 쓰는 명령 예시
174
+ awl --skills # /awl-loop·/awl-pipeline·레인·게이트 밀도(<mode>) 설명
175
+ ```
176
+
153
177
  ---
154
178
 
155
179
  ## LLM과 함께 실제로 쓰는 시나리오
@@ -198,10 +222,10 @@ awl status # 지금 어디까지 왔는지 한눈에
198
222
  ```bash
199
223
  awl records --json # 있었던 일 전수(타입·워크아이템으로 필터 가능)
200
224
  awl gotchas # 아직 규칙 안 된 교훈
201
- awl rules # 이 프로젝트에 적용되는 규칙
202
- awl metrics --compare # 워크아이템 세대별 지표 추세
203
- awl brief # 오늘(KST) 진행분만 모아 보기
204
- awl defer-summary # gate-medium/gate-low에서 보류된 중요 항목 최종 확인
225
+ awl rules # 이 프로젝트에 적용되는 규칙
226
+ awl metrics --compare # 워크아이템 세대별 지표 추세
227
+ awl brief # 오늘(KST) 진행분만 모아 보기
228
+ awl defer-summary # gate-medium/gate-low에서 보류된 중요 항목 최종 확인
205
229
  ```
206
230
 
207
231
  `~/.awl`을 직접 열어보면 이렇게 생겼습니다.
@@ -223,7 +247,7 @@ awl defer-summary # gate-medium/gate-low에서 보류된 중요 항목 최
223
247
 
224
248
  ## 명령어 요약
225
249
 
226
- 자주 쓰는 것만 적습니다. 25개 명령(사람용 17 + 스킬용 8) 전부와 실제 실행 예시는 [`commands.md`](docs/presentation/commands.md) 있습니다.
250
+ 자주 쓰는 것만 적습니다. 전체 명령과 실제 실행 예시는 [`commands.md`](docs/presentation/commands.md)에, `awl --examples`로도 바로 볼 수 있습니다.
227
251
 
228
252
  ```bash
229
253
  awl status # 지금 어디까지 왔는지
@@ -245,7 +269,7 @@ awl feedback-log # awl 도구 자체에 남겨진 피드백 기록 검토
245
269
  ```json
246
270
  {
247
271
  "project": "my-project",
248
- "mainLanguage": "typescript",
272
+ "mainLanguage": ["typescript"],
249
273
  "character": "디자인 토큰 강제, 여백은 자유 px 금지",
250
274
  "verify": {
251
275
  "typecheck": { "cmd": "tsc --noEmit" },
@@ -286,11 +310,11 @@ $ awl config set verify.typecheck.cwd packages/app
286
310
  |---|---|---|
287
311
  | 담는 것 | 엔진(스킬 템플릿), 규칙, 교훈(gotcha), 기록(records), 등록된 프로젝트 목록 | 이 프로젝트의 설정(config.json), 지금 루프 위치(state.json) |
288
312
  | 누구 것인가 | **당신**. 프로젝트를 옮겨 다녀도 따라옵니다 | **이 프로젝트**. 리포지토리를 나가면 안 따라옵니다 |
289
- | git 커밋 | 대상 아님(홈 디렉토리) | `config.json`은 커밋, `state.json`은 gitignore |
313
+ | git 커밋 | 대상 아님(홈 디렉토리) | 프로젝트마다 다름 — `state.json`은 항상 gitignore, `config.json`은 팀 컨벤션에 따라 커밋하거나 로컬 전용으로 gitignore 수 있습니다 |
290
314
 
291
315
  두 층으로 나눈 이유(다음 프로젝트에서 빈손으로 시작하지 않기 위해)와 규칙/교훈이 어떻게 승격되는지는 [`storyline.md` 4절·5절](docs/presentation/storyline.md)에서 다룹니다. `AWL_HOME` 환경변수로 전역 위치를 바꿀 수 있습니다(테스트·CI에서 유용합니다).
292
316
 
293
- 규칙 승격은 사람이 직접 합니다(자동 승격 없음):
317
+ 규칙 승격은 사람이 직접 합니다(자동 승격 없음).
294
318
 
295
319
  ```bash
296
320
  awl rules promote G-003 \
@@ -298,7 +322,7 @@ awl rules promote G-003 \
298
322
  --counter "이미 디자인 토큰 시스템이 없는 프로젝트"
299
323
  ```
300
324
 
301
- 교훈(gotcha, `~/.awl/gotchas`)과 awl 도구 자체 피드백(`~/.awl/records`, `awl record awl-feedback`)은 다른 종류입니다. 전자는 작업 대상 코드에 대한 것이고, 후자는 "awl commit이 이래서 아팠다" 같은 도구 자체의 문제입니다.
325
+ 교훈(gotcha, `~/.awl/gotchas`)과 awl 도구 자체 피드백(`~/.awl/records`, `awl record awl-feedback`)은 다른 종류입니다. 전자는 작업 대상 코드에 대한 것이고, 후자는 "awl commit이 이래서 아팠다" 같은 도구 자체의 문제입니다. `awl feedback-log`로 모아 봅니다.
302
326
 
303
327
  ---
304
328
 
@@ -335,9 +359,6 @@ awl work switch <ID> # 보관된 워크아이템으로 전환
335
359
  **`awl: command not found` (pnpm 설치 후)**
336
360
  `pnpm setup`을 실행하고 터미널을 새로 엽니다. pnpm 전역 bin이 PATH에 없어서입니다.
337
361
 
338
- **`npm install -g agent-work-loop`를 했는데 `awl`이 아무것도 못 한다**
339
- 위 설치 섹션을 참고하세요. 아직 npm에 `0.0.0` placeholder만 배포돼 있습니다. 저장소를 클론해 `pnpm install && pnpm run build && pnpm link --global`로 설치하세요.
340
-
341
362
  **`awl --version`이 엔진 버전 불일치를 경고한다**
342
363
  `awl`을 업그레이드했는데 `~/.awl/engine`은 예전 버전 그대로일 때 나옵니다. `awl update`로 엔진을 갱신하면 맞춰집니다(프로젝트 설정은 안 건드립니다). `awl version-check`로 어긋난 버전 쌍(package.json, 설치된 엔진, 프로젝트 config, 설치된 스킬)을 직접 봅니다. 프로젝트 config가 엔진보다 낡았다면 `awl init --yes`를 다시 실행합니다.
343
364
 
@@ -357,10 +378,7 @@ awl work switch <ID> # 보관된 워크아이템으로 전환
357
378
  `awl init`이 심는 pre-push 훅 때문입니다(위 5분 시작 참고). 실제 터미널에서 사람이 직접 치는 push는 막히지 않습니다 — 막혔다면 비대화형(에이전트, CI, 스크립트)으로 실행된 경우이고, 그때는 `AWL_ALLOW_PUSH=1 git push`를 씁니다.
358
379
 
359
380
  **대형 저장소에서 `awl lane new`/`awl work new --worktree`가 `Could not write new index file`로 실패한다**
360
- `git worktree add`가 기본 180초 안에 못 끝나 강제 종료됐다는 뜻입니다(대형 모노레포는
361
- 체크아웃이 오래 걸립니다) — 디스크 문제가 아닙니다. `AWL_GIT_WORKTREE_TIMEOUT_MS=<ms>`
362
- 환경변수로 값을 늘려 다시 시도하세요(예: `AWL_GIT_WORKTREE_TIMEOUT_MS=300000 awl lane new
363
- <이름>`).
381
+ `git worktree add`가 기본 180초 안에 못 끝나 강제 종료됐다는 뜻입니다(대형 모노레포는 체크아웃이 오래 걸립니다) — 디스크 문제가 아닙니다. `AWL_GIT_WORKTREE_TIMEOUT_MS=<ms>` 환경변수로 값을 늘려 다시 시도하세요(예: `AWL_GIT_WORKTREE_TIMEOUT_MS=300000 awl lane new <이름>`).
364
382
 
365
383
  **게이트에서 스킬이 안 멈추고 그냥 진행한다**
366
384
  스킬 문서가 게이트에서 반드시 도구를 호출하도록 지시돼 있습니다. 계속 안 멈추면 스킬이 최신인지 확인하세요. `awl doctor`가 `claude-skill-vs-engine` 불일치를 경고하면 `awl init --yes`로 재설치합니다.
@@ -370,7 +388,7 @@ awl work switch <ID> # 보관된 워크아이템으로 전환
370
388
 
371
389
  ---
372
390
 
373
- ## 기여 / 라이선스
391
+ ## 기여하기
374
392
 
375
393
  이 저장소는 pnpm을 사용합니다.
376
394
 
@@ -382,8 +400,10 @@ pnpm run lint # biome 검사
382
400
  pnpm run typecheck
383
401
  ```
384
402
 
403
+ PR을 올리면 [GitHub Actions](.github/workflows/ci.yml)가 Node 18/22 두 버전에서 typecheck·lint·test·build를 자동으로 돌립니다.
404
+
385
405
  릴리스는 `pnpm release:patch` / `pnpm release:minor` / `pnpm release:major`로 합니다. 검증·빌드·tarball·`npm publish --dry-run`을 통과한 뒤 버전, CHANGELOG, 커밋, 태그, 원격 push를 한 번에 처리합니다. **`npm publish`만 사람이 실행합니다.**
386
406
 
387
407
  npm에 `awl` 패키지가 이미 있어, 패키지 이름은 `agent-work-loop`로 배포하고 명령어 이름만 `awl`로 씁니다.
388
408
 
389
- 라이선스는 MIT입니다.
409
+ 라이선스는 [MIT](LICENSE)입니다.
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  loadProjectName,
3
3
  readRecords
4
- } from "./chunk-FK3Q7UY2.js";
4
+ } from "./chunk-2NETKDCV.js";
5
5
  import {
6
6
  loadState
7
- } from "./chunk-VE2GVNNS.js";
7
+ } from "./chunk-RTZLAXTZ.js";
8
8
  import {
9
9
  resolveProjectRoot,
10
10
  run
11
- } from "./chunk-MLTY3EE6.js";
12
- import "./chunk-SJG22DAK.js";
11
+ } from "./chunk-FLVRMRHX.js";
12
+ import "./chunk-IPNBHVGX.js";
13
13
  import "./chunk-FGHILN3D.js";
14
14
 
15
15
  // src/commands/brief.ts
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  readRecords
3
- } from "./chunk-FK3Q7UY2.js";
3
+ } from "./chunk-2NETKDCV.js";
4
4
  import {
5
5
  loadState
6
- } from "./chunk-VE2GVNNS.js";
6
+ } from "./chunk-RTZLAXTZ.js";
7
7
  import {
8
8
  resolveProjectRoot
9
- } from "./chunk-MLTY3EE6.js";
10
- import "./chunk-SJG22DAK.js";
9
+ } from "./chunk-FLVRMRHX.js";
10
+ import "./chunk-IPNBHVGX.js";
11
11
  import {
12
12
  caps,
13
13
  signal
@@ -2,11 +2,11 @@ import {
2
2
  getCriterion,
3
3
  loadState,
4
4
  writeState
5
- } from "./chunk-VE2GVNNS.js";
5
+ } from "./chunk-RTZLAXTZ.js";
6
6
  import {
7
7
  resolveProjectRoot,
8
8
  run
9
- } from "./chunk-MLTY3EE6.js";
9
+ } from "./chunk-FLVRMRHX.js";
10
10
  import {
11
11
  caps,
12
12
  makeColors,
@@ -3,14 +3,14 @@ import {
3
3
  getLatestVersionCached,
4
4
  name,
5
5
  version
6
- } from "./chunk-ZEX5ZF6C.js";
6
+ } from "./chunk-HW2OSIB3.js";
7
7
  import {
8
8
  resolveProjectRoot
9
- } from "./chunk-MLTY3EE6.js";
9
+ } from "./chunk-FLVRMRHX.js";
10
10
  import {
11
11
  packageEngineDir,
12
12
  skillsVersionPath
13
- } from "./chunk-SJG22DAK.js";
13
+ } from "./chunk-IPNBHVGX.js";
14
14
  import {
15
15
  caps,
16
16
  installedEngineVersion,
@@ -13,7 +13,7 @@ import {
13
13
  selectSingle,
14
14
  step,
15
15
  verifyStepLines
16
- } from "./chunk-SJG22DAK.js";
16
+ } from "./chunk-IPNBHVGX.js";
17
17
  import {
18
18
  caps,
19
19
  findProjectRoot,
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  readRecords
3
- } from "./chunk-FK3Q7UY2.js";
3
+ } from "./chunk-2NETKDCV.js";
4
4
  import {
5
5
  loadState
6
- } from "./chunk-VE2GVNNS.js";
6
+ } from "./chunk-RTZLAXTZ.js";
7
7
  import {
8
8
  multiProjectFooter,
9
9
  resolveProjectScope,
10
10
  run
11
- } from "./chunk-MLTY3EE6.js";
11
+ } from "./chunk-FLVRMRHX.js";
12
12
  import {
13
13
  WORKTREES_DIR,
14
14
  caps,
@@ -4,7 +4,7 @@ import {
4
4
 
5
5
  // package.json
6
6
  var name = "agent-work-loop";
7
- var version = "0.6.46";
7
+ var version = "0.6.48";
8
8
 
9
9
  // src/core/npm-registry.ts
10
10
  import fs from "fs";
@@ -531,9 +531,38 @@ function ensureGitignore(projectRoot) {
531
531
  fs.writeFileSync(gi, content);
532
532
  return "added";
533
533
  }
534
+ function safetyHookPath(projectRoot) {
535
+ const dotGit = path.join(projectRoot, ".git");
536
+ const dotGitStat = fs.statSync(dotGit);
537
+ if (dotGitStat.isDirectory()) {
538
+ return path.join(dotGit, "hooks", "pre-push");
539
+ }
540
+ if (!dotGitStat.isFile()) {
541
+ throw new Error(`${dotGit} is neither a directory nor a gitdir file`);
542
+ }
543
+ const gitdirMatch = fs.readFileSync(dotGit, "utf8").match(/^gitdir:\s*(.+?)\s*$/m);
544
+ if (!gitdirMatch) {
545
+ throw new Error(`${dotGit} does not contain a gitdir: line`);
546
+ }
547
+ const gitdir = gitdirMatch[1];
548
+ if (!gitdir) {
549
+ throw new Error(`${dotGit} has an empty gitdir: line`);
550
+ }
551
+ const worktreeGitDir = path.resolve(path.dirname(dotGit), gitdir);
552
+ const commonDirFile = path.join(worktreeGitDir, "commondir");
553
+ let commonGitDir = worktreeGitDir;
554
+ if (exists(commonDirFile)) {
555
+ const commonDir = fs.readFileSync(commonDirFile, "utf8").trim();
556
+ if (commonDir === "") {
557
+ throw new Error(`${commonDirFile} is empty`);
558
+ }
559
+ commonGitDir = path.resolve(worktreeGitDir, commonDir);
560
+ }
561
+ return path.join(commonGitDir, "hooks", "pre-push");
562
+ }
534
563
  function installSafetyHook(projectRoot) {
535
564
  try {
536
- const hook = path.join(projectRoot, ".git", "hooks", "pre-push");
565
+ const hook = safetyHookPath(projectRoot);
537
566
  if (exists(hook))
538
567
  return { installed: false, warning: "\uAE30\uC874 pre-push \uD6C5\uC774 \uC788\uC5B4 awl \uD6C5\uC744 \uB36E\uC5B4\uC4F0\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4." };
539
568
  const template = path.join(packageEngineDir(), "templates", "pre-push.sample");
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  computeDurationMs
3
- } from "./chunk-IVEV5L7P.js";
3
+ } from "./chunk-TCIRO4GU.js";
4
4
  import {
5
5
  computeCoverage,
6
6
  readRecords
7
- } from "./chunk-FK3Q7UY2.js";
7
+ } from "./chunk-2NETKDCV.js";
8
8
  import {
9
9
  loadState
10
- } from "./chunk-VE2GVNNS.js";
10
+ } from "./chunk-RTZLAXTZ.js";
11
11
  import {
12
12
  requireConfig
13
- } from "./chunk-MLTY3EE6.js";
13
+ } from "./chunk-FLVRMRHX.js";
14
14
  import {
15
15
  caps,
16
16
  generationsDir,
@@ -1,20 +1,20 @@
1
1
  import {
2
2
  protectedFilesMessage
3
- } from "./chunk-VVZOIFL4.js";
3
+ } from "./chunk-RTALSPLK.js";
4
4
  import {
5
5
  gitDirtyFiles
6
- } from "./chunk-DA27NBCS.js";
6
+ } from "./chunk-KKFXZ3NV.js";
7
7
  import {
8
8
  applyVerificationAttempts,
9
9
  loadState,
10
10
  writeState
11
- } from "./chunk-VE2GVNNS.js";
11
+ } from "./chunk-RTZLAXTZ.js";
12
12
  import {
13
13
  CommandNotFoundError,
14
14
  VERIFY_ORDER,
15
15
  requireConfig,
16
16
  run
17
- } from "./chunk-MLTY3EE6.js";
17
+ } from "./chunk-FLVRMRHX.js";
18
18
  import {
19
19
  caps,
20
20
  makeColors,
@@ -4,37 +4,37 @@ import {
4
4
  } from "./chunk-D5OINC3G.js";
5
5
  import {
6
6
  parseRuleFile
7
- } from "./chunk-EHJYX57P.js";
7
+ } from "./chunk-OQJIRLKN.js";
8
8
  import {
9
9
  nextGotchaId
10
- } from "./chunk-ZRKOZQKP.js";
10
+ } from "./chunk-ITZ52UYF.js";
11
11
  import {
12
12
  buildVerifyBaseline,
13
13
  isCheckPassed,
14
14
  runVerifyChecks,
15
15
  writeVerifyBaseline
16
- } from "./chunk-WWZIKKHP.js";
16
+ } from "./chunk-IUXE7EG3.js";
17
17
  import {
18
18
  gitBranch
19
- } from "./chunk-DA27NBCS.js";
19
+ } from "./chunk-KKFXZ3NV.js";
20
20
  import {
21
21
  loadProjectName
22
- } from "./chunk-FK3Q7UY2.js";
22
+ } from "./chunk-2NETKDCV.js";
23
23
  import {
24
24
  loadState,
25
25
  migrateState,
26
26
  writeState
27
- } from "./chunk-VE2GVNNS.js";
27
+ } from "./chunk-RTZLAXTZ.js";
28
28
  import {
29
29
  loadConfig,
30
30
  multiProjectFooter,
31
31
  resolveProjectRoot,
32
32
  resolveProjectScope,
33
33
  run
34
- } from "./chunk-MLTY3EE6.js";
34
+ } from "./chunk-FLVRMRHX.js";
35
35
  import {
36
36
  installClaudeSkill
37
- } from "./chunk-SJG22DAK.js";
37
+ } from "./chunk-IPNBHVGX.js";
38
38
  import {
39
39
  caps,
40
40
  feedback,
@@ -1,25 +1,25 @@
1
1
  import {
2
2
  loadProjectName,
3
3
  readRecords
4
- } from "./chunk-FK3Q7UY2.js";
4
+ } from "./chunk-2NETKDCV.js";
5
5
  import {
6
6
  loadState,
7
7
  readStateLock
8
- } from "./chunk-VE2GVNNS.js";
8
+ } from "./chunk-RTZLAXTZ.js";
9
9
  import {
10
10
  gatherVersionInputs
11
- } from "./chunk-XWMS6Z5E.js";
11
+ } from "./chunk-AVIUMBJZ.js";
12
12
  import {
13
13
  checkVersions
14
- } from "./chunk-ZEX5ZF6C.js";
14
+ } from "./chunk-HW2OSIB3.js";
15
15
  import {
16
16
  CommandNotFoundError,
17
17
  run,
18
18
  tokenize
19
- } from "./chunk-MLTY3EE6.js";
19
+ } from "./chunk-FLVRMRHX.js";
20
20
  import {
21
21
  listRegisteredProjects
22
- } from "./chunk-SJG22DAK.js";
22
+ } from "./chunk-IPNBHVGX.js";
23
23
  import {
24
24
  caps,
25
25
  clipToWidth,
@@ -2,7 +2,7 @@ import {
2
2
  acquireLock,
3
3
  loadGotchaList,
4
4
  releaseLock
5
- } from "./chunk-ZRKOZQKP.js";
5
+ } from "./chunk-ITZ52UYF.js";
6
6
  import {
7
7
  caps,
8
8
  makeColors,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  run
3
- } from "./chunk-MLTY3EE6.js";
3
+ } from "./chunk-FLVRMRHX.js";
4
4
 
5
5
  // src/core/protected-files.ts
6
6
  async function changedProtectedFiles(projectRoot, protectedFiles) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  resolveProjectRoot
3
- } from "./chunk-MLTY3EE6.js";
3
+ } from "./chunk-FLVRMRHX.js";
4
4
  import {
5
5
  caps,
6
6
  makeColors,
@@ -232,13 +232,16 @@ function runStateSet(jsonPatch, opts = {}) {
232
232
  process.exit(1);
233
233
  }
234
234
  let gateRejected = false;
235
+ let workitemRejected = false;
236
+ let activeWorkitem;
235
237
  try {
236
238
  const current = loadState(root);
237
- if (p.phase === "loop" && opts.requireGateForLoop) {
238
- const workitem = typeof current.workitem === "string" ? current.workitem : void 0;
239
- gateRejected = !opts.requireGateForLoop(workitem);
239
+ activeWorkitem = typeof current.workitem === "string" ? current.workitem : void 0;
240
+ workitemRejected = opts.workitem !== void 0 && activeWorkitem !== opts.workitem;
241
+ if (!workitemRejected && p.phase === "loop" && opts.requireGateForLoop) {
242
+ gateRejected = !opts.requireGateForLoop(activeWorkitem);
240
243
  }
241
- if (!gateRejected) {
244
+ if (!workitemRejected && !gateRejected) {
242
245
  const merged = mergeState(current, p);
243
246
  writeState(root, merged);
244
247
  process.stdout.write(`${JSON.stringify(merged, null, 2)}
@@ -247,6 +250,14 @@ function runStateSet(jsonPatch, opts = {}) {
247
250
  } finally {
248
251
  releaseStateLock(root, token);
249
252
  }
253
+ if (workitemRejected) {
254
+ process.stderr.write(
255
+ `
256
+ \uD65C\uC131 \uC6CC\uD06C\uC544\uC774\uD15C\uC774 ${activeWorkitem ?? "(\uC5C6\uC74C)"}\uC73C\uB85C \uBC14\uB00C\uC5B4 \uC788\uC5B4 ${opts.workitem} \uB300\uC0C1 \uAC31\uC2E0\uC744 \uAC70\uBD80\uD588\uC2B5\uB2C8\uB2E4.
257
+ `
258
+ );
259
+ process.exit(1);
260
+ }
250
261
  if (gateRejected) {
251
262
  process.stderr.write(
252
263
  "\n \uAC8C\uC774\uD2B8 1 \uC2B9\uC778 \uAE30\uB85D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. awl record gate \uB85C \uACC4\uD68D\uC744 \uC2B9\uC778(approved)\uD55C \uB4A4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.\n"
@@ -3,7 +3,7 @@ import {
3
3
  multiProjectFooter,
4
4
  requireConfig,
5
5
  resolveProjectScope
6
- } from "./chunk-MLTY3EE6.js";
6
+ } from "./chunk-FLVRMRHX.js";
7
7
  import {
8
8
  caps,
9
9
  generationsDir,
@@ -5,18 +5,18 @@ import {
5
5
  runWorkNew,
6
6
  sanitizeForGit,
7
7
  worktreeDirtyTracked
8
- } from "./chunk-AGFQC2UO.js";
8
+ } from "./chunk-JY3DXRXV.js";
9
9
  import {
10
10
  loadProjectName
11
- } from "./chunk-FK3Q7UY2.js";
11
+ } from "./chunk-2NETKDCV.js";
12
12
  import {
13
13
  loadState,
14
14
  writeState
15
- } from "./chunk-VE2GVNNS.js";
15
+ } from "./chunk-RTZLAXTZ.js";
16
16
  import {
17
17
  resolveProjectRoot,
18
18
  run
19
- } from "./chunk-MLTY3EE6.js";
19
+ } from "./chunk-FLVRMRHX.js";
20
20
  import {
21
21
  WORKTREES_DIR,
22
22
  caps,