@slahon/lazykit 1.2.9 → 1.3.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 +15 -4
- package/git.js +10 -1
- package/init.js +54 -35
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,12 +21,14 @@ Run this from your **project's root directory** — the same folder that contain
|
|
|
21
21
|
- Node.js 18+
|
|
22
22
|
- A GitHub repository with a remote set up (`git remote -v` should show a GitHub URL)
|
|
23
23
|
- A Claude Pro or Max subscription ([claude.ai](https://claude.ai))
|
|
24
|
-
- Claude Code
|
|
25
|
-
- GitHub CLI (`gh`) —
|
|
24
|
+
- **Claude Code CLI** — `npm install -g @anthropic-ai/claude-code`
|
|
25
|
+
- **GitHub CLI (`gh`)** — `brew install gh` or [cli.github.com](https://cli.github.com), then `gh auth login`
|
|
26
26
|
- **Claude Code GitHub App** installed on your repo — [github.com/apps/claude](https://github.com/apps/claude) *(required for the Actions workflow to run)*
|
|
27
27
|
|
|
28
28
|
> **All `npx @slahon/lazykit` commands must be run from your project's root directory** — the folder where your `.git` directory lives and your GitHub remote is configured.
|
|
29
29
|
|
|
30
|
+
`init` checks for `gh` and `claude` before proceeding. If either is missing or not authenticated, it will show you exactly what to install and wait for you to confirm before continuing — no need to restart.
|
|
31
|
+
|
|
30
32
|
## How it works
|
|
31
33
|
|
|
32
34
|
### 1. Open an issue using the LazyKit Task template
|
|
@@ -137,9 +139,18 @@ During `npx @slahon/lazykit@latest init` you will be asked:
|
|
|
137
139
|
|
|
138
140
|
LazyKit uses your Claude Pro/Max subscription via an OAuth token — no pay-per-token API billing.
|
|
139
141
|
|
|
140
|
-
`init
|
|
142
|
+
During `init`, LazyKit runs `claude setup-token` and tries to capture the token automatically. On many systems a browser window opens, you approve access, and the token is stored as `CLAUDE_CODE_OAUTH_TOKEN` in your repo secrets without any extra steps.
|
|
143
|
+
|
|
144
|
+
If the token can't be captured automatically (varies by system), LazyKit falls back and asks you to paste it:
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
⚠ Could not capture the token automatically.
|
|
148
|
+
If a browser opened, complete the auth and copy the token it shows.
|
|
149
|
+
|
|
150
|
+
Paste your token here (or press Enter to skip) ›
|
|
151
|
+
```
|
|
141
152
|
|
|
142
|
-
|
|
153
|
+
Just paste the token from your terminal and LazyKit sets the secret for you. If you skip, you'll get step-by-step instructions to add it manually.
|
|
143
154
|
|
|
144
155
|
**Token expiry:** OAuth tokens can expire. Run `lazykit status` to check the age of your token. If it's expired, re-run `npx @slahon/lazykit@latest init` to generate and store a fresh one.
|
|
145
156
|
|
package/git.js
CHANGED
|
@@ -91,6 +91,15 @@ function detectStack() {
|
|
|
91
91
|
return stack.length > 0 ? stack.join(' + ') : null;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
function isClaudeAvailable() {
|
|
95
|
+
try {
|
|
96
|
+
execSync('claude --version', { stdio: 'ignore' });
|
|
97
|
+
return true;
|
|
98
|
+
} catch {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
94
103
|
function hasBranchProtection({ owner, repo }) {
|
|
95
104
|
try {
|
|
96
105
|
execSync(`gh api repos/${owner}/${repo}/branches/main/protection`, { stdio: 'pipe' });
|
|
@@ -100,4 +109,4 @@ function hasBranchProtection({ owner, repo }) {
|
|
|
100
109
|
}
|
|
101
110
|
}
|
|
102
111
|
|
|
103
|
-
module.exports = { getGitRemote, parseGitHubRepo, isGitRepo, isGhCliAvailable, isGhAuthenticated, detectStack, hasBranchProtection };
|
|
112
|
+
module.exports = { getGitRemote, parseGitHubRepo, isGitRepo, isGhCliAvailable, isGhAuthenticated, isClaudeAvailable, detectStack, hasBranchProtection };
|
package/init.js
CHANGED
|
@@ -7,8 +7,8 @@ const chalk = require('chalk');
|
|
|
7
7
|
const ora = require('ora');
|
|
8
8
|
|
|
9
9
|
const { log } = require('./logger');
|
|
10
|
-
const { confirm } = require('./prompt');
|
|
11
|
-
const { isGitRepo, getGitRemote, parseGitHubRepo, isGhCliAvailable, isGhAuthenticated, detectStack, hasBranchProtection } = require('./git');
|
|
10
|
+
const { ask, confirm } = require('./prompt');
|
|
11
|
+
const { isGitRepo, getGitRemote, parseGitHubRepo, isGhCliAvailable, isGhAuthenticated, isClaudeAvailable, detectStack, hasBranchProtection } = require('./git');
|
|
12
12
|
const { generateWorkflow } = require('./workflow');
|
|
13
13
|
const { generateClaudeMd } = require('./claude-md');
|
|
14
14
|
const { generateIssueTemplate } = require('./issue-template');
|
|
@@ -45,28 +45,38 @@ async function init({ dryRun = false } = {}) {
|
|
|
45
45
|
process.exit(1);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
// ─── Step 2: Check
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
log.
|
|
54
|
-
console.log(chalk.gray('
|
|
55
|
-
console.log(chalk.gray('
|
|
56
|
-
console.log(chalk.gray('
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
console.log(chalk.gray('\n
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
} else {
|
|
68
|
-
ghReady = true;
|
|
48
|
+
// ─── Step 2: Check dependencies (mandatory) ──────────────────────────────
|
|
49
|
+
|
|
50
|
+
// gh CLI
|
|
51
|
+
while (!isGhCliAvailable()) {
|
|
52
|
+
log.error('GitHub CLI (gh) is required but not found.');
|
|
53
|
+
console.log(chalk.gray('\n LazyKit uses gh to create labels, set secrets, and enable PR permissions.\n'));
|
|
54
|
+
console.log(chalk.gray(' Install it from: ') + chalk.cyan('https://cli.github.com'));
|
|
55
|
+
console.log(chalk.gray(' macOS: ') + chalk.cyan('brew install gh'));
|
|
56
|
+
console.log(chalk.gray(' Windows: ') + chalk.cyan('winget install --id GitHub.cli\n'));
|
|
57
|
+
await confirm('Press Enter once gh is installed to check again', true);
|
|
58
|
+
}
|
|
59
|
+
log.success('GitHub CLI (gh) found');
|
|
60
|
+
|
|
61
|
+
// gh auth
|
|
62
|
+
while (!isGhAuthenticated()) {
|
|
63
|
+
log.error('GitHub CLI is not authenticated.');
|
|
64
|
+
console.log(chalk.gray('\n Run this in your terminal, then come back:\n'));
|
|
65
|
+
console.log(chalk.cyan(' gh auth login\n'));
|
|
66
|
+
await confirm('Press Enter once you are logged in to check again', true);
|
|
69
67
|
}
|
|
68
|
+
log.success('GitHub CLI authenticated');
|
|
69
|
+
|
|
70
|
+
// Claude Code CLI
|
|
71
|
+
while (!isClaudeAvailable()) {
|
|
72
|
+
log.error('Claude Code CLI is required but not found.');
|
|
73
|
+
console.log(chalk.gray('\n Install it with:\n'));
|
|
74
|
+
console.log(chalk.cyan(' npm install -g @anthropic-ai/claude-code\n'));
|
|
75
|
+
await confirm('Press Enter once Claude Code is installed to check again', true);
|
|
76
|
+
}
|
|
77
|
+
log.success('Claude Code CLI found');
|
|
78
|
+
|
|
79
|
+
const ghReady = true;
|
|
70
80
|
|
|
71
81
|
log.blank();
|
|
72
82
|
|
|
@@ -197,22 +207,32 @@ async function init({ dryRun = false } = {}) {
|
|
|
197
207
|
if (result.stderr) process.stderr.write(result.stderr);
|
|
198
208
|
|
|
199
209
|
const output = (result.stdout || '') + (result.stderr || '');
|
|
200
|
-
|
|
210
|
+
let token = (output.match(/sk-ant-oat[^\s]+/) || [])[0];
|
|
211
|
+
|
|
212
|
+
// Fallback: ask user to paste if auto-capture didn't work
|
|
213
|
+
if (!token) {
|
|
214
|
+
console.log();
|
|
215
|
+
log.warn('Could not capture the token automatically.');
|
|
216
|
+
console.log(chalk.gray(' If a browser opened, complete the auth and copy the token it shows.\n'));
|
|
217
|
+
const pasted = await ask('Paste your token here (or press Enter to skip)', '');
|
|
218
|
+
if (pasted && pasted.trim().startsWith('sk-ant-oat')) {
|
|
219
|
+
token = pasted.trim();
|
|
220
|
+
}
|
|
221
|
+
}
|
|
201
222
|
|
|
202
|
-
if (
|
|
223
|
+
if (token && ghReady) {
|
|
203
224
|
const secretSpinner = ora({ text: 'Adding CLAUDE_CODE_OAUTH_TOKEN to GitHub secrets...', color: 'cyan' }).start();
|
|
204
225
|
execSync(
|
|
205
|
-
`gh secret set CLAUDE_CODE_OAUTH_TOKEN --body "${
|
|
226
|
+
`gh secret set CLAUDE_CODE_OAUTH_TOKEN --body "${token}" --repo ${repoInfo.owner}/${repoInfo.repo}`,
|
|
206
227
|
{ stdio: 'pipe' }
|
|
207
228
|
);
|
|
208
229
|
secretSpinner.succeed(chalk.green('CLAUDE_CODE_OAUTH_TOKEN added to GitHub secrets'));
|
|
209
|
-
} else if (
|
|
210
|
-
log.warn('
|
|
211
|
-
console.log(chalk.gray('
|
|
212
|
-
console.log(chalk.gray('
|
|
213
|
-
console.log(chalk.cyan(` https://github.com/${repoInfo.owner}/${repoInfo.repo}/settings/secrets/actions`));
|
|
230
|
+
} else if (token && !ghReady) {
|
|
231
|
+
log.warn('gh CLI not available — add the secret manually:');
|
|
232
|
+
console.log(chalk.gray(' Go to: ') + chalk.cyan(`https://github.com/${repoInfo.owner}/${repoInfo.repo}/settings/secrets/actions`));
|
|
233
|
+
console.log(chalk.gray(' Add secret: ') + chalk.cyan('CLAUDE_CODE_OAUTH_TOKEN') + chalk.gray(' = ') + chalk.cyan(token));
|
|
214
234
|
} else {
|
|
215
|
-
throw new Error('
|
|
235
|
+
throw new Error('No token');
|
|
216
236
|
}
|
|
217
237
|
} catch (err) {
|
|
218
238
|
const isNotFound = (err.message || '').includes('ENOENT');
|
|
@@ -220,10 +240,9 @@ async function init({ dryRun = false } = {}) {
|
|
|
220
240
|
log.warn('Claude Code CLI not found — install it first:');
|
|
221
241
|
console.log(chalk.cyan('\n npm install -g @anthropic-ai/claude-code'));
|
|
222
242
|
} else {
|
|
223
|
-
log.warn('
|
|
243
|
+
log.warn('Token not set — add it manually:');
|
|
224
244
|
}
|
|
225
|
-
console.log(chalk.gray('\n
|
|
226
|
-
console.log(chalk.gray(' 1. Run: ') + chalk.cyan('claude setup-token'));
|
|
245
|
+
console.log(chalk.gray('\n 1. Run: ') + chalk.cyan('claude setup-token'));
|
|
227
246
|
console.log(chalk.gray(' 2. Go to: ') + chalk.cyan(`https://github.com/${repoInfo.owner}/${repoInfo.repo}/settings/secrets/actions`));
|
|
228
247
|
console.log(chalk.gray(' 3. Add secret: ') + chalk.cyan('CLAUDE_CODE_OAUTH_TOKEN') + chalk.gray(' = the token you copied\n'));
|
|
229
248
|
}
|