lazyclaude-ai 0.1.1 → 0.1.3

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.
package/README.md CHANGED
@@ -1,19 +1,48 @@
1
- # LazyClaude
2
-
3
- LazyClaude is a Claude Code-native retrofit of the LazyCodex workflow style:
4
- simple local activation, prompt-triggered ultrawork discipline,
5
- planner/executor/reviewer agents, lifecycle hooks, MCP scaffolding, and
6
- LSP-backed code checks.
7
-
8
- The current public package is `lazyclaude-ai@0.1.0`, and this checkout prepares
9
- the next `0.1.1` patch for personal install convenience. The repo can remain
10
- private and quiet; publishing to npm here does not imply public repo promotion,
11
- marketplace publication, or advertisement. Future package releases still require
12
- explicit user approval.
13
-
14
- ## Quick Install
15
-
16
- Use the npm package from any machine:
1
+ <p align="center"><img src="./cover.png" width="100%" /></p>
2
+
3
+ <h1 align="center">LazyClaude</h1>
4
+ <p align="center">
5
+ <em>Claude Code-native LazyCodex workflows: prompt hooks, ultrawork skills, agents, MCP, and LSP helpers.</em>
6
+ </p>
7
+ <p align="center">
8
+ <a href="#quick-start">Quick Start</a> · <a href="#ulw-usage">ULW Usage</a> · <a href="#commands">Commands</a> · <a href="./README_ko-KR.md">한국어</a>
9
+ </p>
10
+ <p align="center">
11
+ <img src="https://img.shields.io/badge/npm-lazyclaude--ai-cb3837" />
12
+ <img src="https://img.shields.io/badge/version-0.1.3-2ea44f" />
13
+ <img src="https://img.shields.io/badge/Claude%20Code-plugin-blueviolet" />
14
+ <img src="https://img.shields.io/badge/license-MIT-blue" />
15
+ </p>
16
+
17
+ ---
18
+
19
+ > [!NOTE]
20
+ > LazyClaude is a quiet personal distribution for bringing LazyCodex-style prompt engineering into Claude Code. It installs as `lazyclaude@lazyclaude-ai`, so normal `claude` launches can load the LazyClaude skills and hooks without a long `--plugin-dir` command.
21
+
22
+ The current public package is `lazyclaude-ai@0.1.3` for personal install
23
+ convenience. The repo can remain private and quiet; publishing to npm here does
24
+ not imply public repo promotion, marketplace publication, or advertisement.
25
+ Future package releases still require explicit user approval.
26
+
27
+ ## Features
28
+
29
+ - **Natural Claude launch** - install once with `npx`, then run plain `claude`
30
+ - **ULW prompt hooks** - type `ulw`, `ultrawork`, `$ulw-plan`, or `$ulw-loop`
31
+ - **Native goal guidance** - ULW context points Claude toward `/goal` or
32
+ model-facing `get_goal`, `create_goal`, and delayed `update_goal` when those
33
+ surfaces exist
34
+ - **Dynamic workflow/worktree guidance** - large or parallel tasks are steered
35
+ toward Claude Code Dynamic workflow orchestration and Dynamic worktree
36
+ isolation
37
+ - **Claude skills** - `ulw-plan`, `ulw-loop`, `start-work`, `rules`, `lsp`, `programming`, and `review-work`
38
+ - **Workflow agents** - planner, executor, verifier, reviewer, librarian, and QA runner
39
+ - **Local marketplace registration** - Claude can resolve `claude plugin details lazyclaude@lazyclaude-ai`
40
+ - **MCP and LSP helpers** - plugin-local stdio MCP plus TypeScript-family LSP doctor
41
+ - **Safe uninstall** - removes only LazyClaude-managed state
42
+
43
+ ## Quick Start
44
+
45
+ ### Install
17
46
 
18
47
  ```bash
19
48
  npx --yes lazyclaude-ai install
@@ -31,7 +60,7 @@ Launch Claude Code normally:
31
60
  claude
32
61
  ```
33
62
 
34
- Alternative entrypoints:
63
+ ### Alternative Entrypoints
35
64
 
36
65
  ```bash
37
66
  bunx lazyclaude-ai install
@@ -52,41 +81,26 @@ Remove only LazyClaude-managed install state:
52
81
  npx --yes lazyclaude-ai uninstall
53
82
  ```
54
83
 
55
- The installer registers `lazyclaude@lazyclaude-ai` in Claude Code's user plugin
56
- registry under `~/.claude/plugins` and enables it in `settings.json`
57
- `enabledPlugins`, so you do not need to type a long `--plugin-dir` command every
58
- time. Set `CLAUDE_CONFIG_DIR=/some/path` and `LAZYCLAUDE_HOME=/some/path` only
59
- for isolated tests.
60
-
61
- ## Local Development
62
-
63
- Use the plugin directly from this checkout while editing it:
64
-
65
- ```bash
66
- claude --plugin-dir ./plugins/lazyclaude
67
- ```
68
-
69
- Inside Claude Code, reload local plugin metadata after edits:
84
+ ## How Install Works
70
85
 
71
- ```text
72
- /reload-plugins
73
- ```
86
+ The installer registers `lazyclaude@lazyclaude-ai` in Claude Code's user plugin
87
+ registry under `~/.claude/plugins`, enables it in `settings.json`
88
+ `enabledPlugins`, and writes a LazyClaude-managed local marketplace entry in
89
+ `known_marketplaces.json`. That marketplace metadata is what lets Claude show
90
+ the skill and hook inventory in `claude plugin details`.
74
91
 
75
- If an OMC/omc Claude plugin is installed, do not co-load it with LazyClaude
76
- while testing this MVP. LazyClaude is loaded directly by `--plugin-dir` and does
77
- not ship a root Claude marketplace skeleton. If user-level OMC creates a local
78
- `.omc/` state directory during smoke tests, LazyClaude ignores and excludes that
79
- directory from git and npm package surfaces.
92
+ Use `CLAUDE_CONFIG_DIR=/some/path` and `LAZYCLAUDE_HOME=/some/path` only for
93
+ isolated tests.
80
94
 
81
95
  ## ULW Usage
82
96
 
83
- LazyClaude is available in normal Claude Code sessions after install:
97
+ After install, start Claude Code:
84
98
 
85
99
  ```bash
86
100
  claude
87
101
  ```
88
102
 
89
- Then type one of these prompts in Claude Code:
103
+ Then type one of these prompts:
90
104
 
91
105
  ```text
92
106
  ulw
@@ -98,12 +112,20 @@ $start-work plans/lazyclaude-retrofit.md
98
112
 
99
113
  Expected behavior: LazyClaude's prompt hook adds `ULTRAWORK MODE ENABLED`
100
114
  context, then the matching skill and agent instructions steer Claude toward
101
- test-first work, manual QA evidence, cleanup receipts, and explicit approval
102
- before future release steps.
115
+ test-first work, native goal handling, manual QA evidence, cleanup receipts,
116
+ and explicit approval before future release steps.
117
+
118
+ LazyClaude does not auto-type `/goal` or send slash command text for you.
119
+ Instead, the hook and ULW skills add run-context guidance: use Claude Code's
120
+ native goal surface when available, inspect `get_goal`, call `create_goal` only
121
+ when no matching goal is active, and reserve `update_goal` for verified
122
+ completion or a genuine blocker. For large independent work, the guidance also
123
+ points Claude toward Dynamic workflow orchestration and Dynamic worktree
124
+ isolation.
103
125
 
104
126
  Plain `ulw` is hook context activation, so it may not show a separate Skill
105
127
  tool invocation in Claude Code history. Use visible namespaced commands when
106
- you want explicit LazyClaude skill or command activation:
128
+ you want explicit LazyClaude skill activation:
107
129
 
108
130
  ```text
109
131
  /lazyclaude:ulw-loop <what you want executed with evidence>
@@ -113,14 +135,14 @@ you want explicit LazyClaude skill or command activation:
113
135
 
114
136
  ## Commands
115
137
 
116
- ```bash
117
- npx --yes lazyclaude-ai install
118
- npx --yes lazyclaude-ai doctor
119
- npx --yes lazyclaude-ai path
120
- npx --yes lazyclaude-ai run -- --help
121
- npx --yes lazyclaude-ai update
122
- npx --yes lazyclaude-ai uninstall
123
- ```
138
+ | Command | Purpose |
139
+ | --- | --- |
140
+ | `npx --yes lazyclaude-ai install` | Install and enable the Claude Code plugin |
141
+ | `npx --yes lazyclaude-ai doctor` | Validate files, Claude plugin validation, and plugin details |
142
+ | `npx --yes lazyclaude-ai path` | Print the installed Claude plugin path |
143
+ | `npx --yes lazyclaude-ai run -- --help` | Run plain `claude` after verifying install state |
144
+ | `npx --yes lazyclaude-ai update` | Reinstall the current package version |
145
+ | `npx --yes lazyclaude-ai uninstall` | Remove LazyClaude-managed install state |
124
146
 
125
147
  The package alias can be inspected without installing anything globally:
126
148
 
@@ -129,6 +151,27 @@ node bin/lazyclaude-ai.js --dry-run install
129
151
  node bin/lazyclaude-ai.js --dry-run doctor
130
152
  ```
131
153
 
154
+ ## Local Development
155
+
156
+ Use the plugin directly from this checkout while editing it:
157
+
158
+ ```bash
159
+ claude --plugin-dir ./plugins/lazyclaude
160
+ ```
161
+
162
+ Inside Claude Code, reload local plugin metadata after edits:
163
+
164
+ ```text
165
+ /reload-plugins
166
+ ```
167
+
168
+ If an OMC/omc Claude plugin is installed, do not co-load it with LazyClaude
169
+ while testing. Local checkout tests use `--plugin-dir`, and npm installs create
170
+ only a LazyClaude-managed local marketplace under the user's LazyClaude home.
171
+ The checkout does not ship a root Claude marketplace skeleton. If user-level
172
+ OMC creates a local `.omc/` state directory during smoke tests, LazyClaude
173
+ ignores and excludes that directory from git and npm package surfaces.
174
+
132
175
  ## Verification
133
176
 
134
177
  ```bash
@@ -145,30 +188,25 @@ version probe evidence instead of failing mysteriously.
145
188
 
146
189
  ## Safety Model
147
190
 
148
- LazyClaude is intentionally local-first:
149
-
150
191
  - Hooks read Claude Code event JSON from stdin and return bounded JSON context.
151
192
  - Hooks do not execute user prompt text.
152
- - The ultrawork prompt hook returns constant guidance and does not echo prompt
153
- text back into the context.
154
- - Any `.omc/`, `.omo/`, or `evidence/` local state is ignored and is not
155
- included in the LazyClaude npm package.
193
+ - The ultrawork prompt hook returns constant guidance and does not echo prompt text.
194
+ - `.omc/`, `.omo/`, and `evidence/` local state are ignored and excluded from the npm package.
156
195
  - The planner agent is read-only and has no edit tools.
157
196
  - MCP and LSP helpers are local stdio commands.
158
- - Future publication, remote marketplace mutation, and package release all
159
- require explicit user approval.
160
-
161
- ## MVP Scope
162
-
163
- - Package/bin: `lazyclaude-ai`
164
- - Friendly bin alias: `lazyclaude`
165
- - Plugin namespace: `lazyclaude`
166
- - Claude Code platform: `claude-code`
167
- - Skills: ultrawork planning, ultrawork loop, start-work, rules, LSP,
168
- programming, review-work
169
- - Agents: planner, executor, verifier, reviewer, librarian, QA runner
170
- - Hooks: session-start, user-prompt-submit, post-tool-use, post-compact
171
- - Config: local MCP server and TypeScript-family LSP doctor
197
+ - Future publication, remote marketplace mutation, and package releases require explicit user approval.
198
+
199
+ ## Project Map
200
+
201
+ | Surface | Path |
202
+ | --- | --- |
203
+ | CLI | `bin/lazyclaude-ai.js` |
204
+ | Claude plugin | `plugins/lazyclaude/` |
205
+ | Skills | `plugins/lazyclaude/skills/` |
206
+ | Agents | `plugins/lazyclaude/agents/` |
207
+ | Hooks | `plugins/lazyclaude/hooks/hooks.json` |
208
+ | MCP | `plugins/lazyclaude/.mcp.json` |
209
+ | LSP | `plugins/lazyclaude/.lsp.json` |
172
210
 
173
211
  See `README_ko-KR.md` for Korean setup notes, `REFERENCE.md` for the pinned
174
212
  LazyCodex source, and `docs/migration.md` for the Codex-to-Claude migration
package/README_ko-KR.md CHANGED
@@ -1,20 +1,52 @@
1
- # LazyClaude
2
-
3
- LazyClaude는 LazyCodex 스타일의 작업 흐름을 Claude Code에 맞게 옮긴
4
- 개인용 배포판입니다. 간단한 프롬프트 트리거, ultrawork 규칙, 플래너와
5
- 실행자 에이전트, hooks, MCP scaffold, LSP doctor를 Claude Code 플러그인
6
- 형태로 제공합니다.
7
-
8
- 현재 공개 npm 패키지는 `lazyclaude-ai@0.1.0`이고, checkout은 다음
9
- `0.1.1` 패치 배포 후보를 준비합니다. 목적은 다른 PC에서도 빠르게
10
- 설치하기 위한 개인용 배포물입니다. 저장소는 비공개 저장소로 유지할 수
11
- 있고, npm 배포가 곧 홍보, 공개 저장소 운영, Claude marketplace 등록을
12
- 의미하지는 않습니다. 새 버전 배포는 항상 별도의 명시적 승인 후에
1
+ <p align="center"><img src="./cover.png" width="100%" /></p>
2
+
3
+ <h1 align="center">LazyClaude</h1>
4
+ <p align="center">
5
+ <em>Claude Code 안에서 쓰는 LazyCodex 스타일 워크플로우: prompt hook, ultrawork skill, agent, MCP, LSP helper.</em>
6
+ </p>
7
+ <p align="center">
8
+ <a href="#빠른-시작">빠른 시작</a> · <a href="#ulw-사용법">ULW 사용법</a> · <a href="#명령어">명령어</a> · <a href="./README.md">English</a>
9
+ </p>
10
+ <p align="center">
11
+ <img src="https://img.shields.io/badge/npm-lazyclaude--ai-cb3837" />
12
+ <img src="https://img.shields.io/badge/version-0.1.3-2ea44f" />
13
+ <img src="https://img.shields.io/badge/Claude%20Code-plugin-blueviolet" />
14
+ <img src="https://img.shields.io/badge/license-MIT-blue" />
15
+ </p>
16
+
17
+ ---
18
+
19
+ [English](./README.md) | **한국어**
20
+
21
+ ---
22
+
23
+ > [!NOTE]
24
+ > LazyClaude는 LazyCodex 스타일의 prompt engineering을 Claude Code로 가져오기 위한 조용한 개인용 배포판입니다. `lazyclaude@lazyclaude-ai`로 설치되므로, 매번 긴 `--plugin-dir` 없이 일반 `claude` 실행에서 LazyClaude skill과 hook을 불러올 수 있습니다.
25
+
26
+ 현재 공개 npm 패키지는 `lazyclaude-ai@0.1.3`입니다. 목적은 다른 PC에서도
27
+ 빠르게 설치하기 위한 개인용 배포물입니다. 저장소는 비공개 저장소로
28
+ 유지할 수 있고, npm 배포가 곧 홍보, 공개 저장소 운영, Claude marketplace
29
+ 등록을 의미하지는 않습니다. 새 버전 배포는 항상 별도의 명시적 승인 후에
13
30
  진행합니다.
14
31
 
15
- ## 빠른 설치
32
+ ## 기능
16
33
 
17
- 환경에서는 아래줄을 사용합니다.
34
+ - **자연스러운 Claude 실행** - `npx`로 설치한 뒤에는 plain `claude`
35
+ - **ULW prompt hook** - `ulw`, `ultrawork`, `$ulw-plan`, `$ulw-loop` 트리거
36
+ - **Native goal guidance** - Claude Code가 `/goal` 또는 `get_goal`,
37
+ `create_goal`, `update_goal` 같은 goal surface를 제공하면 이를 우선
38
+ 사용하도록 유도
39
+ - **Dynamic workflow/worktree guidance** - 크거나 병렬적인 작업은 Claude
40
+ Code Dynamic workflow와 Dynamic worktree 격리 쪽으로 유도
41
+ - **Claude skills** - `ulw-plan`, `ulw-loop`, `start-work`, `rules`, `lsp`, `programming`, `review-work`
42
+ - **워크플로우 agents** - planner, executor, verifier, reviewer, librarian, QA runner
43
+ - **Local marketplace 등록** - `claude plugin details lazyclaude@lazyclaude-ai`에서 inventory 확인
44
+ - **MCP와 LSP helper** - plugin-local stdio MCP와 TypeScript 계열 LSP doctor
45
+ - **안전한 uninstall** - LazyClaude가 관리한 상태만 제거
46
+
47
+ ## 빠른 시작
48
+
49
+ ### 설치
18
50
 
19
51
  ```bash
20
52
  npx --yes lazyclaude-ai install
@@ -32,7 +64,7 @@ npx --yes lazyclaude-ai doctor
32
64
  claude
33
65
  ```
34
66
 
35
- 대체 진입점은 다음과 같습니다.
67
+ ### 대체 진입점
36
68
 
37
69
  ```bash
38
70
  bunx lazyclaude-ai install
@@ -53,32 +85,16 @@ LazyClaude가 관리한 설치 상태만 제거하려면 다음을 사용합니
53
85
  npx --yes lazyclaude-ai uninstall
54
86
  ```
55
87
 
56
- installer는 `lazyclaude@lazyclaude-ai`를 Claude Code user plugin registry
57
- 아래의 `~/.claude/plugins`에 등록하고 `settings.json`의 `enabledPlugins`에
58
- 활성 상태를 기록합니다. 따라서 매번 긴 `--plugin-dir` 명령을 입력할
59
- 필요가 없습니다. 격리 테스트가 필요할 때만
60
- `CLAUDE_CONFIG_DIR=/some/path`와 `LAZYCLAUDE_HOME=/some/path`를 지정하세요.
61
-
62
- ## 로컬 개발
63
-
64
- 이 저장소에서 바로 플러그인을 테스트할 때는 다음처럼 실행합니다.
65
-
66
- ```bash
67
- claude --plugin-dir ./plugins/lazyclaude
68
- ```
69
-
70
- Claude Code 안에서 플러그인 메타데이터를 다시 읽고 싶으면 다음 명령을
71
- 사용합니다.
88
+ ## 설치 방식
72
89
 
73
- ```text
74
- /reload-plugins
75
- ```
90
+ installer는 `lazyclaude@lazyclaude-ai`를 Claude Code user plugin registry
91
+ 아래의 `~/.claude/plugins`에 등록하고 `settings.json`의 `enabledPlugins`,
92
+ LazyClaude 전용 local marketplace 항목, `known_marketplaces.json`을 함께
93
+ 기록합니다. 이 marketplace metadata가 있어야 Claude가
94
+ `claude plugin details`에서 skill과 hook inventory를 해석합니다.
76
95
 
77
- 기존 OMC/omc Claude 플러그인이 설치되어 있다면 MVP 테스트 중에는
78
- LazyClaude와 함께 co-load하지 않는 것을 권장합니다. LazyClaude는
79
- `--plugin-dir`로 직접 로드하며 root Claude marketplace skeleton을 배포하지
80
- 않습니다. user-level OMC가 검증 중 `.omc/` 상태 디렉터리를 만들더라도
81
- LazyClaude는 이를 git 및 npm package surface에서 제외합니다.
96
+ 격리 테스트가 필요할 때만 `CLAUDE_CONFIG_DIR=/some/path`와
97
+ `LAZYCLAUDE_HOME=/some/path`를 지정하세요.
82
98
 
83
99
  ## ULW 사용법
84
100
 
@@ -99,13 +115,21 @@ $start-work plans/lazyclaude-retrofit.md
99
115
  ```
100
116
 
101
117
  기대 동작은 LazyClaude prompt hook이 `ULTRAWORK MODE ENABLED` 컨텍스트를
102
- 추가하고, skill 및 agent 지시문이 Claude를 test-first 작업, 실제 수동 QA
103
- 증거, cleanup receipt, 향후 release step의 명시적 승인 쪽으로 유도하는
104
- 것입니다.
118
+ 추가하고, skill 및 agent 지시문이 Claude를 test-first 작업, native goal
119
+ 처리, 실제 수동 QA 증거, cleanup receipt, 향후 release step의 명시적 승인
120
+ 쪽으로 유도하는 것입니다.
121
+
122
+ LazyClaude는 `/goal`을 대신 입력하거나 slash command text를 자동 전송하지
123
+ 않습니다. 대신 hook과 ULW skill이 run context를 보강합니다. Claude Code의
124
+ native goal surface가 있으면 `get_goal`을 먼저 확인하고, matching active
125
+ goal이 없을 때만 `create_goal`을 호출하며, `update_goal`은 검증 완료 또는
126
+ 진짜 blocker가 있을 때까지 미루도록 안내합니다. 큰 독립 작업에서는 Dynamic
127
+ workflow orchestration과 Dynamic worktree isolation도 함께 고려하도록
128
+ 유도합니다.
105
129
 
106
130
  단순히 `ulw`라고 입력하는 것은 hook context activation이므로 Claude Code
107
131
  history에 별도 Skill tool invocation으로 보이지 않을 수 있습니다. 명시적인
108
- LazyClaude command activation을 보고 싶다면 namespaced command를 사용합니다.
132
+ LazyClaude skill activation을 보고 싶다면 namespaced command를 사용합니다.
109
133
 
110
134
  ```text
111
135
  /lazyclaude:ulw-loop <증거 기반으로 실행할 작업>
@@ -115,14 +139,14 @@ LazyClaude command activation을 보고 싶다면 namespaced command를 사용
115
139
 
116
140
  ## 명령어
117
141
 
118
- ```bash
119
- npx --yes lazyclaude-ai install
120
- npx --yes lazyclaude-ai doctor
121
- npx --yes lazyclaude-ai path
122
- npx --yes lazyclaude-ai run -- --help
123
- npx --yes lazyclaude-ai update
124
- npx --yes lazyclaude-ai uninstall
125
- ```
142
+ | 명령어 | 용도 |
143
+ | --- | --- |
144
+ | `npx --yes lazyclaude-ai install` | Claude Code plugin 설치 및 활성화 |
145
+ | `npx --yes lazyclaude-ai doctor` | 파일, Claude validation, plugin details 검증 |
146
+ | `npx --yes lazyclaude-ai path` | 설치된 Claude plugin 경로 출력 |
147
+ | `npx --yes lazyclaude-ai run -- --help` | 설치 상태를 확인한 뒤 plain `claude` 실행 |
148
+ | `npx --yes lazyclaude-ai update` | 현재 package version 재설치 |
149
+ | `npx --yes lazyclaude-ai uninstall` | LazyClaude가 관리한 설치 상태 제거 |
126
150
 
127
151
  전역 설치 없이 checkout 내부 bin을 확인할 수도 있습니다.
128
152
 
@@ -131,6 +155,29 @@ node bin/lazyclaude-ai.js --dry-run install
131
155
  node bin/lazyclaude-ai.js --dry-run doctor
132
156
  ```
133
157
 
158
+ ## 로컬 개발
159
+
160
+ 이 저장소에서 바로 플러그인을 테스트할 때는 다음처럼 실행합니다.
161
+
162
+ ```bash
163
+ claude --plugin-dir ./plugins/lazyclaude
164
+ ```
165
+
166
+ Claude Code 안에서 플러그인 메타데이터를 다시 읽고 싶으면 다음 명령을
167
+ 사용합니다.
168
+
169
+ ```text
170
+ /reload-plugins
171
+ ```
172
+
173
+ 기존 OMC/omc Claude 플러그인이 설치되어 있다면 테스트 중에는 LazyClaude와
174
+ 함께 co-load하지 않는 것을 권장합니다. 로컬 checkout 테스트는
175
+ `--plugin-dir`로 직접 로드하고, npm 설치는 사용자 홈 아래에 LazyClaude가
176
+ 관리하는 local marketplace만 만듭니다. root Claude marketplace skeleton은
177
+ 저장소 checkout에 배포하지 않습니다. user-level OMC가 검증 중 `.omc/`
178
+ 상태 디렉터리를 만들더라도 LazyClaude는 이를 git 및 npm package surface에서
179
+ 제외합니다.
180
+
134
181
  ## 검증
135
182
 
136
183
  ```bash
@@ -147,29 +194,25 @@ probe evidence를 남깁니다.
147
194
 
148
195
  ## 안전 모델
149
196
 
150
- - Hook은 Claude Code event JSON을 stdin으로 읽고 제한된 JSON context를
151
- 반환합니다.
197
+ - Hook은 Claude Code event JSON을 stdin으로 읽고 제한된 JSON context를 반환합니다.
152
198
  - Hook은 사용자 프롬프트 텍스트를 실행하지 않습니다.
153
- - ultrawork prompt hook은 고정된 guidance를 반환하며 prompt text를 다시
154
- echo하지 않습니다.
155
- - `.omc/`, `.omo/`, `evidence/` 같은 로컬 상태는 ignore되며 npm 패키지에
156
- 포함되지 않습니다.
199
+ - ultrawork prompt hook은 고정된 guidance를 반환하며 prompt text를 다시 echo하지 않습니다.
200
+ - `.omc/`, `.omo/`, `evidence/` 같은 로컬 상태는 ignore되며 npm 패키지에 포함되지 않습니다.
157
201
  - planner agent는 read-only이며 edit tool을 갖지 않습니다.
158
202
  - MCP와 LSP helper는 local stdio command입니다.
159
- - 향후 publish, remote marketplace 변경, 새 package release는 모두 명시적
160
- 승인 후 진행합니다.
161
-
162
- ## 범위
163
-
164
- - Package/bin: `lazyclaude-ai`
165
- - Friendly bin alias: `lazyclaude`
166
- - Plugin namespace: `lazyclaude`
167
- - Claude Code platform: `claude-code`
168
- - Skills: ultrawork planning, ultrawork loop, start-work, rules, LSP,
169
- programming, review-work
170
- - Agents: planner, executor, verifier, reviewer, librarian, QA runner
171
- - Hooks: session-start, user-prompt-submit, post-tool-use, post-compact
172
- - Config: local MCP server and TypeScript-family LSP doctor
203
+ - 향후 publish, remote marketplace 변경, 새 package release는 모두 명시적 승인 후 진행합니다.
204
+
205
+ ## 프로젝트 지도
206
+
207
+ | Surface | Path |
208
+ | --- | --- |
209
+ | CLI | `bin/lazyclaude-ai.js` |
210
+ | Claude plugin | `plugins/lazyclaude/` |
211
+ | Skills | `plugins/lazyclaude/skills/` |
212
+ | Agents | `plugins/lazyclaude/agents/` |
213
+ | Hooks | `plugins/lazyclaude/hooks/hooks.json` |
214
+ | MCP | `plugins/lazyclaude/.mcp.json` |
215
+ | LSP | `plugins/lazyclaude/.lsp.json` |
173
216
 
174
217
  영문 설명은 `README.md`, LazyCodex 출처 고정 정보는 `REFERENCE.md`,
175
218
  Codex-to-Claude 변환 표는 `docs/migration.md`를 참고하세요.
@@ -1,9 +1,7 @@
1
1
  # LazyClaude Release Checklist
2
2
 
3
- Status: quiet public npm package `lazyclaude-ai@0.1.0` is published for
4
- personal install convenience after explicit user approval. This checkout is
5
- prepared as the next `0.1.1` patch candidate until the user explicitly approves
6
- another publish.
3
+ Status: quiet public npm package `lazyclaude-ai@0.1.3` is published for
4
+ personal install convenience after explicit user approval.
7
5
 
8
6
  DO NOT publish a new version of LazyClaude, run `npm publish`, push release
9
7
  tags, or add a remote Claude Code marketplace entry without explicit user
@@ -40,9 +38,10 @@ Use this track after an approved npm package version exists:
40
38
  3. Start Claude Code with the normal `claude` command.
41
39
 
42
40
  The installer should register `lazyclaude@lazyclaude-ai` in Claude Code's user
43
- plugin registry and `settings.json` `enabledPlugins`. Do not ask users to type a
44
- generated command that shells out to `npx --yes lazyclaude-ai path` for normal
45
- npm installs.
41
+ plugin registry, `settings.json` `enabledPlugins`, and the LazyClaude-managed
42
+ local marketplace metadata needed by `claude plugin details`. Do not ask users
43
+ to type a generated command that shells out to `npx --yes lazyclaude-ai path` for
44
+ normal npm installs.
46
45
 
47
46
  ## Quiet Public NPM Package Release Or Update
48
47
 
@@ -92,10 +91,13 @@ rollback action with user approval:
92
91
  Local checkout testing still uses `claude --plugin-dir ./plugins/lazyclaude`.
93
92
  NPM installs should use the global Claude user plugin registry and normal
94
93
  `claude` launches. A root Claude marketplace file is intentionally not shipped
95
- because users may already have an OMC/omc marketplace or plugin installed. Do
96
- not add a local or remote marketplace entry without explicit user approval. If
97
- user-level OMC creates `.omc/` state during validation, keep it ignored and
98
- confirm it is absent from the package dry-run.
94
+ because users may already have an OMC/omc marketplace or plugin installed. The
95
+ npm installer creates a LazyClaude-managed local marketplace under
96
+ `LAZYCLAUDE_HOME` and registers only `lazyclaude-ai` in Claude's user marketplace
97
+ metadata so skills and hooks appear in `claude plugin details`. Do not add a
98
+ remote marketplace entry without explicit user approval. If user-level OMC
99
+ creates `.omc/` state during validation, keep it ignored and confirm it is
100
+ absent from the package dry-run.
99
101
 
100
102
  ## Rollback
101
103
 
@@ -43,9 +43,13 @@ const currentRoot = (home = lazyHome()) => join(home, "current");
43
43
  const pluginPathForRoot = (installRoot) => join(installRoot, "plugins", "lazyclaude");
44
44
  const claudePluginRoot = (home = claudeHome()) => join(home, "plugins", "cache", "lazyclaude-ai", "lazyclaude", version);
45
45
  const installedPluginsPath = (home = claudeHome()) => join(home, "plugins", "installed_plugins.json");
46
+ const knownMarketplacesPath = (home = claudeHome()) => join(home, "plugins", "known_marketplaces.json");
46
47
  const claudeSettingsPath = (home = claudeHome()) => join(home, "settings.json");
47
48
  const pluginKey = "lazyclaude@lazyclaude-ai";
49
+ const marketplaceName = "lazyclaude-ai";
48
50
  const intendedPluginPath = (home = claudeHome()) => claudePluginRoot(home);
51
+ const marketplaceRoot = (home = lazyHome()) => join(home, "marketplaces", marketplaceName);
52
+ const marketplacePluginPath = (home = lazyHome()) => join(marketplaceRoot(home), "plugins", "lazyclaude");
49
53
  const sourcePluginPath = () => join(root, "plugins", "lazyclaude");
50
54
 
51
55
  const printUsage = () => {
@@ -88,23 +92,98 @@ const writeClaudeSettings = (settings, home = claudeHome()) => {
88
92
  writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\n`);
89
93
  };
90
94
 
91
- const enableClaudePlugin = (home = claudeHome()) => {
95
+ const readKnownMarketplaces = (home = claudeHome()) => {
96
+ const path = knownMarketplacesPath(home);
97
+ if (!existsSync(path)) return {};
98
+ return JSON.parse(readFileSync(path, "utf8"));
99
+ };
100
+
101
+ const writeKnownMarketplaces = (knownMarketplaces, home = claudeHome()) => {
102
+ const path = knownMarketplacesPath(home);
103
+ mkdirSync(dirname(path), { recursive: true });
104
+ writeFileSync(path, `${JSON.stringify(knownMarketplaces, null, 2)}\n`);
105
+ };
106
+
107
+ const marketplaceSource = (marketplacePath) => ({
108
+ source: "directory",
109
+ path: marketplacePath,
110
+ });
111
+
112
+ const writeLocalMarketplace = (legacyHome = lazyHome()) => {
113
+ const rootPath = marketplaceRoot(legacyHome);
114
+ const targetPlugin = marketplacePluginPath(legacyHome);
115
+ rmSync(rootPath, { recursive: true, force: true });
116
+ mkdirSync(dirname(targetPlugin), { recursive: true });
117
+ cpSync(sourcePluginPath(), targetPlugin, { recursive: true });
118
+ const marketplaceManifestPath = join(rootPath, ".claude-plugin", "marketplace.json");
119
+ mkdirSync(dirname(marketplaceManifestPath), { recursive: true });
120
+ writeFileSync(
121
+ marketplaceManifestPath,
122
+ `${JSON.stringify(
123
+ {
124
+ $schema: "https://anthropic.com/claude-code/marketplace.schema.json",
125
+ name: marketplaceName,
126
+ description: "Local LazyClaude npm-installed marketplace.",
127
+ owner: {
128
+ name: "LazyClaude contributors",
129
+ },
130
+ plugins: [
131
+ {
132
+ name: "lazyclaude",
133
+ description: packageJson.description,
134
+ author: {
135
+ name: "LazyClaude contributors",
136
+ },
137
+ source: "./plugins/lazyclaude",
138
+ category: "development",
139
+ homepage: "https://github.com/code-yeongyu/lazycodex",
140
+ },
141
+ ],
142
+ },
143
+ null,
144
+ 2,
145
+ )}\n`,
146
+ );
147
+ return rootPath;
148
+ };
149
+
150
+ const registerMarketplace = (marketplacePath, home = claudeHome()) => {
92
151
  const settings = readClaudeSettings(home);
93
152
  settings.enabledPlugins = settings.enabledPlugins ?? {};
94
153
  settings.enabledPlugins[pluginKey] = true;
154
+ settings.extraKnownMarketplaces = settings.extraKnownMarketplaces ?? {};
155
+ settings.extraKnownMarketplaces[marketplaceName] = {
156
+ source: marketplaceSource(marketplacePath),
157
+ };
95
158
  writeClaudeSettings(settings, home);
159
+
160
+ const knownMarketplaces = readKnownMarketplaces(home);
161
+ knownMarketplaces[marketplaceName] = {
162
+ source: marketplaceSource(marketplacePath),
163
+ installLocation: marketplacePath,
164
+ lastUpdated: new Date().toISOString(),
165
+ };
166
+ writeKnownMarketplaces(knownMarketplaces, home);
96
167
  };
97
168
 
98
- const disableClaudePlugin = (home = claudeHome()) => {
169
+ const unregisterMarketplace = (home = claudeHome()) => {
99
170
  const settingsPath = claudeSettingsPath(home);
100
- if (!existsSync(settingsPath)) return;
101
- const settings = readClaudeSettings(home);
102
- if (!settings.enabledPlugins) return;
103
- delete settings.enabledPlugins[pluginKey];
104
- writeClaudeSettings(settings, home);
171
+ if (existsSync(settingsPath)) {
172
+ const settings = readClaudeSettings(home);
173
+ if (settings.enabledPlugins) delete settings.enabledPlugins[pluginKey];
174
+ if (settings.extraKnownMarketplaces) delete settings.extraKnownMarketplaces[marketplaceName];
175
+ writeClaudeSettings(settings, home);
176
+ }
177
+
178
+ const knownPath = knownMarketplacesPath(home);
179
+ if (existsSync(knownPath)) {
180
+ const knownMarketplaces = readKnownMarketplaces(home);
181
+ delete knownMarketplaces[marketplaceName];
182
+ writeKnownMarketplaces(knownMarketplaces, home);
183
+ }
105
184
  };
106
185
 
107
- const registerClaudePlugin = (installRoot, home = claudeHome()) => {
186
+ const registerClaudePlugin = (installRoot, marketplacePath, home = claudeHome()) => {
108
187
  const registry = readInstalledPlugins(home);
109
188
  const now = new Date().toISOString();
110
189
  const existing = registry.plugins[pluginKey]?.[0] ?? {};
@@ -120,7 +199,7 @@ const registerClaudePlugin = (installRoot, home = claudeHome()) => {
120
199
  },
121
200
  ];
122
201
  writeInstalledPlugins(registry, home);
123
- enableClaudePlugin(home);
202
+ registerMarketplace(marketplacePath, home);
124
203
  };
125
204
 
126
205
  const unregisterClaudePlugin = (home = claudeHome()) => {
@@ -158,10 +237,12 @@ const install = ({ dryRun }) => {
158
237
  const legacyHome = lazyHome();
159
238
  const targetPlugin = claudePluginRoot(home);
160
239
  const sourcePlugin = sourcePluginPath();
240
+ const targetMarketplace = marketplaceRoot(legacyHome);
161
241
 
162
242
  if (dryRun) {
163
243
  process.stdout.write(`DRY_RUN: install LazyClaude ${version}\n`);
164
244
  process.stdout.write(`Would copy: ${sourcePlugin} -> ${targetPlugin}\n`);
245
+ process.stdout.write(`Would create local marketplace: ${targetMarketplace}\n`);
165
246
  process.stdout.write(`Would register Claude plugin: ${pluginKey}\n`);
166
247
  process.stdout.write("Launch with: claude\n");
167
248
  return;
@@ -174,7 +255,8 @@ const install = ({ dryRun }) => {
174
255
  rmSync(targetPlugin, { recursive: true, force: true });
175
256
  mkdirSync(dirname(targetPlugin), { recursive: true });
176
257
  cpSync(sourcePlugin, targetPlugin, { recursive: true });
177
- registerClaudePlugin(targetPlugin, home);
258
+ const localMarketplace = writeLocalMarketplace(legacyHome);
259
+ registerClaudePlugin(targetPlugin, localMarketplace, home);
178
260
 
179
261
  const legacyRoot = versionRoot(legacyHome);
180
262
  rmSync(legacyRoot, { recursive: true, force: true });
@@ -184,6 +266,7 @@ const install = ({ dryRun }) => {
184
266
 
185
267
  process.stdout.write(`INSTALL_PASS: LazyClaude ${version} installed\n`);
186
268
  process.stdout.write(`Claude plugin: ${pluginKey}\n`);
269
+ process.stdout.write(`Marketplace: ${localMarketplace}\n`);
187
270
  process.stdout.write(`Plugin path: ${intendedPluginPath(home)}\n`);
188
271
  process.stdout.write("Launch with: claude\n");
189
272
  };
@@ -229,6 +312,13 @@ const doctor = ({ dryRun }) => {
229
312
  fail("Claude plugin validation failed.");
230
313
  }
231
314
  process.stdout.write("CLAUDE_PLUGIN_VALIDATE_PASS\n");
315
+ const details = spawnSync("claude", ["plugin", "details", pluginKey], { encoding: "utf8" });
316
+ if (details.status !== 0) {
317
+ if (details.stdout) process.stderr.write(details.stdout);
318
+ if (details.stderr) process.stderr.write(details.stderr);
319
+ fail("Claude plugin details failed.");
320
+ }
321
+ process.stdout.write("CLAUDE_PLUGIN_DETAILS_PASS\n");
232
322
  }
233
323
 
234
324
  process.stdout.write(`Plugin path: ${pluginPath}\n`);
@@ -261,13 +351,14 @@ const uninstall = ({ dryRun }) => {
261
351
  const home = lazyHome();
262
352
  const pluginCacheRoot = join(claudeHome(), "plugins", "cache", "lazyclaude-ai");
263
353
  if (dryRun) {
264
- process.stdout.write(`DRY_RUN: remove ${join(home, "lazyclaude-ai")}, ${currentRoot(home)}, ${pluginCacheRoot}, ${pluginKey}, and its enabledPlugins entry\n`);
354
+ process.stdout.write(`DRY_RUN: remove ${join(home, "lazyclaude-ai")}, ${currentRoot(home)}, ${marketplaceRoot(home)}, ${pluginCacheRoot}, ${pluginKey}, and its marketplace/settings entries\n`);
265
355
  return;
266
356
  }
267
357
  rmSync(currentRoot(home), { recursive: true, force: true });
268
358
  rmSync(join(home, "lazyclaude-ai"), { recursive: true, force: true });
359
+ rmSync(marketplaceRoot(home), { recursive: true, force: true });
269
360
  unregisterClaudePlugin();
270
- disableClaudePlugin();
361
+ unregisterMarketplace();
271
362
  rmSync(pluginCacheRoot, { recursive: true, force: true });
272
363
  process.stdout.write("UNINSTALL_PASS\n");
273
364
  };
package/cover.png ADDED
Binary file
package/docs/hooks.md CHANGED
@@ -28,6 +28,17 @@ When a trigger is present, the hook returns additional context containing
28
28
  `ULTRAWORK MODE ENABLED`. That context is guidance for Claude Code; it is not
29
29
  executed as a command.
30
30
 
31
+ LazyClaude follows the LazyCodex goal pattern as model-facing run context, not
32
+ as slash-command injection. If Claude Code exposes the native goal surface, the
33
+ guidance tells Claude to inspect `get_goal`, call `create_goal` only when no
34
+ matching goal is active, and delay `update_goal` until verified completion or a
35
+ genuine blocker. It may also point Claude toward the user-visible `/goal`
36
+ surface, but it does not auto-type `/goal` or send slash command text.
37
+
38
+ For broad work, the same context can steer Claude toward Dynamic workflow
39
+ orchestration and Dynamic worktree isolation so independent work proceeds with
40
+ bounded evidence paths and without mutating unrelated workspace state.
41
+
31
42
  Plain `ulw` therefore activates hook context, not a visible Skill tool call.
32
43
  For a visible LazyClaude command/skill invocation, use the namespaced Claude
33
44
  Code commands:
package/docs/migration.md CHANGED
@@ -9,6 +9,8 @@ of surface, and a conservative local fallback where it does not.
9
9
  | Codex CLI prompt engineering | Claude Code skills | `plugins/lazyclaude/skills/*/SKILL.md` |
10
10
  | Codex ultrawork plan mode | Claude Code skill plus planner agent | `ulw-plan` and `prometheus-planner` |
11
11
  | Codex execution loop | Claude Code skill plus executor agent | `ulw-loop`, `start-work`, and `boulder-executor` |
12
+ | Codex goal-tool guidance | Claude Code native goal surface | `/goal` when user-selected, or model-facing `get_goal`, `create_goal`, and verified-final `update_goal` guidance when exposed |
13
+ | Codex parallel orchestration | Claude Code Dynamic workflow and Dynamic worktree surfaces | Use Dynamic workflow for broad independent work and Dynamic worktree isolation for risky or parallel edits |
12
14
  | Codex hooks | Claude Code hooks | `plugins/lazyclaude/hooks/hooks.json` |
13
15
  | Codex MCP helpers | Claude Code plugin MCP config | `plugins/lazyclaude/.mcp.json` |
14
16
  | Codex LSP integration | Claude Code plugin LSP config | `plugins/lazyclaude/.lsp.json` |
@@ -38,9 +40,25 @@ claude
38
40
  ```
39
41
 
40
42
  The installer writes the user plugin cache, `plugins/installed_plugins.json`,
41
- and the `settings.json` `enabledPlugins` entry for `lazyclaude@lazyclaude-ai`.
43
+ the `settings.json` `enabledPlugins` entry for `lazyclaude@lazyclaude-ai`, and
44
+ a LazyClaude-managed local marketplace entry in `known_marketplaces.json`.
42
45
  This keeps installation convenient without requiring public repo promotion,
43
- manual `--plugin-dir` launch commands, or a Claude marketplace entry.
46
+ manual `--plugin-dir` launch commands, or a remote Claude marketplace entry.
47
+
48
+ ## Goal And Dynamic Workflow Parity
49
+
50
+ LazyCodex goal integration is model-facing guidance around goal tools; it does
51
+ not type a `/goal` slash command for the user. LazyClaude mirrors that contract:
52
+ ULW hook context and ULW skills mention Claude Code's native goal surface, ask
53
+ Claude to inspect `get_goal`, create a goal with `create_goal` only when no
54
+ matching active goal exists, and defer `update_goal` until the evidence gate has
55
+ passed or a real blocker is recorded.
56
+
57
+ When the user explicitly chooses Claude Code `/goal`, that native session goal
58
+ remains user-visible and user-controlled. LazyClaude does not auto-type
59
+ `/goal`. For multi-lane work, LazyClaude should prefer Dynamic workflow
60
+ orchestration where available and Dynamic worktree isolation when parallel or
61
+ risky edits might otherwise collide.
44
62
 
45
63
  If OMC/omc is already installed in Claude Code, keep it disabled or start a
46
64
  separate Claude Code session without OMC while testing LazyClaude. This repo no
@@ -0,0 +1,123 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from pathlib import Path
5
+ from typing import TypeAlias
6
+
7
+ import numpy as np
8
+ from PIL import Image, ImageDraw, ImageFilter, ImageFont
9
+
10
+ WIDTH = 2560
11
+ HEIGHT = 1280
12
+ CORNER_RADIUS = 80
13
+ ROOT = Path(__file__).resolve().parent
14
+ OUT_PATH = ROOT / "cover.png"
15
+ ReadableFont: TypeAlias = ImageFont.FreeTypeFont | ImageFont.ImageFont
16
+
17
+
18
+ def make_blob(size: tuple[int, int], color: tuple[int, int, int, int], cx: int, cy: int, rx: int, ry: int) -> Image.Image:
19
+ layer = Image.new("RGBA", size, (0, 0, 0, 0))
20
+ draw = ImageDraw.Draw(layer)
21
+ draw.ellipse([cx - rx, cy - ry, cx + rx, cy + ry], fill=color)
22
+ return layer
23
+
24
+
25
+ def load_font(size: int, *, bold: bool) -> ReadableFont:
26
+ candidates = [
27
+ ("/System/Library/Fonts/Menlo.ttc", 1 if bold else 0),
28
+ ("/System/Library/Fonts/Supplemental/Courier New Bold.ttf", 0),
29
+ ("/Library/Fonts/Arial Unicode.ttf", 0),
30
+ ]
31
+ for path, index in candidates:
32
+ try:
33
+ return ImageFont.truetype(path, size, index=index)
34
+ except OSError:
35
+ continue
36
+ return ImageFont.load_default(size=size)
37
+
38
+
39
+ def draw_text_layer(text: str, x: int, y: int, font: ReadableFont, color: tuple[int, int, int, int]) -> Image.Image:
40
+ layer = Image.new("RGBA", (WIDTH, HEIGHT), (0, 0, 0, 0))
41
+ ImageDraw.Draw(layer).text((x, y), text, font=font, fill=color)
42
+ return layer
43
+
44
+
45
+ def centered_position(draw: ImageDraw.ImageDraw, text: str, font: ReadableFont, y: int) -> tuple[int, int, int]:
46
+ bbox = draw.textbbox((0, 0), text, font=font)
47
+ text_width = bbox[2] - bbox[0]
48
+ text_height = bbox[3] - bbox[1]
49
+ return int((WIDTH - text_width) // 2 - bbox[0]), int(y - bbox[1]), int(text_height)
50
+
51
+
52
+ def main() -> None:
53
+ canvas = Image.new("RGBA", (WIDTH, HEIGHT), (9, 12, 18, 255))
54
+
55
+ blobs = [
56
+ ((30, 144, 255, 180), 560, 380, 760, 520, 120),
57
+ ((0, 210, 160, 150), 1880, 820, 820, 480, 120),
58
+ ((160, 80, 255, 140), 1350, 180, 780, 360, 110),
59
+ ((255, 210, 80, 90), 820, 1040, 620, 300, 90),
60
+ ]
61
+ for color, cx, cy, rx, ry, blur in blobs:
62
+ blob = make_blob((WIDTH, HEIGHT), color, cx, cy, rx, ry).filter(ImageFilter.GaussianBlur(radius=blur))
63
+ canvas = Image.alpha_composite(canvas, blob)
64
+
65
+ canvas = canvas.filter(ImageFilter.GaussianBlur(radius=7))
66
+
67
+ rng = np.random.default_rng(42)
68
+ noise = rng.integers(0, 255, (HEIGHT, WIDTH), dtype=np.uint8)
69
+ grain_alpha = (noise * 0.16).astype(np.uint8)
70
+ grain = Image.fromarray(np.stack([noise, noise, noise, grain_alpha], axis=-1).astype(np.uint8), "RGBA")
71
+ canvas = Image.alpha_composite(canvas, grain)
72
+
73
+ title = "LazyClaude"
74
+ subtitle = "Claude Code workflows with hooks, skills, agents, MCP, and LSP"
75
+ title_font = load_font(220, bold=True)
76
+ subtitle_font = load_font(58, bold=False)
77
+
78
+ measure = ImageDraw.Draw(Image.new("RGBA", (WIDTH, HEIGHT), (0, 0, 0, 0)))
79
+ title_x, title_y, title_h = centered_position(measure, title, title_font, 445)
80
+ subtitle_x, subtitle_y, _ = centered_position(measure, subtitle, subtitle_font, title_y + title_h + 88)
81
+
82
+ for color, blur in [
83
+ ((80, 210, 255, 70), 22),
84
+ ((150, 255, 220, 85), 10),
85
+ ((255, 255, 255, 120), 4),
86
+ ]:
87
+ glow = draw_text_layer(title, title_x, title_y, title_font, color).filter(ImageFilter.GaussianBlur(radius=blur))
88
+ canvas = Image.alpha_composite(canvas, glow)
89
+
90
+ canvas = Image.alpha_composite(canvas, draw_text_layer(title, title_x, title_y, title_font, (255, 255, 255, 246)))
91
+ canvas = Image.alpha_composite(canvas, draw_text_layer(subtitle, subtitle_x, subtitle_y, subtitle_font, (218, 232, 240, 220)))
92
+
93
+ label_font = load_font(44, bold=False)
94
+ package = json.loads((ROOT / "package.json").read_text(encoding="utf8"))
95
+ label = f"lazyclaude-ai@{package['version']}"
96
+ label_x, label_y, _ = centered_position(measure, label, label_font, 930)
97
+ pill = Image.new("RGBA", (WIDTH, HEIGHT), (0, 0, 0, 0))
98
+ pill_draw = ImageDraw.Draw(pill)
99
+ label_bbox = pill_draw.textbbox((label_x, label_y), label, font=label_font)
100
+ pad_x = 42
101
+ pad_y = 24
102
+ pill_draw.rounded_rectangle(
103
+ [label_bbox[0] - pad_x, label_bbox[1] - pad_y, label_bbox[2] + pad_x, label_bbox[3] + pad_y],
104
+ radius=42,
105
+ fill=(5, 10, 18, 145),
106
+ outline=(255, 255, 255, 42),
107
+ width=2,
108
+ )
109
+ pill_draw.text((label_x, label_y), label, font=label_font, fill=(232, 244, 247, 230))
110
+ canvas = Image.alpha_composite(canvas, pill)
111
+
112
+ mask = Image.new("L", (WIDTH, HEIGHT), 0)
113
+ ImageDraw.Draw(mask).rounded_rectangle([(0, 0), (WIDTH - 1, HEIGHT - 1)], radius=CORNER_RADIUS, fill=255)
114
+ canvas.putalpha(mask)
115
+ canvas = canvas.filter(ImageFilter.GaussianBlur(radius=1))
116
+ canvas.save(OUT_PATH, "PNG", dpi=(400, 400))
117
+ print(f"Saved: {OUT_PATH}")
118
+ print(f"Size: {canvas.size}")
119
+ print(f"Mode: {canvas.mode}")
120
+
121
+
122
+ if __name__ == "__main__":
123
+ main()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lazyclaude-ai",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Claude Code-native LazyCodex-style workflow distribution.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -14,6 +14,8 @@
14
14
  "scripts",
15
15
  "README.md",
16
16
  "README_ko-KR.md",
17
+ "cover.png",
18
+ "generate_cover.py",
17
19
  "REFERENCE.md",
18
20
  "RELEASE_CHECKLIST.md",
19
21
  "LICENSE"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "lazyclaude",
3
3
  "description": "Claude Code-native LazyCodex-style workflow plugin.",
4
- "version": "0.1.1",
4
+ "version": "0.1.3",
5
5
  "author": {
6
6
  "name": "LazyClaude contributors"
7
7
  },
@@ -33,6 +33,15 @@ const writeContext = (additionalContext) => {
33
33
  );
34
34
  };
35
35
 
36
+ const ultraworkContext = [
37
+ "ULTRAWORK MODE ENABLED.",
38
+ "Use evidence-bound planning, tests, manual QA, and cleanup receipts.",
39
+ "Native goal integration: when Claude Code exposes /goal or model-facing goal tools, first inspect get_goal, call create_goal with an objective-only payload when no matching goal is active, and reserve update_goal for verified completion or a genuine blocker.",
40
+ "Do not auto-type or inject the user's slash commands; treat /goal as Claude Code's native goal surface, not as prompt text for this hook to send.",
41
+ "Dynamic workflow: for large independent work, prefer Claude Code Dynamic workflows or subagents with explicit criteria and artifacts.",
42
+ "Dynamic worktree: isolate risky or parallel edits in the active Claude Code worktree/workspace and never mutate unrelated user state.",
43
+ ].join(" ");
44
+
36
45
  const input = readInput();
37
46
 
38
47
  switch (eventName) {
@@ -45,7 +54,7 @@ switch (eventName) {
45
54
  const prompt = typeof input.prompt === "string" ? input.prompt : "";
46
55
  const activates = /\b(?:ultrawork|ulw)\b|\$(?:ulw-plan|ulw-loop|start-work)\b/u.test(prompt);
47
56
  if (activates) {
48
- writeContext("ULTRAWORK MODE ENABLED. Use evidence-bound planning, tests, manual QA, and cleanup receipts.");
57
+ writeContext(ultraworkContext);
49
58
  } else {
50
59
  writeContext("LazyClaude prompt hook checked: no workflow activation.");
51
60
  }
@@ -17,3 +17,16 @@ first unchecked top-level checkbox. For every checkbox:
17
17
  7. Mark the checkbox complete only after all evidence is captured.
18
18
 
19
19
  The durable ledger is `.omo/start-work/ledger.jsonl`.
20
+
21
+ ## Native Goal + Dynamic Workflow
22
+
23
+ Before the first checkbox, use Claude Code's native goal surface when available:
24
+ call `get_goal`, call `create_goal` with the plan objective when no matching
25
+ active goal exists, and do not call `update_goal` until all top-level checkboxes
26
+ and verification gates are complete or the plan is genuinely blocked. If the
27
+ user invokes `/goal`, respect that native Claude Code session goal; LazyClaude
28
+ does not auto-type or send `/goal` text.
29
+
30
+ For independent checkbox waves, prefer Dynamic workflow orchestration or
31
+ subagents with explicit evidence paths. For risky or parallel edits, use
32
+ Dynamic worktree isolation and keep every command in the selected worktree.
@@ -12,3 +12,18 @@ automation.
12
12
 
13
13
  Keep a ledger of decisions, evidence, cleanup receipts, and remaining work.
14
14
  Never claim completion from tests alone.
15
+
16
+ ## Native Goal + Dynamic Workflow
17
+
18
+ Use Claude Code's native goal surface when it is available. If the session
19
+ exposes model-facing goal tools, call `get_goal` before execution, call
20
+ `create_goal` only when no matching active goal exists, and call `update_goal`
21
+ only after every success criterion has real evidence or when the work is truly
22
+ blocked. If the user chooses `/goal`, treat it as Claude Code's native session
23
+ goal command; LazyClaude must not auto-type or send `/goal` text for them.
24
+
25
+ For broad work, consider Claude Code Dynamic workflow orchestration before
26
+ serial execution. Keep each worker or workflow branch tied to concrete criteria,
27
+ artifacts, and cleanup receipts. Use Dynamic worktree isolation for risky or
28
+ parallel edits, and keep all mutations inside the active Claude Code
29
+ workspace/worktree.
@@ -12,7 +12,16 @@ Use Claude Code surfaces directly:
12
12
 
13
13
  - read/search files for grounding
14
14
  - use subagents only for read-only research or plan review
15
+ - include native goal handling: `get_goal`, `create_goal`, and delayed
16
+ `update_goal` when Claude Code exposes those tools, or a user-managed `/goal`
17
+ condition when the user chooses that surface
18
+ - include Dynamic workflow and Dynamic worktree guidance for independent,
19
+ parallel, risky, or long-running implementation lanes
15
20
  - write only plan artifacts and drafts
16
21
  - include concrete tests, manual QA, cleanup, and non-publish guardrails
17
22
 
23
+ Do not auto-type or send `/goal` text. LazyClaude plans should describe the
24
+ native goal surface and the exact completion condition the user or Claude Code
25
+ should bind.
26
+
18
27
  When `$ARGUMENTS` is present, treat it as the planning brief.
@@ -71,13 +71,35 @@ if [ ! -f "$TMP_CLAUDE_HOME/settings.json" ]; then
71
71
  echo "INSTALL_FAIL"
72
72
  exit 1
73
73
  fi
74
+ if [ ! -f "$TMP_CLAUDE_HOME/plugins/known_marketplaces.json" ]; then
75
+ echo "INSTALL_FAIL: Claude known marketplaces missing" >> "$EVIDENCE"
76
+ echo "INSTALL_FAIL"
77
+ exit 1
78
+ fi
79
+ if [ ! -f "$TMP_HOME/marketplaces/lazyclaude-ai/.claude-plugin/marketplace.json" ]; then
80
+ echo "INSTALL_FAIL: LazyClaude local marketplace missing" >> "$EVIDENCE"
81
+ echo "INSTALL_FAIL"
82
+ exit 1
83
+ fi
74
84
  node -e 'const fs = require("fs"); const p = process.argv[1]; const registry = JSON.parse(fs.readFileSync(`${p}/plugins/installed_plugins.json`, "utf8")); const entry = registry.plugins["lazyclaude@lazyclaude-ai"]?.[0]; if (!entry || entry.scope !== "user" || !entry.installPath.includes("/plugins/cache/lazyclaude-ai/lazyclaude/")) process.exit(1);' "$TMP_CLAUDE_HOME"
75
- node -e 'const fs = require("fs"); const p = process.argv[1]; const settings = JSON.parse(fs.readFileSync(`${p}/settings.json`, "utf8")); if (settings.enabledPlugins?.["lazyclaude@lazyclaude-ai"] !== true) process.exit(1);' "$TMP_CLAUDE_HOME"
85
+ node -e 'const fs = require("fs"); const p = process.argv[1]; const h = process.argv[2]; const settings = JSON.parse(fs.readFileSync(`${p}/settings.json`, "utf8")); const market = settings.extraKnownMarketplaces?.["lazyclaude-ai"]; if (settings.enabledPlugins?.["lazyclaude@lazyclaude-ai"] !== true || market?.source?.source !== "directory" || market.source.path !== `${h}/marketplaces/lazyclaude-ai`) process.exit(1);' "$TMP_CLAUDE_HOME" "$TMP_HOME"
86
+ node -e 'const fs = require("fs"); const p = process.argv[1]; const h = process.argv[2]; const known = JSON.parse(fs.readFileSync(`${p}/plugins/known_marketplaces.json`, "utf8")); const market = known["lazyclaude-ai"]; if (market?.source?.source !== "directory" || market.installLocation !== `${h}/marketplaces/lazyclaude-ai`) process.exit(1);' "$TMP_CLAUDE_HOME" "$TMP_HOME"
76
87
  echo "INSTALL_PASS" >> "$EVIDENCE"
77
88
 
78
89
  run_step DOCTOR node "$ROOT/bin/lazyclaude-ai.js" doctor
79
90
  echo "DOCTOR_PASS" >> "$EVIDENCE"
80
91
 
92
+ if command -v claude >/dev/null 2>&1; then
93
+ run_step CLAUDE_PLUGIN_DETAILS claude plugin details lazyclaude@lazyclaude-ai
94
+ if ! grep -q "Skills (7)" "$EVIDENCE" || ! grep -q "ulw-loop" "$EVIDENCE" || ! grep -q "ulw-plan" "$EVIDENCE" || ! grep -q "Hooks (4)" "$EVIDENCE"; then
95
+ echo "CLAUDE_PLUGIN_DETAILS_FAIL: expected LazyClaude skills/hooks inventory" >> "$EVIDENCE"
96
+ echo "CLAUDE_PLUGIN_DETAILS_FAIL"
97
+ exit 1
98
+ fi
99
+ else
100
+ echo "CLAUDE_PLUGIN_DETAILS_SKIP: claude executable not found" >> "$EVIDENCE"
101
+ fi
102
+
81
103
  run_step DRY_RUN_UNINSTALL node "$ROOT/bin/lazyclaude-ai.js" --dry-run uninstall
82
104
  if [ ! -d "$PLUGIN_PATH" ]; then
83
105
  echo "DRY_RUN_UNINSTALL_FAIL: plugin removed during dry-run" >> "$EVIDENCE"
@@ -91,6 +113,7 @@ if [ -e "$TMP_HOME/current" ] || [ -e "$TMP_HOME/lazyclaude-ai" ] || [ -e "$TMP_
91
113
  exit 1
92
114
  fi
93
115
  node -e 'const fs = require("fs"); const p = process.argv[1]; const settings = JSON.parse(fs.readFileSync(`${p}/settings.json`, "utf8")); if (settings.enabledPlugins?.["lazyclaude@lazyclaude-ai"] !== undefined) process.exit(1);' "$TMP_CLAUDE_HOME"
116
+ node -e 'const fs = require("fs"); const p = process.argv[1]; const settings = JSON.parse(fs.readFileSync(`${p}/settings.json`, "utf8")); const known = JSON.parse(fs.readFileSync(`${p}/plugins/known_marketplaces.json`, "utf8")); if (settings.extraKnownMarketplaces?.["lazyclaude-ai"] !== undefined || known["lazyclaude-ai"] !== undefined) process.exit(1);' "$TMP_CLAUDE_HOME"
94
117
  echo "UNINSTALL_PASS" >> "$EVIDENCE"
95
118
 
96
119
  cleanup