lazyclaude-ai 0.1.0
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/LICENSE +21 -0
- package/README.md +176 -0
- package/REFERENCE.md +21 -0
- package/RELEASE_CHECKLIST.md +86 -0
- package/bin/lazyclaude-ai.js +215 -0
- package/docs/agents.md +31 -0
- package/docs/hooks.md +60 -0
- package/docs/lsp.md +35 -0
- package/docs/migration.md +57 -0
- package/package.json +36 -0
- package/plugins/lazyclaude/.claude-plugin/plugin.json +25 -0
- package/plugins/lazyclaude/.lsp.json +13 -0
- package/plugins/lazyclaude/.mcp.json +9 -0
- package/plugins/lazyclaude/agents/boulder-executor.md +12 -0
- package/plugins/lazyclaude/agents/librarian-researcher.md +11 -0
- package/plugins/lazyclaude/agents/oracle-verifier.md +12 -0
- package/plugins/lazyclaude/agents/prometheus-planner.md +13 -0
- package/plugins/lazyclaude/agents/qa-runner.md +12 -0
- package/plugins/lazyclaude/agents/quality-reviewer.md +12 -0
- package/plugins/lazyclaude/bin/lazyclaude-hook.js +67 -0
- package/plugins/lazyclaude/bin/lazyclaude-lsp-doctor.js +15 -0
- package/plugins/lazyclaude/bin/lazyclaude-mcp.js +70 -0
- package/plugins/lazyclaude/hooks/hooks.json +54 -0
- package/plugins/lazyclaude/skills/lsp/SKILL.md +13 -0
- package/plugins/lazyclaude/skills/programming/SKILL.md +14 -0
- package/plugins/lazyclaude/skills/review-work/SKILL.md +13 -0
- package/plugins/lazyclaude/skills/rules/SKILL.md +13 -0
- package/plugins/lazyclaude/skills/start-work/SKILL.md +19 -0
- package/plugins/lazyclaude/skills/ulw-loop/SKILL.md +14 -0
- package/plugins/lazyclaude/skills/ulw-plan/SKILL.md +18 -0
- package/scripts/audit-plan-checkboxes.mjs +36 -0
- package/scripts/doctor.mjs +27 -0
- package/scripts/inspect-agent-tools.mjs +27 -0
- package/scripts/qa-claude-plugin-smoke.sh +60 -0
- package/scripts/qa-portable-install.sh +80 -0
- package/scripts/validate-plugin.mjs +71 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 LazyClaude contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
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
|
+
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.
|
|
13
|
+
|
|
14
|
+
## Fresh PC Install
|
|
15
|
+
|
|
16
|
+
Use one of these npm-compatible entrypoints on a new machine:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npx lazyclaude-ai install
|
|
20
|
+
bunx lazyclaude-ai install
|
|
21
|
+
npm install -g lazyclaude-ai
|
|
22
|
+
lazyclaude install
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The installer copies the packaged Claude Code plugin into a LazyClaude-managed
|
|
26
|
+
user directory and prints the exact launch command:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
claude --plugin-dir ~/.lazyclaude/current/plugins/lazyclaude
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Validate the install:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
lazyclaude doctor
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Launch Claude Code through the installed plugin:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
lazyclaude run -- --help
|
|
42
|
+
lazyclaude run
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Remove only LazyClaude-managed install state:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
lazyclaude uninstall
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Set `LAZYCLAUDE_HOME=/some/path` to install into a custom directory for testing
|
|
52
|
+
or isolated machines.
|
|
53
|
+
|
|
54
|
+
## Local Install
|
|
55
|
+
|
|
56
|
+
Use the plugin directly from this checkout while it is under development:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
claude --plugin-dir ./plugins/lazyclaude
|
|
60
|
+
```
|
|
61
|
+
|
|
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
|
+
Inside Claude Code, reload local plugin metadata after edits:
|
|
71
|
+
|
|
72
|
+
```text
|
|
73
|
+
/reload-plugins
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## ULW Usage
|
|
77
|
+
|
|
78
|
+
Start Claude Code from this repo:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
claude --plugin-dir ./plugins/lazyclaude
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Then type one of these prompts in Claude Code:
|
|
85
|
+
|
|
86
|
+
```text
|
|
87
|
+
ulw
|
|
88
|
+
ultrawork
|
|
89
|
+
$ulw-plan <what you want planned>
|
|
90
|
+
$ulw-loop <what you want executed with evidence>
|
|
91
|
+
$start-work plans/lazyclaude-retrofit.md
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
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.
|
|
98
|
+
|
|
99
|
+
Plain `ulw` is hook context activation, so it may not show a separate Skill
|
|
100
|
+
tool invocation in Claude Code history. Use visible namespaced commands when
|
|
101
|
+
you want explicit LazyClaude skill/command activation:
|
|
102
|
+
|
|
103
|
+
```text
|
|
104
|
+
/lazyclaude:ulw-loop <what you want executed with evidence>
|
|
105
|
+
/lazyclaude:ulw-plan <what you want planned>
|
|
106
|
+
/lazyclaude:start-work plans/lazyclaude-retrofit.md
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The package alias can be inspected without installing anything globally:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
node bin/lazyclaude-ai.js --dry-run install
|
|
113
|
+
node bin/lazyclaude-ai.js --dry-run doctor
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Local Development
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
npm test
|
|
120
|
+
npm run validate:plugin
|
|
121
|
+
npm run qa:tmux
|
|
122
|
+
npm run pack:dry-run
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`validate:plugin` and `qa:tmux` are local checks. If Claude Code is not
|
|
126
|
+
available on the machine, the smoke harness records a controlled skip with the
|
|
127
|
+
version probe evidence instead of failing mysteriously.
|
|
128
|
+
|
|
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
|
+
## Safety Model
|
|
150
|
+
|
|
151
|
+
LazyClaude is intentionally local-first:
|
|
152
|
+
|
|
153
|
+
- Hooks read Claude Code event JSON from stdin and return bounded JSON context.
|
|
154
|
+
- Hooks do not execute user prompt text.
|
|
155
|
+
- The ultrawork prompt hook returns constant guidance and does not echo prompt
|
|
156
|
+
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.
|
|
159
|
+
- The planner agent is read-only and has no edit tools.
|
|
160
|
+
- MCP and LSP helpers are local stdio commands.
|
|
161
|
+
- Publication, remote marketplace mutation, and package release all require
|
|
162
|
+
explicit user approval.
|
|
163
|
+
|
|
164
|
+
## MVP Scope
|
|
165
|
+
|
|
166
|
+
- Package/bin: `lazyclaude-ai`
|
|
167
|
+
- Plugin namespace: `lazyclaude`
|
|
168
|
+
- Claude Code platform: `claude-code`
|
|
169
|
+
- Skills: ultrawork planning, ultrawork loop, start-work, rules, LSP,
|
|
170
|
+
programming, review-work
|
|
171
|
+
- Agents: planner, executor, verifier, reviewer, librarian, QA runner
|
|
172
|
+
- Hooks: session-start, user-prompt-submit, post-tool-use, post-compact
|
|
173
|
+
- Config: local MCP server and TypeScript-family LSP doctor
|
|
174
|
+
|
|
175
|
+
See `REFERENCE.md` for the pinned LazyCodex source. See `docs/migration.md` for
|
|
176
|
+
the Codex-to-Claude migration table.
|
package/REFERENCE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# LazyClaude Reference Pin
|
|
2
|
+
|
|
3
|
+
LazyCodex commit: 3fb8802e314dc0a1f23481dd3782cdca26b92dc2
|
|
4
|
+
Claude docs snapshot: 2026-05-31
|
|
5
|
+
|
|
6
|
+
## Source
|
|
7
|
+
|
|
8
|
+
- Repository: https://github.com/code-yeongyu/lazycodex
|
|
9
|
+
- Pinned tree: https://github.com/code-yeongyu/lazycodex/tree/3fb8802e314dc0a1f23481dd3782cdca26b92dc2
|
|
10
|
+
- Observed reference clone: `/tmp/lazycodex-ref`
|
|
11
|
+
|
|
12
|
+
## Migration Table
|
|
13
|
+
|
|
14
|
+
| Codex surface | Claude Code surface | Notes |
|
|
15
|
+
| --- | --- | --- |
|
|
16
|
+
| `.codex-plugin/plugin.json` | `.claude-plugin/plugin.json` | Plugin identity and component paths are translated, not copied. |
|
|
17
|
+
| Codex skills | Claude Code `skills/<name>/SKILL.md` | Skills keep workflow intent and remove Codex-only tool names. |
|
|
18
|
+
| Codex hooks | Claude Code `hooks/hooks.json` | Hook events are mapped to Claude tool names and JSON stdin behavior. |
|
|
19
|
+
| Codex MCP config | Claude Code `.mcp.json` | Local helper tools stay plugin-scoped. |
|
|
20
|
+
| Codex LSP MCP component | Claude Code `.lsp.json` | MVP starts with TypeScript/JavaScript. |
|
|
21
|
+
| Codex subagent orchestration | Claude Code `agents/*.md` | Agents get bounded tools, permissions, and optional preloaded skills. |
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# LazyClaude Release Checklist
|
|
2
|
+
|
|
3
|
+
Status: quiet public npm package candidate; currently unpublished until explicit
|
|
4
|
+
user approval.
|
|
5
|
+
|
|
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
|
+
|
|
9
|
+
This release path is for personal install convenience, not advertisement,
|
|
10
|
+
public repo promotion, or a public launch campaign.
|
|
11
|
+
|
|
12
|
+
## Verified Locally
|
|
13
|
+
|
|
14
|
+
- `npm test`
|
|
15
|
+
- `npm run validate:plugin`
|
|
16
|
+
- `npm run qa:tmux`
|
|
17
|
+
- `npm run pack:dry-run`
|
|
18
|
+
|
|
19
|
+
## Local / Private Checkout Use
|
|
20
|
+
|
|
21
|
+
Use this track when testing from the current checkout:
|
|
22
|
+
|
|
23
|
+
1. Run `npm test`.
|
|
24
|
+
2. Run `npm run validate:plugin`.
|
|
25
|
+
3. Run `npm run qa:tmux`.
|
|
26
|
+
4. Start Claude Code with `claude --plugin-dir ./plugins/lazyclaude`.
|
|
27
|
+
|
|
28
|
+
No npm publication is required for this track.
|
|
29
|
+
|
|
30
|
+
## Quiet Public NPM Package Release
|
|
31
|
+
|
|
32
|
+
Use this track only when the user explicitly approves making the package
|
|
33
|
+
installable through `npm`, `npx`, and `bunx`.
|
|
34
|
+
|
|
35
|
+
1. Confirm the target package name and version with the user.
|
|
36
|
+
2. Review `REFERENCE.md` and `docs/migration.md` for accurate attribution.
|
|
37
|
+
3. Run `npm whoami` and confirm the intended npm account.
|
|
38
|
+
4. Re-run the full local verification set from a clean checkout:
|
|
39
|
+
- `npm test`
|
|
40
|
+
- `npm run validate:plugin`
|
|
41
|
+
- `npm run doctor`
|
|
42
|
+
- `npm run qa:tmux`
|
|
43
|
+
- `npm run qa:portable`
|
|
44
|
+
- `npm run pack:dry-run`
|
|
45
|
+
5. Inspect `npm pack --dry-run --json` and confirm package contents.
|
|
46
|
+
6. Ask for explicit user approval to publish.
|
|
47
|
+
7. Only after approval, perform the selected publication path in a separate,
|
|
48
|
+
auditable release step.
|
|
49
|
+
|
|
50
|
+
For the current unscoped `lazyclaude-ai` package name, use:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm publish
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
If the package is renamed to a scoped package such as `@scope/lazyclaude-ai`,
|
|
57
|
+
publish it publicly with:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npm publish --access public
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Accidental Publish Rollback
|
|
64
|
+
|
|
65
|
+
If the wrong package or version is published, stop and report the incident
|
|
66
|
+
before further mutation. Depending on npm policy and timing, choose one audited
|
|
67
|
+
rollback action with user approval:
|
|
68
|
+
|
|
69
|
+
1. `npm deprecate <package>@<version> "<message>"`
|
|
70
|
+
2. `npm unpublish <package>@<version>` only when allowed and explicitly approved
|
|
71
|
+
3. Publish a corrected patch version after verification
|
|
72
|
+
|
|
73
|
+
## Marketplace Boundary
|
|
74
|
+
|
|
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.
|
|
81
|
+
|
|
82
|
+
## Rollback
|
|
83
|
+
|
|
84
|
+
If a local test load causes problems, stop Claude Code, restart without
|
|
85
|
+
`claude --plugin-dir ./plugins/lazyclaude`, and run `/reload-plugins` in any
|
|
86
|
+
remaining session that should drop the local plugin metadata.
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import {
|
|
5
|
+
cpSync,
|
|
6
|
+
existsSync,
|
|
7
|
+
mkdirSync,
|
|
8
|
+
readFileSync,
|
|
9
|
+
rmSync,
|
|
10
|
+
symlinkSync,
|
|
11
|
+
} from "node:fs";
|
|
12
|
+
import { homedir } from "node:os";
|
|
13
|
+
import { dirname, join, resolve } from "node:path";
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
15
|
+
|
|
16
|
+
const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
17
|
+
const packageJson = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
|
|
18
|
+
const version = packageJson.version;
|
|
19
|
+
|
|
20
|
+
const usage = `Usage: lazyclaude-ai [--dry-run] <install|doctor|path|run|update|uninstall> [...args]
|
|
21
|
+
|
|
22
|
+
Commands:
|
|
23
|
+
install Copy the packaged LazyClaude plugin into LAZYCLAUDE_HOME.
|
|
24
|
+
doctor Validate the installed LazyClaude plugin path.
|
|
25
|
+
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.
|
|
28
|
+
uninstall Remove LazyClaude-managed install state.
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
const parseArgs = (argv) => {
|
|
32
|
+
const args = [...argv];
|
|
33
|
+
const dryRun = args[0] === "--dry-run";
|
|
34
|
+
if (dryRun) args.shift();
|
|
35
|
+
return { dryRun, command: args[0], rest: args.slice(1) };
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const lazyHome = () => resolve(process.env.LAZYCLAUDE_HOME ?? join(homedir(), ".lazyclaude"));
|
|
39
|
+
const versionRoot = (home = lazyHome()) => join(home, "lazyclaude-ai", version);
|
|
40
|
+
const currentRoot = (home = lazyHome()) => join(home, "current");
|
|
41
|
+
const pluginPathForRoot = (installRoot) => join(installRoot, "plugins", "lazyclaude");
|
|
42
|
+
const intendedPluginPath = (home = lazyHome()) => pluginPathForRoot(currentRoot(home));
|
|
43
|
+
const sourcePluginPath = () => join(root, "plugins", "lazyclaude");
|
|
44
|
+
|
|
45
|
+
const printUsage = () => {
|
|
46
|
+
process.stderr.write(usage);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const fail = (message, status = 1) => {
|
|
50
|
+
process.stderr.write(`${message}\n`);
|
|
51
|
+
process.exit(status);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const currentExists = (home = lazyHome()) => existsSync(currentRoot(home));
|
|
55
|
+
|
|
56
|
+
const requireInstalledPluginPath = (home = lazyHome()) => {
|
|
57
|
+
if (!currentExists(home)) {
|
|
58
|
+
fail("LazyClaude is not installed. Run `lazyclaude install` first.");
|
|
59
|
+
}
|
|
60
|
+
const path = intendedPluginPath(home);
|
|
61
|
+
if (!existsSync(join(path, ".claude-plugin", "plugin.json"))) {
|
|
62
|
+
fail("LazyClaude install is incomplete. Run `lazyclaude install` again.");
|
|
63
|
+
}
|
|
64
|
+
return path;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const resetCurrentPointer = (home, target) => {
|
|
68
|
+
const current = currentRoot(home);
|
|
69
|
+
rmSync(current, { recursive: true, force: true });
|
|
70
|
+
try {
|
|
71
|
+
symlinkSync(target, current, "dir");
|
|
72
|
+
} catch {
|
|
73
|
+
mkdirSync(current, { recursive: true });
|
|
74
|
+
cpSync(target, current, { recursive: true });
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const install = ({ dryRun }) => {
|
|
79
|
+
const home = lazyHome();
|
|
80
|
+
const targetRoot = versionRoot(home);
|
|
81
|
+
const targetPlugin = pluginPathForRoot(targetRoot);
|
|
82
|
+
const sourcePlugin = sourcePluginPath();
|
|
83
|
+
|
|
84
|
+
if (dryRun) {
|
|
85
|
+
process.stdout.write(`DRY_RUN: install LazyClaude ${version}\n`);
|
|
86
|
+
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`);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!existsSync(sourcePlugin)) {
|
|
93
|
+
fail(`Packaged LazyClaude plugin payload is missing: ${sourcePlugin}`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
rmSync(targetRoot, { recursive: true, force: true });
|
|
97
|
+
mkdirSync(dirname(targetPlugin), { recursive: true });
|
|
98
|
+
cpSync(sourcePlugin, targetPlugin, { recursive: true });
|
|
99
|
+
resetCurrentPointer(home, targetRoot);
|
|
100
|
+
|
|
101
|
+
process.stdout.write(`INSTALL_PASS: LazyClaude ${version} installed\n`);
|
|
102
|
+
process.stdout.write(`Plugin path: ${intendedPluginPath(home)}\n`);
|
|
103
|
+
process.stdout.write(`Launch with: claude --plugin-dir ${intendedPluginPath(home)}\n`);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const doctor = ({ dryRun }) => {
|
|
107
|
+
if (dryRun) {
|
|
108
|
+
const pluginPath = intendedPluginPath();
|
|
109
|
+
process.stdout.write("DRY_RUN: doctor LazyClaude install\n");
|
|
110
|
+
process.stdout.write(`Would check plugin files under: ${pluginPath}\n`);
|
|
111
|
+
process.stdout.write(`Would run: claude plugin validate ${pluginPath}\n`);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const pluginPath = requireInstalledPluginPath();
|
|
116
|
+
const requiredFiles = [
|
|
117
|
+
".claude-plugin/plugin.json",
|
|
118
|
+
".mcp.json",
|
|
119
|
+
".lsp.json",
|
|
120
|
+
"hooks/hooks.json",
|
|
121
|
+
"bin/lazyclaude-hook.js",
|
|
122
|
+
"bin/lazyclaude-mcp.js",
|
|
123
|
+
"skills/ulw-loop/SKILL.md",
|
|
124
|
+
"skills/ulw-plan/SKILL.md",
|
|
125
|
+
];
|
|
126
|
+
|
|
127
|
+
for (const file of requiredFiles) {
|
|
128
|
+
const path = join(pluginPath, file);
|
|
129
|
+
if (!existsSync(path)) fail(`LazyClaude install is missing ${file}. Run \`lazyclaude install\` again.`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const claude = spawnSync("claude", ["--version"], { encoding: "utf8" });
|
|
133
|
+
if (claude.error) {
|
|
134
|
+
process.stdout.write("CLAUDE_WARNING: claude executable not found in PATH\n");
|
|
135
|
+
} else {
|
|
136
|
+
process.stdout.write(`CLAUDE_VERSION: ${(claude.stdout || claude.stderr).trim()}\n`);
|
|
137
|
+
const validation = spawnSync("claude", ["plugin", "validate", pluginPath], { encoding: "utf8" });
|
|
138
|
+
if (validation.status !== 0) {
|
|
139
|
+
if (validation.stdout) process.stderr.write(validation.stdout);
|
|
140
|
+
if (validation.stderr) process.stderr.write(validation.stderr);
|
|
141
|
+
fail("Claude plugin validation failed.");
|
|
142
|
+
}
|
|
143
|
+
process.stdout.write("CLAUDE_PLUGIN_VALIDATE_PASS\n");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
process.stdout.write(`Plugin path: ${pluginPath}\n`);
|
|
147
|
+
process.stdout.write(`Launch with: claude --plugin-dir ${pluginPath}\n`);
|
|
148
|
+
process.stdout.write("DOCTOR_PASS\n");
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const printPath = () => {
|
|
152
|
+
process.stdout.write(`${requireInstalledPluginPath()}\n`);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const runClaude = ({ dryRun, rest }) => {
|
|
156
|
+
const separatorIndex = rest.indexOf("--");
|
|
157
|
+
const claudeArgs = separatorIndex === -1 ? rest : rest.slice(separatorIndex + 1);
|
|
158
|
+
const pluginPath = dryRun ? intendedPluginPath() : requireInstalledPluginPath();
|
|
159
|
+
const command = ["claude", "--plugin-dir", pluginPath, ...claudeArgs];
|
|
160
|
+
|
|
161
|
+
if (dryRun) {
|
|
162
|
+
process.stdout.write(command.join(" "));
|
|
163
|
+
process.stdout.write("\n");
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const result = spawnSync(command[0], command.slice(1), { stdio: "inherit" });
|
|
168
|
+
if (result.error) fail(`failed to start claude: ${result.error.message}`);
|
|
169
|
+
process.exit(result.status ?? 1);
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const uninstall = ({ dryRun }) => {
|
|
173
|
+
const home = lazyHome();
|
|
174
|
+
if (dryRun) {
|
|
175
|
+
process.stdout.write(`DRY_RUN: remove ${join(home, "lazyclaude-ai")} and ${currentRoot(home)}\n`);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
rmSync(currentRoot(home), { recursive: true, force: true });
|
|
179
|
+
rmSync(join(home, "lazyclaude-ai"), { recursive: true, force: true });
|
|
180
|
+
process.stdout.write("UNINSTALL_PASS\n");
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const main = () => {
|
|
184
|
+
const parsed = parseArgs(process.argv.slice(2));
|
|
185
|
+
const { command } = parsed;
|
|
186
|
+
|
|
187
|
+
if (!command) {
|
|
188
|
+
printUsage();
|
|
189
|
+
process.exit(64);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
switch (command) {
|
|
193
|
+
case "install":
|
|
194
|
+
case "update":
|
|
195
|
+
install(parsed);
|
|
196
|
+
break;
|
|
197
|
+
case "doctor":
|
|
198
|
+
doctor(parsed);
|
|
199
|
+
break;
|
|
200
|
+
case "path":
|
|
201
|
+
printPath(parsed);
|
|
202
|
+
break;
|
|
203
|
+
case "run":
|
|
204
|
+
runClaude(parsed);
|
|
205
|
+
break;
|
|
206
|
+
case "uninstall":
|
|
207
|
+
uninstall(parsed);
|
|
208
|
+
break;
|
|
209
|
+
default:
|
|
210
|
+
printUsage();
|
|
211
|
+
fail(`Unknown command: ${command}`, 64);
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
main();
|
package/docs/agents.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# LazyClaude Agents
|
|
2
|
+
|
|
3
|
+
LazyClaude agents map the LazyCodex team workflow into Claude Code subagents
|
|
4
|
+
with bounded responsibilities.
|
|
5
|
+
|
|
6
|
+
| Agent | Responsibility | Boundary |
|
|
7
|
+
| --- | --- | --- |
|
|
8
|
+
| `prometheus-planner` | Build implementation plans and identify risks. | Read-only tools, no write/edit/bash tools. |
|
|
9
|
+
| `boulder-executor` | Execute checked plan tasks. | Must follow task acceptance criteria and collect evidence. |
|
|
10
|
+
| `oracle-verifier` | Verify tests, artifacts, and task completion. | Reviews evidence before approval. |
|
|
11
|
+
| `quality-reviewer` | Perform code-review style risk checks. | Findings first, no unrelated rewrites. |
|
|
12
|
+
| `librarian-researcher` | Fetch and summarize external references. | Use primary sources and cite them. |
|
|
13
|
+
| `qa-runner` | Run tmux/manual QA scenarios. | Must clean up sessions and capture receipts. |
|
|
14
|
+
|
|
15
|
+
The planner is deliberately constrained because the safest Claude Code retrofit
|
|
16
|
+
keeps planning separate from mutation. Executor and QA agents can act only
|
|
17
|
+
inside the plan and evidence contract.
|
|
18
|
+
|
|
19
|
+
## Local Use
|
|
20
|
+
|
|
21
|
+
Load the plugin from this checkout:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
claude --plugin-dir ./plugins/lazyclaude
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Then reload after edits:
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
/reload-plugins
|
|
31
|
+
```
|
package/docs/hooks.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# LazyClaude Hooks
|
|
2
|
+
|
|
3
|
+
LazyClaude hooks translate the LazyCodex prompt workflow into Claude Code hook
|
|
4
|
+
events while keeping execution local and bounded.
|
|
5
|
+
|
|
6
|
+
## Hook Events
|
|
7
|
+
|
|
8
|
+
| Event | Runner | Purpose |
|
|
9
|
+
| --- | --- | --- |
|
|
10
|
+
| `SessionStart` | `plugins/lazyclaude/bin/lazyclaude-hook.js session-start` | Adds a compact rules-loaded context line. |
|
|
11
|
+
| `UserPromptSubmit` | `plugins/lazyclaude/bin/lazyclaude-hook.js user-prompt-submit` | Detects ultrawork prompts and injects workflow context. |
|
|
12
|
+
| `PostToolUse` | `plugins/lazyclaude/bin/lazyclaude-hook.js post-tool-use` | Reminds the session to run post-edit checks. |
|
|
13
|
+
| `PostCompact` | `plugins/lazyclaude/bin/lazyclaude-hook.js post-compact` | Resets compacted rule context after summarization. |
|
|
14
|
+
|
|
15
|
+
## ULW Trigger Phrases
|
|
16
|
+
|
|
17
|
+
The `UserPromptSubmit` hook activates on any of these phrases:
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
ulw
|
|
21
|
+
ultrawork
|
|
22
|
+
$ulw-plan
|
|
23
|
+
$ulw-loop
|
|
24
|
+
$start-work
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
When a trigger is present, the hook returns additional context containing
|
|
28
|
+
`ULTRAWORK MODE ENABLED`. That context is guidance for Claude Code; it is not
|
|
29
|
+
executed as a command.
|
|
30
|
+
|
|
31
|
+
Plain `ulw` therefore activates hook context, not a visible Skill tool call.
|
|
32
|
+
For a visible LazyClaude command/skill invocation, use the namespaced Claude
|
|
33
|
+
Code commands:
|
|
34
|
+
|
|
35
|
+
```text
|
|
36
|
+
/lazyclaude:ulw-loop <goal>
|
|
37
|
+
/lazyclaude:ulw-plan <planning brief>
|
|
38
|
+
/lazyclaude:start-work plans/lazyclaude-retrofit.md
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Safety
|
|
42
|
+
|
|
43
|
+
Hooks parse JSON from stdin and return JSON to Claude Code. The hook does not
|
|
44
|
+
execute prompt text and does not echo prompt text into the returned context. The
|
|
45
|
+
ultrawork detector returns constant workflow guidance, so a prompt cannot become
|
|
46
|
+
a shell command through the hook response.
|
|
47
|
+
|
|
48
|
+
## Local Smoke
|
|
49
|
+
|
|
50
|
+
Run the hook fixture directly:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
node plugins/lazyclaude/bin/lazyclaude-hook.js user-prompt-submit < fixtures/hooks/user-prompt-ultrawork.json
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Reload local plugin metadata in Claude Code after hook edits:
|
|
57
|
+
|
|
58
|
+
```text
|
|
59
|
+
/reload-plugins
|
|
60
|
+
```
|
package/docs/lsp.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# LazyClaude LSP
|
|
2
|
+
|
|
3
|
+
LazyClaude includes a Claude Code plugin LSP config and a local doctor command
|
|
4
|
+
for TypeScript-family projects.
|
|
5
|
+
|
|
6
|
+
## Configuration
|
|
7
|
+
|
|
8
|
+
`plugins/lazyclaude/.lsp.json` declares a TypeScript language server command:
|
|
9
|
+
|
|
10
|
+
```json
|
|
11
|
+
["typescript-language-server", "--stdio"]
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
It covers `.ts`, `.tsx`, `.js`, `.jsx`, `.mjs`, and `.cjs` files.
|
|
15
|
+
|
|
16
|
+
## Doctor
|
|
17
|
+
|
|
18
|
+
Run the doctor locally:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
node plugins/lazyclaude/bin/lazyclaude-lsp-doctor.js
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
If `typescript-language-server` is unavailable, the doctor exits successfully
|
|
25
|
+
with installation guidance so local smoke tests can distinguish an environment
|
|
26
|
+
gap from a plugin failure.
|
|
27
|
+
|
|
28
|
+
## Claude Code Reload
|
|
29
|
+
|
|
30
|
+
After changing `.lsp.json`, restart Claude Code with the local plugin directory
|
|
31
|
+
or use:
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
/reload-plugins
|
|
35
|
+
```
|