lazyclaude-ai 0.1.0 → 0.1.1

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
@@ -5,80 +5,85 @@ simple local activation, prompt-triggered ultrawork discipline,
5
5
  planner/executor/reviewer agents, lifecycle hooks, MCP scaffolding, and
6
6
  LSP-backed code checks.
7
7
 
8
- LazyClaude is intended as a quiet personal distribution. It can be prepared for
9
- public npm installation convenience without advertising, public repo promotion,
10
- or Claude marketplace publication. Do not run `npm publish`, mutate a
11
- marketplace, or promote this as publicly available without explicit user
12
- approval. Until that approval is given, this package is not published.
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
13
 
14
- ## Fresh PC Install
14
+ ## Quick Install
15
15
 
16
- Use one of these npm-compatible entrypoints on a new machine:
16
+ Use the npm package from any machine:
17
17
 
18
18
  ```bash
19
- npx lazyclaude-ai install
20
- bunx lazyclaude-ai install
21
- npm install -g lazyclaude-ai
22
- lazyclaude install
19
+ npx --yes lazyclaude-ai install
23
20
  ```
24
21
 
25
- The installer copies the packaged Claude Code plugin into a LazyClaude-managed
26
- user directory and prints the exact launch command:
22
+ Validate the installed plugin:
27
23
 
28
24
  ```bash
29
- claude --plugin-dir ~/.lazyclaude/current/plugins/lazyclaude
25
+ npx --yes lazyclaude-ai doctor
30
26
  ```
31
27
 
32
- Validate the install:
28
+ Launch Claude Code normally:
33
29
 
34
30
  ```bash
35
- lazyclaude doctor
31
+ claude
36
32
  ```
37
33
 
38
- Launch Claude Code through the installed plugin:
34
+ Alternative entrypoints:
39
35
 
40
36
  ```bash
41
- lazyclaude run -- --help
42
- lazyclaude run
37
+ bunx lazyclaude-ai install
38
+ npm install -g lazyclaude-ai
39
+ lazyclaude install
40
+ ```
41
+
42
+ If you need the explicit `npm exec` form, use a fresh prefix so npm does not
43
+ confuse the registry package with a same-name source checkout:
44
+
45
+ ```bash
46
+ npm exec --prefix "$(mktemp -d)" --yes --package lazyclaude-ai -- lazyclaude install
43
47
  ```
44
48
 
45
49
  Remove only LazyClaude-managed install state:
46
50
 
47
51
  ```bash
48
- lazyclaude uninstall
52
+ npx --yes lazyclaude-ai uninstall
49
53
  ```
50
54
 
51
- Set `LAZYCLAUDE_HOME=/some/path` to install into a custom directory for testing
52
- or isolated machines.
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.
53
60
 
54
- ## Local Install
61
+ ## Local Development
55
62
 
56
- Use the plugin directly from this checkout while it is under development:
63
+ Use the plugin directly from this checkout while editing it:
57
64
 
58
65
  ```bash
59
66
  claude --plugin-dir ./plugins/lazyclaude
60
67
  ```
61
68
 
62
- If you already have an OMC/omc Claude plugin installed, do not co-load it with
63
- LazyClaude during this local MVP test. LazyClaude intentionally avoids a root
64
- Claude marketplace skeleton now, so it can be loaded directly by
65
- path without competing with an existing OMC marketplace or plugin namespace.
66
- If your user-level Claude config still enables OMC, Claude may create a local
67
- `.omc/` state directory during smoke tests; LazyClaude ignores and excludes that
68
- directory from git and npm package surfaces.
69
-
70
69
  Inside Claude Code, reload local plugin metadata after edits:
71
70
 
72
71
  ```text
73
72
  /reload-plugins
74
73
  ```
75
74
 
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.
80
+
76
81
  ## ULW Usage
77
82
 
78
- Start Claude Code from this repo:
83
+ LazyClaude is available in normal Claude Code sessions after install:
79
84
 
80
85
  ```bash
81
- claude --plugin-dir ./plugins/lazyclaude
86
+ claude
82
87
  ```
83
88
 
84
89
  Then type one of these prompts in Claude Code:
@@ -92,13 +97,13 @@ $start-work plans/lazyclaude-retrofit.md
92
97
  ```
93
98
 
94
99
  Expected behavior: LazyClaude's prompt hook adds `ULTRAWORK MODE ENABLED`
95
- context, then the matching skill/agent instructions steer Claude toward
96
- test-first work, real manual QA evidence, cleanup receipts, and no publish step
97
- without approval.
100
+ 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.
98
103
 
99
104
  Plain `ulw` is hook context activation, so it may not show a separate Skill
100
105
  tool invocation in Claude Code history. Use visible namespaced commands when
101
- you want explicit LazyClaude skill/command activation:
106
+ you want explicit LazyClaude skill or command activation:
102
107
 
103
108
  ```text
104
109
  /lazyclaude:ulw-loop <what you want executed with evidence>
@@ -106,6 +111,17 @@ you want explicit LazyClaude skill/command activation:
106
111
  /lazyclaude:start-work plans/lazyclaude-retrofit.md
107
112
  ```
108
113
 
114
+ ## Commands
115
+
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
+ ```
124
+
109
125
  The package alias can be inspected without installing anything globally:
110
126
 
111
127
  ```bash
@@ -113,12 +129,13 @@ node bin/lazyclaude-ai.js --dry-run install
113
129
  node bin/lazyclaude-ai.js --dry-run doctor
114
130
  ```
115
131
 
116
- ## Local Development
132
+ ## Verification
117
133
 
118
134
  ```bash
119
135
  npm test
120
136
  npm run validate:plugin
121
137
  npm run qa:tmux
138
+ npm run qa:portable
122
139
  npm run pack:dry-run
123
140
  ```
124
141
 
@@ -126,26 +143,6 @@ npm run pack:dry-run
126
143
  available on the machine, the smoke harness records a controlled skip with the
127
144
  version probe evidence instead of failing mysteriously.
128
145
 
129
- ## Rollback And Uninstall
130
-
131
- For npm-installed LazyClaude, run:
132
-
133
- ```bash
134
- lazyclaude uninstall
135
- ```
136
-
137
- Because the local MVP can also be loaded with
138
- `claude --plugin-dir ./plugins/lazyclaude`, checkout rollback is local and
139
- reversible:
140
-
141
- 1. Stop the Claude Code session that loaded the plugin.
142
- 2. Start Claude Code without the `--plugin-dir` flag.
143
- 3. Remove any temporary plugin-dir reference you added for testing.
144
- 4. Run `/reload-plugins` in any remaining Claude Code session that should stop
145
- seeing LazyClaude.
146
-
147
- No global install step is required for this MVP.
148
-
149
146
  ## Safety Model
150
147
 
151
148
  LazyClaude is intentionally local-first:
@@ -154,16 +151,17 @@ LazyClaude is intentionally local-first:
154
151
  - Hooks do not execute user prompt text.
155
152
  - The ultrawork prompt hook returns constant guidance and does not echo prompt
156
153
  text back into the context.
157
- - Any `.omc/` directory created by a user-level OMC plugin is ignored and is not
158
- included in the LazyClaude package.
154
+ - Any `.omc/`, `.omo/`, or `evidence/` local state is ignored and is not
155
+ included in the LazyClaude npm package.
159
156
  - The planner agent is read-only and has no edit tools.
160
157
  - MCP and LSP helpers are local stdio commands.
161
- - Publication, remote marketplace mutation, and package release all require
162
- explicit user approval.
158
+ - Future publication, remote marketplace mutation, and package release all
159
+ require explicit user approval.
163
160
 
164
161
  ## MVP Scope
165
162
 
166
163
  - Package/bin: `lazyclaude-ai`
164
+ - Friendly bin alias: `lazyclaude`
167
165
  - Plugin namespace: `lazyclaude`
168
166
  - Claude Code platform: `claude-code`
169
167
  - Skills: ultrawork planning, ultrawork loop, start-work, rules, LSP,
@@ -172,5 +170,6 @@ LazyClaude is intentionally local-first:
172
170
  - Hooks: session-start, user-prompt-submit, post-tool-use, post-compact
173
171
  - Config: local MCP server and TypeScript-family LSP doctor
174
172
 
175
- See `REFERENCE.md` for the pinned LazyCodex source. See `docs/migration.md` for
176
- the Codex-to-Claude migration table.
173
+ See `README_ko-KR.md` for Korean setup notes, `REFERENCE.md` for the pinned
174
+ LazyCodex source, and `docs/migration.md` for the Codex-to-Claude migration
175
+ table.
@@ -0,0 +1,175 @@
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
+ 의미하지는 않습니다. 새 버전 배포는 항상 별도의 명시적 승인 후에
13
+ 진행합니다.
14
+
15
+ ## 빠른 설치
16
+
17
+ 새 환경에서는 아래 한 줄을 사용합니다.
18
+
19
+ ```bash
20
+ npx --yes lazyclaude-ai install
21
+ ```
22
+
23
+ 설치 상태를 확인합니다.
24
+
25
+ ```bash
26
+ npx --yes lazyclaude-ai doctor
27
+ ```
28
+
29
+ 설치 후에는 일반적인 `claude` 명령으로 Claude Code를 실행합니다.
30
+
31
+ ```bash
32
+ claude
33
+ ```
34
+
35
+ 대체 진입점은 다음과 같습니다.
36
+
37
+ ```bash
38
+ bunx lazyclaude-ai install
39
+ npm install -g lazyclaude-ai
40
+ lazyclaude install
41
+ ```
42
+
43
+ 현재 checkout처럼 package 이름이 같은 폴더 안에서 `npm exec`를 직접 쓸
44
+ 때는 fresh prefix를 주는 편이 안전합니다.
45
+
46
+ ```bash
47
+ npm exec --prefix "$(mktemp -d)" --yes --package lazyclaude-ai -- lazyclaude install
48
+ ```
49
+
50
+ LazyClaude가 관리한 설치 상태만 제거하려면 다음을 사용합니다.
51
+
52
+ ```bash
53
+ npx --yes lazyclaude-ai uninstall
54
+ ```
55
+
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
+ 사용합니다.
72
+
73
+ ```text
74
+ /reload-plugins
75
+ ```
76
+
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에서 제외합니다.
82
+
83
+ ## ULW 사용법
84
+
85
+ 설치 후에는 일반적인 `claude` 명령에서 LazyClaude를 사용할 수 있습니다.
86
+
87
+ ```bash
88
+ claude
89
+ ```
90
+
91
+ 그다음 Claude Code에 아래 프롬프트 중 하나를 입력합니다.
92
+
93
+ ```text
94
+ ulw
95
+ ultrawork
96
+ $ulw-plan <계획이 필요한 작업>
97
+ $ulw-loop <증거 기반으로 실행할 작업>
98
+ $start-work plans/lazyclaude-retrofit.md
99
+ ```
100
+
101
+ 기대 동작은 LazyClaude prompt hook이 `ULTRAWORK MODE ENABLED` 컨텍스트를
102
+ 추가하고, skill 및 agent 지시문이 Claude를 test-first 작업, 실제 수동 QA
103
+ 증거, cleanup receipt, 향후 release step의 명시적 승인 쪽으로 유도하는
104
+ 것입니다.
105
+
106
+ 단순히 `ulw`라고 입력하는 것은 hook context activation이므로 Claude Code
107
+ history에 별도 Skill tool invocation으로 보이지 않을 수 있습니다. 명시적인
108
+ LazyClaude command activation을 보고 싶다면 namespaced command를 사용합니다.
109
+
110
+ ```text
111
+ /lazyclaude:ulw-loop <증거 기반으로 실행할 작업>
112
+ /lazyclaude:ulw-plan <계획이 필요한 작업>
113
+ /lazyclaude:start-work plans/lazyclaude-retrofit.md
114
+ ```
115
+
116
+ ## 명령어
117
+
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
+ ```
126
+
127
+ 전역 설치 없이 checkout 내부 bin을 확인할 수도 있습니다.
128
+
129
+ ```bash
130
+ node bin/lazyclaude-ai.js --dry-run install
131
+ node bin/lazyclaude-ai.js --dry-run doctor
132
+ ```
133
+
134
+ ## 검증
135
+
136
+ ```bash
137
+ npm test
138
+ npm run validate:plugin
139
+ npm run qa:tmux
140
+ npm run qa:portable
141
+ npm run pack:dry-run
142
+ ```
143
+
144
+ `validate:plugin`과 `qa:tmux`는 로컬 검증용입니다. Claude Code가 없는
145
+ 환경에서는 smoke harness가 모호한 실패 대신 controlled skip과 version
146
+ probe evidence를 남깁니다.
147
+
148
+ ## 안전 모델
149
+
150
+ - Hook은 Claude Code event JSON을 stdin으로 읽고 제한된 JSON context를
151
+ 반환합니다.
152
+ - Hook은 사용자 프롬프트 텍스트를 실행하지 않습니다.
153
+ - ultrawork prompt hook은 고정된 guidance를 반환하며 prompt text를 다시
154
+ echo하지 않습니다.
155
+ - `.omc/`, `.omo/`, `evidence/` 같은 로컬 상태는 ignore되며 npm 패키지에
156
+ 포함되지 않습니다.
157
+ - planner agent는 read-only이며 edit tool을 갖지 않습니다.
158
+ - 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
173
+
174
+ 영문 설명은 `README.md`, LazyCodex 출처 고정 정보는 `REFERENCE.md`,
175
+ Codex-to-Claude 변환 표는 `docs/migration.md`를 참고하세요.
@@ -1,10 +1,13 @@
1
1
  # LazyClaude Release Checklist
2
2
 
3
- Status: quiet public npm package candidate; currently unpublished until explicit
4
- user approval.
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.
5
7
 
6
- DO NOT publish LazyClaude, run `npm publish`, push release tags, or add a
7
- remote Claude Code marketplace entry without explicit user approval.
8
+ DO NOT publish a new version of LazyClaude, run `npm publish`, push release
9
+ tags, or add a remote Claude Code marketplace entry without explicit user
10
+ approval.
8
11
 
9
12
  This release path is for personal install convenience, not advertisement,
10
13
  public repo promotion, or a public launch campaign.
@@ -14,6 +17,7 @@ public repo promotion, or a public launch campaign.
14
17
  - `npm test`
15
18
  - `npm run validate:plugin`
16
19
  - `npm run qa:tmux`
20
+ - `npm run qa:portable`
17
21
  - `npm run pack:dry-run`
18
22
 
19
23
  ## Local / Private Checkout Use
@@ -27,10 +31,23 @@ Use this track when testing from the current checkout:
27
31
 
28
32
  No npm publication is required for this track.
29
33
 
30
- ## Quiet Public NPM Package Release
34
+ ## Fresh Machine NPM Install Use
31
35
 
32
- Use this track only when the user explicitly approves making the package
33
- installable through `npm`, `npx`, and `bunx`.
36
+ Use this track after an approved npm package version exists:
37
+
38
+ 1. Run `npx --yes lazyclaude-ai install`.
39
+ 2. Run `npx --yes lazyclaude-ai doctor`.
40
+ 3. Start Claude Code with the normal `claude` command.
41
+
42
+ 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.
46
+
47
+ ## Quiet Public NPM Package Release Or Update
48
+
49
+ Use this track only when the user explicitly approves making a new package
50
+ version installable through `npm`, `npx`, and `bunx`.
34
51
 
35
52
  1. Confirm the target package name and version with the user.
36
53
  2. Review `REFERENCE.md` and `docs/migration.md` for accurate attribution.
@@ -72,15 +89,22 @@ rollback action with user approval:
72
89
 
73
90
  ## Marketplace Boundary
74
91
 
75
- LazyClaude is tested through `claude --plugin-dir ./plugins/lazyclaude`. A root
76
- Claude marketplace file is intentionally not shipped because users may already
77
- have an OMC/omc marketplace or plugin installed. Do not add a local or remote
78
- marketplace entry without explicit user approval. If user-level OMC creates
79
- `.omc/` state during validation, keep it ignored and confirm it is absent from
80
- the package dry-run.
92
+ Local checkout testing still uses `claude --plugin-dir ./plugins/lazyclaude`.
93
+ NPM installs should use the global Claude user plugin registry and normal
94
+ `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.
81
99
 
82
100
  ## Rollback
83
101
 
84
102
  If a local test load causes problems, stop Claude Code, restart without
85
103
  `claude --plugin-dir ./plugins/lazyclaude`, and run `/reload-plugins` in any
86
104
  remaining session that should drop the local plugin metadata.
105
+
106
+ If an npm-installed LazyClaude copy causes problems, run:
107
+
108
+ ```bash
109
+ npx --yes lazyclaude-ai uninstall
110
+ ```
@@ -8,6 +8,7 @@ import {
8
8
  readFileSync,
9
9
  rmSync,
10
10
  symlinkSync,
11
+ writeFileSync,
11
12
  } from "node:fs";
12
13
  import { homedir } from "node:os";
13
14
  import { dirname, join, resolve } from "node:path";
@@ -20,11 +21,11 @@ const version = packageJson.version;
20
21
  const usage = `Usage: lazyclaude-ai [--dry-run] <install|doctor|path|run|update|uninstall> [...args]
21
22
 
22
23
  Commands:
23
- install Copy the packaged LazyClaude plugin into LAZYCLAUDE_HOME.
24
+ install Register the packaged LazyClaude plugin in Claude Code.
24
25
  doctor Validate the installed LazyClaude plugin path.
25
26
  path Print the installed Claude plugin path.
26
- run -- ... Run Claude Code with the installed plugin path.
27
- update Reinstall this package version and refresh the current pointer.
27
+ run -- ... Run Claude Code after the global plugin install.
28
+ update Reinstall this package version and refresh the Claude plugin registry.
28
29
  uninstall Remove LazyClaude-managed install state.
29
30
  `;
30
31
 
@@ -36,10 +37,15 @@ const parseArgs = (argv) => {
36
37
  };
37
38
 
38
39
  const lazyHome = () => resolve(process.env.LAZYCLAUDE_HOME ?? join(homedir(), ".lazyclaude"));
40
+ const claudeHome = () => resolve(process.env.CLAUDE_CONFIG_DIR ?? process.env.CLAUDE_HOME ?? join(homedir(), ".claude"));
39
41
  const versionRoot = (home = lazyHome()) => join(home, "lazyclaude-ai", version);
40
42
  const currentRoot = (home = lazyHome()) => join(home, "current");
41
43
  const pluginPathForRoot = (installRoot) => join(installRoot, "plugins", "lazyclaude");
42
- const intendedPluginPath = (home = lazyHome()) => pluginPathForRoot(currentRoot(home));
44
+ const claudePluginRoot = (home = claudeHome()) => join(home, "plugins", "cache", "lazyclaude-ai", "lazyclaude", version);
45
+ const installedPluginsPath = (home = claudeHome()) => join(home, "plugins", "installed_plugins.json");
46
+ const claudeSettingsPath = (home = claudeHome()) => join(home, "settings.json");
47
+ const pluginKey = "lazyclaude@lazyclaude-ai";
48
+ const intendedPluginPath = (home = claudeHome()) => claudePluginRoot(home);
43
49
  const sourcePluginPath = () => join(root, "plugins", "lazyclaude");
44
50
 
45
51
  const printUsage = () => {
@@ -51,9 +57,81 @@ const fail = (message, status = 1) => {
51
57
  process.exit(status);
52
58
  };
53
59
 
54
- const currentExists = (home = lazyHome()) => existsSync(currentRoot(home));
60
+ const currentExists = (home = claudeHome()) => existsSync(claudePluginRoot(home));
55
61
 
56
- const requireInstalledPluginPath = (home = lazyHome()) => {
62
+ const readInstalledPlugins = (home = claudeHome()) => {
63
+ const registryPath = installedPluginsPath(home);
64
+ if (!existsSync(registryPath)) return { version: 2, plugins: {} };
65
+ const parsed = JSON.parse(readFileSync(registryPath, "utf8"));
66
+ return {
67
+ ...parsed,
68
+ version: parsed.version ?? 2,
69
+ plugins: parsed.plugins ?? {},
70
+ };
71
+ };
72
+
73
+ const writeInstalledPlugins = (registry, home = claudeHome()) => {
74
+ const registryPath = installedPluginsPath(home);
75
+ mkdirSync(dirname(registryPath), { recursive: true });
76
+ writeFileSync(registryPath, `${JSON.stringify(registry, null, 2)}\n`);
77
+ };
78
+
79
+ const readClaudeSettings = (home = claudeHome()) => {
80
+ const settingsPath = claudeSettingsPath(home);
81
+ if (!existsSync(settingsPath)) return {};
82
+ return JSON.parse(readFileSync(settingsPath, "utf8"));
83
+ };
84
+
85
+ const writeClaudeSettings = (settings, home = claudeHome()) => {
86
+ const settingsPath = claudeSettingsPath(home);
87
+ mkdirSync(dirname(settingsPath), { recursive: true });
88
+ writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\n`);
89
+ };
90
+
91
+ const enableClaudePlugin = (home = claudeHome()) => {
92
+ const settings = readClaudeSettings(home);
93
+ settings.enabledPlugins = settings.enabledPlugins ?? {};
94
+ settings.enabledPlugins[pluginKey] = true;
95
+ writeClaudeSettings(settings, home);
96
+ };
97
+
98
+ const disableClaudePlugin = (home = claudeHome()) => {
99
+ 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);
105
+ };
106
+
107
+ const registerClaudePlugin = (installRoot, home = claudeHome()) => {
108
+ const registry = readInstalledPlugins(home);
109
+ const now = new Date().toISOString();
110
+ const existing = registry.plugins[pluginKey]?.[0] ?? {};
111
+ registry.plugins[pluginKey] = [
112
+ {
113
+ scope: "user",
114
+ installPath: installRoot,
115
+ version,
116
+ installedAt: existing.installedAt ?? now,
117
+ lastUpdated: now,
118
+ installedBy: "lazyclaude-ai",
119
+ enabled: true,
120
+ },
121
+ ];
122
+ writeInstalledPlugins(registry, home);
123
+ enableClaudePlugin(home);
124
+ };
125
+
126
+ const unregisterClaudePlugin = (home = claudeHome()) => {
127
+ const registryPath = installedPluginsPath(home);
128
+ if (!existsSync(registryPath)) return;
129
+ const registry = readInstalledPlugins(home);
130
+ delete registry.plugins[pluginKey];
131
+ writeInstalledPlugins(registry, home);
132
+ };
133
+
134
+ const requireInstalledPluginPath = (home = claudeHome()) => {
57
135
  if (!currentExists(home)) {
58
136
  fail("LazyClaude is not installed. Run `lazyclaude install` first.");
59
137
  }
@@ -76,16 +154,16 @@ const resetCurrentPointer = (home, target) => {
76
154
  };
77
155
 
78
156
  const install = ({ dryRun }) => {
79
- const home = lazyHome();
80
- const targetRoot = versionRoot(home);
81
- const targetPlugin = pluginPathForRoot(targetRoot);
157
+ const home = claudeHome();
158
+ const legacyHome = lazyHome();
159
+ const targetPlugin = claudePluginRoot(home);
82
160
  const sourcePlugin = sourcePluginPath();
83
161
 
84
162
  if (dryRun) {
85
163
  process.stdout.write(`DRY_RUN: install LazyClaude ${version}\n`);
86
164
  process.stdout.write(`Would copy: ${sourcePlugin} -> ${targetPlugin}\n`);
87
- process.stdout.write(`Would point current: ${currentRoot(home)} -> ${targetRoot}\n`);
88
- process.stdout.write(`Launch with: claude --plugin-dir ${intendedPluginPath(home)}\n`);
165
+ process.stdout.write(`Would register Claude plugin: ${pluginKey}\n`);
166
+ process.stdout.write("Launch with: claude\n");
89
167
  return;
90
168
  }
91
169
 
@@ -93,14 +171,21 @@ const install = ({ dryRun }) => {
93
171
  fail(`Packaged LazyClaude plugin payload is missing: ${sourcePlugin}`);
94
172
  }
95
173
 
96
- rmSync(targetRoot, { recursive: true, force: true });
174
+ rmSync(targetPlugin, { recursive: true, force: true });
97
175
  mkdirSync(dirname(targetPlugin), { recursive: true });
98
176
  cpSync(sourcePlugin, targetPlugin, { recursive: true });
99
- resetCurrentPointer(home, targetRoot);
177
+ registerClaudePlugin(targetPlugin, home);
178
+
179
+ const legacyRoot = versionRoot(legacyHome);
180
+ rmSync(legacyRoot, { recursive: true, force: true });
181
+ mkdirSync(dirname(pluginPathForRoot(legacyRoot)), { recursive: true });
182
+ cpSync(sourcePlugin, pluginPathForRoot(legacyRoot), { recursive: true });
183
+ resetCurrentPointer(legacyHome, legacyRoot);
100
184
 
101
185
  process.stdout.write(`INSTALL_PASS: LazyClaude ${version} installed\n`);
186
+ process.stdout.write(`Claude plugin: ${pluginKey}\n`);
102
187
  process.stdout.write(`Plugin path: ${intendedPluginPath(home)}\n`);
103
- process.stdout.write(`Launch with: claude --plugin-dir ${intendedPluginPath(home)}\n`);
188
+ process.stdout.write("Launch with: claude\n");
104
189
  };
105
190
 
106
191
  const doctor = ({ dryRun }) => {
@@ -109,10 +194,13 @@ const doctor = ({ dryRun }) => {
109
194
  process.stdout.write("DRY_RUN: doctor LazyClaude install\n");
110
195
  process.stdout.write(`Would check plugin files under: ${pluginPath}\n`);
111
196
  process.stdout.write(`Would run: claude plugin validate ${pluginPath}\n`);
197
+ process.stdout.write(`Would check Claude plugin registry: ${pluginKey}\n`);
112
198
  return;
113
199
  }
114
200
 
115
201
  const pluginPath = requireInstalledPluginPath();
202
+ const registryEntry = readInstalledPlugins().plugins[pluginKey]?.[0];
203
+ if (!registryEntry) fail(`LazyClaude is missing from Claude plugin registry: ${pluginKey}`);
116
204
  const requiredFiles = [
117
205
  ".claude-plugin/plugin.json",
118
206
  ".mcp.json",
@@ -144,7 +232,7 @@ const doctor = ({ dryRun }) => {
144
232
  }
145
233
 
146
234
  process.stdout.write(`Plugin path: ${pluginPath}\n`);
147
- process.stdout.write(`Launch with: claude --plugin-dir ${pluginPath}\n`);
235
+ process.stdout.write("Launch with: claude\n");
148
236
  process.stdout.write("DOCTOR_PASS\n");
149
237
  };
150
238
 
@@ -155,8 +243,8 @@ const printPath = () => {
155
243
  const runClaude = ({ dryRun, rest }) => {
156
244
  const separatorIndex = rest.indexOf("--");
157
245
  const claudeArgs = separatorIndex === -1 ? rest : rest.slice(separatorIndex + 1);
158
- const pluginPath = dryRun ? intendedPluginPath() : requireInstalledPluginPath();
159
- const command = ["claude", "--plugin-dir", pluginPath, ...claudeArgs];
246
+ if (!dryRun) requireInstalledPluginPath();
247
+ const command = ["claude", ...claudeArgs];
160
248
 
161
249
  if (dryRun) {
162
250
  process.stdout.write(command.join(" "));
@@ -171,12 +259,16 @@ const runClaude = ({ dryRun, rest }) => {
171
259
 
172
260
  const uninstall = ({ dryRun }) => {
173
261
  const home = lazyHome();
262
+ const pluginCacheRoot = join(claudeHome(), "plugins", "cache", "lazyclaude-ai");
174
263
  if (dryRun) {
175
- process.stdout.write(`DRY_RUN: remove ${join(home, "lazyclaude-ai")} and ${currentRoot(home)}\n`);
264
+ process.stdout.write(`DRY_RUN: remove ${join(home, "lazyclaude-ai")}, ${currentRoot(home)}, ${pluginCacheRoot}, ${pluginKey}, and its enabledPlugins entry\n`);
176
265
  return;
177
266
  }
178
267
  rmSync(currentRoot(home), { recursive: true, force: true });
179
268
  rmSync(join(home, "lazyclaude-ai"), { recursive: true, force: true });
269
+ unregisterClaudePlugin();
270
+ disableClaudePlugin();
271
+ rmSync(pluginCacheRoot, { recursive: true, force: true });
180
272
  process.stdout.write("UNINSTALL_PASS\n");
181
273
  };
182
274
 
package/docs/migration.md CHANGED
@@ -26,14 +26,21 @@ claude --plugin-dir ./plugins/lazyclaude
26
26
  For fresh machines, the quiet npm distribution path is:
27
27
 
28
28
  ```bash
29
- npx lazyclaude-ai install
29
+ npx --yes lazyclaude-ai install
30
30
  bunx lazyclaude-ai install
31
31
  lazyclaude doctor
32
- lazyclaude run
33
32
  ```
34
33
 
35
- This keeps installation convenient without requiring public repo promotion or a
36
- Claude marketplace entry.
34
+ Launch Claude Code normally after install:
35
+
36
+ ```bash
37
+ claude
38
+ ```
39
+
40
+ The installer writes the user plugin cache, `plugins/installed_plugins.json`,
41
+ and the `settings.json` `enabledPlugins` entry for `lazyclaude@lazyclaude-ai`.
42
+ This keeps installation convenient without requiring public repo promotion,
43
+ manual `--plugin-dir` launch commands, or a Claude marketplace entry.
37
44
 
38
45
  If OMC/omc is already installed in Claude Code, keep it disabled or start a
39
46
  separate Claude Code session without OMC while testing LazyClaude. This repo no
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lazyclaude-ai",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Claude Code-native LazyCodex-style workflow distribution.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -13,6 +13,7 @@
13
13
  "plugins",
14
14
  "scripts",
15
15
  "README.md",
16
+ "README_ko-KR.md",
16
17
  "REFERENCE.md",
17
18
  "RELEASE_CHECKLIST.md",
18
19
  "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.0",
4
+ "version": "0.1.1",
5
5
  "author": {
6
6
  "name": "LazyClaude contributors"
7
7
  },
@@ -4,6 +4,7 @@ set -u
4
4
  ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
5
  EVIDENCE="$ROOT/evidence/portable-qa-install.txt"
6
6
  TMP_HOME=""
7
+ TMP_CLAUDE_HOME=""
7
8
 
8
9
  mkdir -p "$ROOT/evidence"
9
10
  : > "$EVIDENCE"
@@ -12,6 +13,9 @@ cleanup() {
12
13
  if [ -n "$TMP_HOME" ]; then
13
14
  rm -rf "$TMP_HOME"
14
15
  fi
16
+ if [ -n "$TMP_CLAUDE_HOME" ]; then
17
+ rm -rf "$TMP_CLAUDE_HOME"
18
+ fi
15
19
  }
16
20
  trap cleanup EXIT INT TERM
17
21
 
@@ -22,7 +26,9 @@ trap cleanup EXIT INT TERM
22
26
  } >> "$EVIDENCE"
23
27
 
24
28
  TMP_HOME="$(mktemp -d "${TMPDIR:-/tmp/}lazyclaude-portable-qa.XXXXXX")"
29
+ TMP_CLAUDE_HOME="$(mktemp -d "${TMPDIR:-/tmp/}lazyclaude-claude-qa.XXXXXX")"
25
30
  export LAZYCLAUDE_HOME="$TMP_HOME"
31
+ export CLAUDE_CONFIG_DIR="$TMP_CLAUDE_HOME"
26
32
 
27
33
  run_step() {
28
34
  local label="$1"
@@ -55,6 +61,18 @@ if [ ! -f "$PLUGIN_PATH/.claude-plugin/plugin.json" ]; then
55
61
  echo "INSTALL_FAIL"
56
62
  exit 1
57
63
  fi
64
+ if [ ! -f "$TMP_CLAUDE_HOME/plugins/installed_plugins.json" ]; then
65
+ echo "INSTALL_FAIL: Claude plugin registry missing" >> "$EVIDENCE"
66
+ echo "INSTALL_FAIL"
67
+ exit 1
68
+ fi
69
+ if [ ! -f "$TMP_CLAUDE_HOME/settings.json" ]; then
70
+ echo "INSTALL_FAIL: Claude settings missing" >> "$EVIDENCE"
71
+ echo "INSTALL_FAIL"
72
+ exit 1
73
+ fi
74
+ 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"
58
76
  echo "INSTALL_PASS" >> "$EVIDENCE"
59
77
 
60
78
  run_step DOCTOR node "$ROOT/bin/lazyclaude-ai.js" doctor
@@ -67,14 +85,16 @@ if [ ! -d "$PLUGIN_PATH" ]; then
67
85
  fi
68
86
 
69
87
  run_step UNINSTALL node "$ROOT/bin/lazyclaude-ai.js" uninstall
70
- if [ -e "$TMP_HOME/current" ] || [ -e "$TMP_HOME/lazyclaude-ai" ]; then
88
+ if [ -e "$TMP_HOME/current" ] || [ -e "$TMP_HOME/lazyclaude-ai" ] || [ -e "$TMP_CLAUDE_HOME/plugins/cache/lazyclaude-ai" ]; then
71
89
  echo "UNINSTALL_FAIL: managed state remains" >> "$EVIDENCE"
72
90
  echo "UNINSTALL_FAIL"
73
91
  exit 1
74
92
  fi
93
+ 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"
75
94
  echo "UNINSTALL_PASS" >> "$EVIDENCE"
76
95
 
77
96
  cleanup
78
97
  TMP_HOME=""
98
+ TMP_CLAUDE_HOME=""
79
99
  echo "CLEANUP: removed portable QA temp home" >> "$EVIDENCE"
80
100
  echo "PORTABLE_QA_PASS"