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 +111 -73
- package/README_ko-KR.md +114 -71
- package/RELEASE_CHECKLIST.md +13 -11
- package/bin/lazyclaude-ai.js +103 -12
- package/cover.png +0 -0
- package/docs/hooks.md +11 -0
- package/docs/migration.md +20 -2
- package/generate_cover.py +123 -0
- package/package.json +3 -1
- package/plugins/lazyclaude/.claude-plugin/plugin.json +1 -1
- package/plugins/lazyclaude/bin/lazyclaude-hook.js +10 -1
- package/plugins/lazyclaude/skills/start-work/SKILL.md +13 -0
- package/plugins/lazyclaude/skills/ulw-loop/SKILL.md +15 -0
- package/plugins/lazyclaude/skills/ulw-plan/SKILL.md +9 -0
- package/scripts/qa-portable-install.sh +24 -1
package/README.md
CHANGED
|
@@ -1,19 +1,48 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
LazyClaude
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
72
|
-
/
|
|
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
|
-
|
|
76
|
-
|
|
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
|
-
|
|
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
|
|
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,
|
|
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
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
npx --yes lazyclaude-ai
|
|
119
|
-
npx --yes lazyclaude-ai
|
|
120
|
-
npx --yes lazyclaude-ai
|
|
121
|
-
npx --yes lazyclaude-ai
|
|
122
|
-
npx --yes lazyclaude-ai
|
|
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
|
-
|
|
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
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
LazyClaude
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
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
|
-
|
|
74
|
-
/
|
|
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
|
-
|
|
78
|
-
|
|
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 작업,
|
|
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
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
npx --yes lazyclaude-ai
|
|
121
|
-
npx --yes lazyclaude-ai
|
|
122
|
-
npx --yes lazyclaude-ai
|
|
123
|
-
npx --yes lazyclaude-ai
|
|
124
|
-
npx --yes lazyclaude-ai
|
|
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
|
-
|
|
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
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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`를 참고하세요.
|
package/RELEASE_CHECKLIST.md
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
# LazyClaude Release Checklist
|
|
2
2
|
|
|
3
|
-
Status: quiet public npm package `lazyclaude-ai@0.1.
|
|
4
|
-
personal install convenience after explicit user approval.
|
|
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
|
|
44
|
-
|
|
45
|
-
|
|
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.
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
|
package/bin/lazyclaude-ai.js
CHANGED
|
@@ -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
|
|
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
|
|
169
|
+
const unregisterMarketplace = (home = claudeHome()) => {
|
|
99
170
|
const settingsPath = claudeSettingsPath(home);
|
|
100
|
-
if (
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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"
|
|
@@ -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(
|
|
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
|