@slahon/lazykit 1.0.5 → 1.0.7
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/claude-md.js +1 -1
- package/git.js +38 -1
- package/init.js +52 -27
- package/package.json +1 -1
package/claude-md.js
CHANGED
|
@@ -4,7 +4,7 @@ function generateClaudeMd({ stack }) {
|
|
|
4
4
|
return `# LazyKit — Project Guide for Claude
|
|
5
5
|
|
|
6
6
|
## Stack
|
|
7
|
-
${stack || '
|
|
7
|
+
${stack || 'Update this section with your tech stack (e.g. TypeScript + Next.js, Postgres via Prisma)'}
|
|
8
8
|
|
|
9
9
|
## Conventions
|
|
10
10
|
- Match the existing code style and patterns in whatever file you are editing.
|
package/git.js
CHANGED
|
@@ -45,4 +45,41 @@ function isGhCliAvailable() {
|
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
function detectStack() {
|
|
49
|
+
const cwd = process.cwd();
|
|
50
|
+
const stack = [];
|
|
51
|
+
|
|
52
|
+
const pkgPath = require('path').join(cwd, 'package.json');
|
|
53
|
+
if (require('fs').existsSync(pkgPath)) {
|
|
54
|
+
try {
|
|
55
|
+
const pkg = JSON.parse(require('fs').readFileSync(pkgPath, 'utf8'));
|
|
56
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
57
|
+
|
|
58
|
+
if (deps['next']) stack.push('Next.js');
|
|
59
|
+
else if (deps['react']) stack.push('React');
|
|
60
|
+
else if (deps['vue']) stack.push('Vue');
|
|
61
|
+
else if (deps['@angular/core']) stack.push('Angular');
|
|
62
|
+
else if (deps['svelte']) stack.push('Svelte');
|
|
63
|
+
else if (deps['express']) stack.push('Express');
|
|
64
|
+
else if (deps['fastify']) stack.push('Fastify');
|
|
65
|
+
|
|
66
|
+
if (deps['typescript'] || require('fs').existsSync(require('path').join(cwd, 'tsconfig.json'))) stack.push('TypeScript');
|
|
67
|
+
|
|
68
|
+
if (deps['@prisma/client'] || deps['prisma']) stack.push('Prisma');
|
|
69
|
+
else if (deps['mongoose']) stack.push('MongoDB');
|
|
70
|
+
else if (deps['pg'] || deps['postgres']) stack.push('PostgreSQL');
|
|
71
|
+
else if (deps['mysql2']) stack.push('MySQL');
|
|
72
|
+
} catch {}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (require('fs').existsSync(require('path').join(cwd, 'requirements.txt')) ||
|
|
76
|
+
require('fs').existsSync(require('path').join(cwd, 'pyproject.toml'))) stack.push('Python');
|
|
77
|
+
if (require('fs').existsSync(require('path').join(cwd, 'go.mod'))) stack.push('Go');
|
|
78
|
+
if (require('fs').existsSync(require('path').join(cwd, 'Cargo.toml'))) stack.push('Rust');
|
|
79
|
+
if (require('fs').existsSync(require('path').join(cwd, 'pom.xml')) ||
|
|
80
|
+
require('fs').existsSync(require('path').join(cwd, 'build.gradle'))) stack.push('Java');
|
|
81
|
+
|
|
82
|
+
return stack.length > 0 ? stack.join(' + ') : null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
module.exports = { getGitRemote, parseGitHubRepo, isGitRepo, isGhCliAvailable, detectStack };
|
package/init.js
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
|
-
const { execSync } = require('child_process');
|
|
5
|
+
const { execSync, spawnSync } = require('child_process');
|
|
6
6
|
const chalk = require('chalk');
|
|
7
7
|
const ora = require('ora');
|
|
8
8
|
|
|
9
9
|
const { log } = require('./logger');
|
|
10
10
|
const { ask, confirm, select } = require('./prompt');
|
|
11
|
-
const { isGitRepo, getGitRemote, parseGitHubRepo, isGhCliAvailable } = require('./git');
|
|
11
|
+
const { isGitRepo, getGitRemote, parseGitHubRepo, isGhCliAvailable, detectStack } = require('./git');
|
|
12
12
|
const { generateWorkflow } = require('./workflow');
|
|
13
13
|
const { generateClaudeMd } = require('./claude-md');
|
|
14
14
|
|
|
@@ -29,8 +29,11 @@ async function init() {
|
|
|
29
29
|
const remote = getGitRemote();
|
|
30
30
|
const repoInfo = parseGitHubRepo(remote);
|
|
31
31
|
|
|
32
|
+
const stack = detectStack();
|
|
33
|
+
|
|
32
34
|
if (repoInfo) {
|
|
33
35
|
log.success(`Detected repo: ${chalk.cyan(`${repoInfo.owner}/${repoInfo.repo}`)}`);
|
|
36
|
+
if (stack) log.success(`Detected stack: ${chalk.cyan(stack)}`);
|
|
34
37
|
} else {
|
|
35
38
|
log.error('No GitHub remote detected.');
|
|
36
39
|
console.log(chalk.gray('\n LazyKit requires a GitHub repository with a remote set up.\n'));
|
|
@@ -48,8 +51,6 @@ async function init() {
|
|
|
48
51
|
|
|
49
52
|
const label = await ask('Label name to trigger Claude', 'lazykit');
|
|
50
53
|
|
|
51
|
-
const stack = await ask('What is your stack?', 'e.g. TypeScript + Next.js, Postgres');
|
|
52
|
-
|
|
53
54
|
const wantClaudeMd = await confirm('Generate CLAUDE.md project guide?', true);
|
|
54
55
|
|
|
55
56
|
log.blank();
|
|
@@ -108,36 +109,60 @@ async function init() {
|
|
|
108
109
|
log.warn(`gh CLI not found — create the '${label}' label manually on GitHub`);
|
|
109
110
|
}
|
|
110
111
|
|
|
111
|
-
// ─── Step 6:
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
// ─── Step 6: Enable Actions to create PRs ────────────────────────────────
|
|
113
|
+
if (ghAvailable && repoInfo) {
|
|
114
|
+
const prSpinner = ora({ text: 'Enabling Actions PR creation permission...', color: 'cyan' }).start();
|
|
115
|
+
try {
|
|
116
|
+
execSync(
|
|
117
|
+
`gh api repos/${repoInfo.owner}/${repoInfo.repo}/actions/permissions/workflow --method PUT --field default_workflow_permissions=write --field can_approve_pull_request_reviews=true`,
|
|
118
|
+
{ stdio: 'pipe' }
|
|
119
|
+
);
|
|
120
|
+
prSpinner.succeed(chalk.green('Enabled: Actions can create and approve pull requests'));
|
|
121
|
+
} catch {
|
|
122
|
+
prSpinner.warn(chalk.yellow('Could not enable PR creation automatically — enable it manually in repo Settings → Actions → General'));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
114
125
|
|
|
115
|
-
|
|
126
|
+
// ─── Step 7: Generate and set Claude token ───────────────────────────────
|
|
127
|
+
console.log('\n' + chalk.bold.green(' ✨ Almost there — setting up your Claude token...\n'));
|
|
116
128
|
|
|
117
|
-
|
|
118
|
-
console.log(chalk.cyan(' claude setup-token'));
|
|
129
|
+
let tokenSet = false;
|
|
119
130
|
|
|
120
|
-
|
|
131
|
+
try {
|
|
132
|
+
log.info('Running claude setup-token (a browser window may open to authenticate)...');
|
|
133
|
+
console.log();
|
|
121
134
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
console.log(chalk.gray('\n 3.') + ' Go to:');
|
|
127
|
-
console.log(chalk.cyan(' Your repo → Settings → Secrets and variables → Actions'));
|
|
128
|
-
}
|
|
135
|
+
const result = spawnSync('claude', ['setup-token'], {
|
|
136
|
+
stdio: ['inherit', 'pipe', 'inherit'],
|
|
137
|
+
encoding: 'utf8',
|
|
138
|
+
});
|
|
129
139
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
console.log(chalk.gray(' Value: ') + chalk.cyan('the token you copied'));
|
|
140
|
+
const output = result.stdout || '';
|
|
141
|
+
const tokenMatch = output.match(/sk-ant-oat\d*-[\w-]+/);
|
|
133
142
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
143
|
+
if (tokenMatch && ghAvailable) {
|
|
144
|
+
const token = tokenMatch[0];
|
|
145
|
+
const secretSpinner = ora({ text: 'Adding CLAUDE_CODE_OAUTH_TOKEN to GitHub secrets...', color: 'cyan' }).start();
|
|
146
|
+
execSync(
|
|
147
|
+
`gh secret set CLAUDE_CODE_OAUTH_TOKEN --body "${token}" --repo ${repoInfo.owner}/${repoInfo.repo}`,
|
|
148
|
+
{ stdio: 'pipe' }
|
|
149
|
+
);
|
|
150
|
+
secretSpinner.succeed(chalk.green('CLAUDE_CODE_OAUTH_TOKEN added to GitHub secrets'));
|
|
151
|
+
tokenSet = true;
|
|
152
|
+
} else if (tokenMatch && !ghAvailable) {
|
|
153
|
+
log.warn('Token generated but gh CLI not found — add it manually:');
|
|
154
|
+
console.log(chalk.gray(' Name: ') + chalk.cyan('CLAUDE_CODE_OAUTH_TOKEN'));
|
|
155
|
+
console.log(chalk.gray(' Value: ') + chalk.cyan(tokenMatch[0]));
|
|
156
|
+
console.log(chalk.cyan(` https://github.com/${repoInfo.owner}/${repoInfo.repo}/settings/secrets/actions`));
|
|
157
|
+
} else {
|
|
158
|
+
throw new Error('Token not found in output');
|
|
159
|
+
}
|
|
160
|
+
} catch {
|
|
161
|
+
log.warn('Could not set token automatically. Add it manually:');
|
|
162
|
+
console.log(chalk.gray('\n 1. Run: ') + chalk.cyan('claude setup-token'));
|
|
163
|
+
console.log(chalk.gray(' 2. Go to: ') + chalk.cyan(`https://github.com/${repoInfo.owner}/${repoInfo.repo}/settings/secrets/actions`));
|
|
164
|
+
console.log(chalk.gray(' 3. Add secret: ') + chalk.cyan('CLAUDE_CODE_OAUTH_TOKEN'));
|
|
139
165
|
}
|
|
140
|
-
console.log(chalk.gray(' Enable: ') + '"Allow GitHub Actions to create and approve pull requests"');
|
|
141
166
|
|
|
142
167
|
log.divider();
|
|
143
168
|
|