claude-cli-advanced-starter-pack 1.0.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/OVERVIEW.md +597 -0
- package/README.md +439 -0
- package/bin/gtask.js +282 -0
- package/bin/postinstall.js +53 -0
- package/package.json +69 -0
- package/src/agents/phase-dev-templates.js +1011 -0
- package/src/agents/templates.js +668 -0
- package/src/analysis/checklist-parser.js +414 -0
- package/src/analysis/codebase.js +481 -0
- package/src/cli/menu.js +958 -0
- package/src/commands/claude-audit.js +1482 -0
- package/src/commands/claude-settings.js +2243 -0
- package/src/commands/create-agent.js +681 -0
- package/src/commands/create-command.js +337 -0
- package/src/commands/create-hook.js +262 -0
- package/src/commands/create-phase-dev/codebase-analyzer.js +813 -0
- package/src/commands/create-phase-dev/documentation-generator.js +352 -0
- package/src/commands/create-phase-dev/post-completion.js +404 -0
- package/src/commands/create-phase-dev/scale-calculator.js +344 -0
- package/src/commands/create-phase-dev/wizard.js +492 -0
- package/src/commands/create-phase-dev.js +481 -0
- package/src/commands/create-skill.js +313 -0
- package/src/commands/create.js +446 -0
- package/src/commands/decompose.js +392 -0
- package/src/commands/detect-tech-stack.js +768 -0
- package/src/commands/explore-mcp/claude-md-updater.js +252 -0
- package/src/commands/explore-mcp/mcp-installer.js +346 -0
- package/src/commands/explore-mcp/mcp-registry.js +438 -0
- package/src/commands/explore-mcp.js +638 -0
- package/src/commands/gtask-init.js +641 -0
- package/src/commands/help.js +128 -0
- package/src/commands/init.js +1890 -0
- package/src/commands/install.js +250 -0
- package/src/commands/list.js +116 -0
- package/src/commands/roadmap.js +750 -0
- package/src/commands/setup-wizard.js +482 -0
- package/src/commands/setup.js +351 -0
- package/src/commands/sync.js +534 -0
- package/src/commands/test-run.js +456 -0
- package/src/commands/test-setup.js +456 -0
- package/src/commands/validate.js +67 -0
- package/src/config/tech-stack.defaults.json +182 -0
- package/src/config/tech-stack.schema.json +502 -0
- package/src/github/client.js +359 -0
- package/src/index.js +84 -0
- package/src/templates/claude-command.js +244 -0
- package/src/templates/issue-body.js +284 -0
- package/src/testing/config.js +411 -0
- package/src/utils/template-engine.js +398 -0
- package/src/utils/validate-templates.js +223 -0
- package/src/utils.js +396 -0
- package/templates/commands/ccasp-setup.template.md +113 -0
- package/templates/commands/context-audit.template.md +97 -0
- package/templates/commands/create-task-list.template.md +382 -0
- package/templates/commands/deploy-full.template.md +261 -0
- package/templates/commands/github-task-start.template.md +99 -0
- package/templates/commands/github-update.template.md +69 -0
- package/templates/commands/happy-start.template.md +117 -0
- package/templates/commands/phase-track.template.md +142 -0
- package/templates/commands/tunnel-start.template.md +127 -0
- package/templates/commands/tunnel-stop.template.md +106 -0
- package/templates/hooks/context-guardian.template.js +173 -0
- package/templates/hooks/deployment-orchestrator.template.js +219 -0
- package/templates/hooks/github-progress-hook.template.js +197 -0
- package/templates/hooks/happy-checkpoint-manager.template.js +222 -0
- package/templates/hooks/phase-dev-enforcer.template.js +183 -0
|
@@ -0,0 +1,641 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gtask-init - Global Project Initialization
|
|
3
|
+
*
|
|
4
|
+
* This command can be run from any Claude Code session to initialize
|
|
5
|
+
* the advanced starter pack in the current project directory.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Auto-detects tech stack from codebase
|
|
9
|
+
* - Creates tech-stack.json with detected values
|
|
10
|
+
* - Generates templated .claude files
|
|
11
|
+
* - Sets up GitHub integration
|
|
12
|
+
* - Configures testing framework
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import chalk from 'chalk';
|
|
16
|
+
import inquirer from 'inquirer';
|
|
17
|
+
import ora from 'ora';
|
|
18
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, copyFileSync } from 'fs';
|
|
19
|
+
import { join, dirname } from 'path';
|
|
20
|
+
import { fileURLToPath } from 'url';
|
|
21
|
+
import { detectTechStack } from './detect-tech-stack.js';
|
|
22
|
+
import { processDirectory, generateTechStack } from '../utils/template-engine.js';
|
|
23
|
+
import { showHeader, showSuccess, showError, showWarning, showInfo } from '../cli/menu.js';
|
|
24
|
+
|
|
25
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
26
|
+
const __dirname = dirname(__filename);
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Main initialization wizard
|
|
30
|
+
*/
|
|
31
|
+
export async function runGtaskInit(options = {}) {
|
|
32
|
+
const projectRoot = options.projectRoot || process.cwd();
|
|
33
|
+
|
|
34
|
+
showHeader('Claude CLI Advanced Starter Pack - Project Setup');
|
|
35
|
+
|
|
36
|
+
console.log(chalk.dim('This wizard will configure the advanced starter pack for your project.\n'));
|
|
37
|
+
|
|
38
|
+
// Check if already initialized
|
|
39
|
+
const techStackPath = join(projectRoot, '.claude', 'config', 'tech-stack.json');
|
|
40
|
+
if (existsSync(techStackPath)) {
|
|
41
|
+
const { proceed } = await inquirer.prompt([
|
|
42
|
+
{
|
|
43
|
+
type: 'confirm',
|
|
44
|
+
name: 'proceed',
|
|
45
|
+
message: chalk.yellow('Project already initialized. Re-run setup?'),
|
|
46
|
+
default: false,
|
|
47
|
+
},
|
|
48
|
+
]);
|
|
49
|
+
if (!proceed) {
|
|
50
|
+
console.log(chalk.dim('Setup cancelled.'));
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Step 1: Detect tech stack
|
|
56
|
+
console.log(chalk.cyan('\n📦 Step 1: Detecting Tech Stack\n'));
|
|
57
|
+
|
|
58
|
+
const spinner = ora('Scanning codebase...').start();
|
|
59
|
+
let detected;
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
detected = await detectTechStack(projectRoot, { silent: true });
|
|
63
|
+
delete detected._detected;
|
|
64
|
+
spinner.succeed('Codebase analysis complete');
|
|
65
|
+
} catch (err) {
|
|
66
|
+
spinner.fail('Detection failed');
|
|
67
|
+
showError('Could not analyze codebase', err.message);
|
|
68
|
+
detected = { version: '1.0.0', project: { name: '', rootPath: '.' } };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Display detected values
|
|
72
|
+
console.log('');
|
|
73
|
+
displayDetectedStack(detected);
|
|
74
|
+
|
|
75
|
+
// Step 2: Confirm and customize
|
|
76
|
+
console.log(chalk.cyan('\n📝 Step 2: Review & Customize\n'));
|
|
77
|
+
|
|
78
|
+
const { customize } = await inquirer.prompt([
|
|
79
|
+
{
|
|
80
|
+
type: 'list',
|
|
81
|
+
name: 'customize',
|
|
82
|
+
message: 'How would you like to proceed?',
|
|
83
|
+
choices: [
|
|
84
|
+
{ name: 'Accept detected values', value: 'accept' },
|
|
85
|
+
{ name: 'Customize settings', value: 'customize' },
|
|
86
|
+
{ name: 'Start from scratch', value: 'scratch' },
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
]);
|
|
90
|
+
|
|
91
|
+
let techStack = detected;
|
|
92
|
+
|
|
93
|
+
if (customize === 'customize') {
|
|
94
|
+
techStack = await customizeSettings(detected);
|
|
95
|
+
} else if (customize === 'scratch') {
|
|
96
|
+
techStack = await fullSetup();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Step 3: Configure integrations
|
|
100
|
+
console.log(chalk.cyan('\n🔗 Step 3: Configure Integrations\n'));
|
|
101
|
+
|
|
102
|
+
techStack = await configureIntegrations(techStack);
|
|
103
|
+
|
|
104
|
+
// Step 4: Save configuration
|
|
105
|
+
console.log(chalk.cyan('\n💾 Step 4: Save Configuration\n'));
|
|
106
|
+
|
|
107
|
+
// Ensure directories exist
|
|
108
|
+
const configDir = join(projectRoot, '.claude', 'config');
|
|
109
|
+
if (!existsSync(configDir)) {
|
|
110
|
+
mkdirSync(configDir, { recursive: true });
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Save tech-stack.json
|
|
114
|
+
writeFileSync(
|
|
115
|
+
join(configDir, 'tech-stack.json'),
|
|
116
|
+
JSON.stringify(techStack, null, 2),
|
|
117
|
+
'utf8'
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
console.log(chalk.green('✓ Saved: .claude/config/tech-stack.json'));
|
|
121
|
+
|
|
122
|
+
// Step 5: Generate templates
|
|
123
|
+
const { generateTemplates } = await inquirer.prompt([
|
|
124
|
+
{
|
|
125
|
+
type: 'confirm',
|
|
126
|
+
name: 'generateTemplates',
|
|
127
|
+
message: 'Generate .claude command templates?',
|
|
128
|
+
default: true,
|
|
129
|
+
},
|
|
130
|
+
]);
|
|
131
|
+
|
|
132
|
+
if (generateTemplates) {
|
|
133
|
+
await generateCommandTemplates(projectRoot, techStack);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Step 6: Complete
|
|
137
|
+
console.log(chalk.cyan('\n✅ Setup Complete!\n'));
|
|
138
|
+
|
|
139
|
+
showSuccess('Project Initialized', [
|
|
140
|
+
'Tech stack: .claude/config/tech-stack.json',
|
|
141
|
+
generateTemplates ? 'Commands: .claude/commands/' : '',
|
|
142
|
+
'',
|
|
143
|
+
'Next steps:',
|
|
144
|
+
' 1. Review generated files',
|
|
145
|
+
' 2. Run "gtask claude-settings" to fine-tune',
|
|
146
|
+
' 3. Use "/create-task-list" to start working',
|
|
147
|
+
].filter(Boolean));
|
|
148
|
+
|
|
149
|
+
return techStack;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Display detected stack summary
|
|
154
|
+
*/
|
|
155
|
+
function displayDetectedStack(stack) {
|
|
156
|
+
const items = [];
|
|
157
|
+
|
|
158
|
+
if (stack.frontend?.framework) {
|
|
159
|
+
items.push(`Frontend: ${chalk.cyan(stack.frontend.framework)}` +
|
|
160
|
+
(stack.frontend.buildTool ? ` (${stack.frontend.buildTool})` : '') +
|
|
161
|
+
` on port ${stack.frontend.port || 5173}`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (stack.backend?.framework) {
|
|
165
|
+
items.push(`Backend: ${chalk.cyan(stack.backend.framework)}` +
|
|
166
|
+
(stack.backend.language ? ` (${stack.backend.language})` : '') +
|
|
167
|
+
` on port ${stack.backend.port || 8000}`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (stack.database?.primary) {
|
|
171
|
+
items.push(`Database: ${chalk.cyan(stack.database.primary)}` +
|
|
172
|
+
(stack.database.orm ? ` with ${stack.database.orm}` : ''));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (stack.testing?.e2e?.framework) {
|
|
176
|
+
items.push(`E2E Testing: ${chalk.cyan(stack.testing.e2e.framework)}`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (stack.testing?.unit?.framework) {
|
|
180
|
+
items.push(`Unit Testing: ${chalk.cyan(stack.testing.unit.framework)}`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (stack.versionControl?.provider) {
|
|
184
|
+
items.push(`Repository: ${chalk.cyan(stack.versionControl.owner + '/' + stack.versionControl.repo)}`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (stack.devEnvironment?.packageManager) {
|
|
188
|
+
items.push(`Package Manager: ${chalk.cyan(stack.devEnvironment.packageManager)}`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (items.length === 0) {
|
|
192
|
+
console.log(chalk.dim(' No technologies detected automatically.'));
|
|
193
|
+
} else {
|
|
194
|
+
items.forEach((item) => console.log(` ${item}`));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Quick customize essential settings
|
|
200
|
+
*/
|
|
201
|
+
async function customizeSettings(detected) {
|
|
202
|
+
const answers = await inquirer.prompt([
|
|
203
|
+
{
|
|
204
|
+
type: 'input',
|
|
205
|
+
name: 'projectName',
|
|
206
|
+
message: 'Project name:',
|
|
207
|
+
default: detected.project?.name || '',
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
type: 'list',
|
|
211
|
+
name: 'frontendFramework',
|
|
212
|
+
message: 'Frontend framework:',
|
|
213
|
+
choices: ['react', 'vue', 'angular', 'svelte', 'nextjs', 'nuxt', 'vanilla', 'none'],
|
|
214
|
+
default: detected.frontend?.framework || 'none',
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
type: 'number',
|
|
218
|
+
name: 'frontendPort',
|
|
219
|
+
message: 'Frontend port:',
|
|
220
|
+
default: detected.frontend?.port || 5173,
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
type: 'list',
|
|
224
|
+
name: 'backendFramework',
|
|
225
|
+
message: 'Backend framework:',
|
|
226
|
+
choices: ['fastapi', 'express', 'nestjs', 'django', 'flask', 'rails', 'none'],
|
|
227
|
+
default: detected.backend?.framework || 'none',
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
type: 'number',
|
|
231
|
+
name: 'backendPort',
|
|
232
|
+
message: 'Backend port:',
|
|
233
|
+
default: detected.backend?.port || 8000,
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
type: 'list',
|
|
237
|
+
name: 'e2eFramework',
|
|
238
|
+
message: 'E2E testing framework:',
|
|
239
|
+
choices: ['playwright', 'cypress', 'puppeteer', 'none'],
|
|
240
|
+
default: detected.testing?.e2e?.framework || 'none',
|
|
241
|
+
},
|
|
242
|
+
]);
|
|
243
|
+
|
|
244
|
+
// Merge answers into detected
|
|
245
|
+
return generateTechStack(detected, {
|
|
246
|
+
project: { name: answers.projectName },
|
|
247
|
+
frontend: {
|
|
248
|
+
framework: answers.frontendFramework,
|
|
249
|
+
port: answers.frontendPort,
|
|
250
|
+
},
|
|
251
|
+
backend: {
|
|
252
|
+
framework: answers.backendFramework,
|
|
253
|
+
port: answers.backendPort,
|
|
254
|
+
},
|
|
255
|
+
testing: {
|
|
256
|
+
e2e: { framework: answers.e2eFramework },
|
|
257
|
+
},
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Full setup from scratch
|
|
263
|
+
*/
|
|
264
|
+
async function fullSetup() {
|
|
265
|
+
const techStack = {
|
|
266
|
+
version: '1.0.0',
|
|
267
|
+
project: {},
|
|
268
|
+
frontend: {},
|
|
269
|
+
backend: {},
|
|
270
|
+
database: {},
|
|
271
|
+
deployment: { frontend: {}, backend: {} },
|
|
272
|
+
devEnvironment: { tunnel: {} },
|
|
273
|
+
testing: { e2e: {}, unit: {}, selectors: {}, credentials: {} },
|
|
274
|
+
versionControl: { projectBoard: {} },
|
|
275
|
+
urls: { local: {}, tunnel: {}, production: {} },
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
// Project
|
|
279
|
+
const projectAnswers = await inquirer.prompt([
|
|
280
|
+
{
|
|
281
|
+
type: 'input',
|
|
282
|
+
name: 'name',
|
|
283
|
+
message: 'Project name:',
|
|
284
|
+
default: '',
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
type: 'input',
|
|
288
|
+
name: 'description',
|
|
289
|
+
message: 'Project description:',
|
|
290
|
+
default: '',
|
|
291
|
+
},
|
|
292
|
+
]);
|
|
293
|
+
techStack.project = projectAnswers;
|
|
294
|
+
|
|
295
|
+
// Frontend
|
|
296
|
+
const frontendAnswers = await inquirer.prompt([
|
|
297
|
+
{
|
|
298
|
+
type: 'list',
|
|
299
|
+
name: 'framework',
|
|
300
|
+
message: 'Frontend framework:',
|
|
301
|
+
choices: ['react', 'vue', 'angular', 'svelte', 'nextjs', 'nuxt', 'vanilla', 'none'],
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
type: 'list',
|
|
305
|
+
name: 'buildTool',
|
|
306
|
+
message: 'Build tool:',
|
|
307
|
+
choices: ['vite', 'webpack', 'esbuild', 'parcel', 'none'],
|
|
308
|
+
default: 'vite',
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
type: 'number',
|
|
312
|
+
name: 'port',
|
|
313
|
+
message: 'Dev server port:',
|
|
314
|
+
default: 5173,
|
|
315
|
+
},
|
|
316
|
+
]);
|
|
317
|
+
techStack.frontend = frontendAnswers;
|
|
318
|
+
|
|
319
|
+
// Backend
|
|
320
|
+
const backendAnswers = await inquirer.prompt([
|
|
321
|
+
{
|
|
322
|
+
type: 'list',
|
|
323
|
+
name: 'language',
|
|
324
|
+
message: 'Backend language:',
|
|
325
|
+
choices: ['python', 'node', 'typescript', 'go', 'rust', 'none'],
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
type: 'list',
|
|
329
|
+
name: 'framework',
|
|
330
|
+
message: 'Backend framework:',
|
|
331
|
+
choices: ['fastapi', 'express', 'nestjs', 'django', 'flask', 'gin', 'none'],
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
type: 'number',
|
|
335
|
+
name: 'port',
|
|
336
|
+
message: 'Backend port:',
|
|
337
|
+
default: 8000,
|
|
338
|
+
},
|
|
339
|
+
]);
|
|
340
|
+
techStack.backend = backendAnswers;
|
|
341
|
+
|
|
342
|
+
// Database
|
|
343
|
+
const dbAnswers = await inquirer.prompt([
|
|
344
|
+
{
|
|
345
|
+
type: 'list',
|
|
346
|
+
name: 'primary',
|
|
347
|
+
message: 'Database:',
|
|
348
|
+
choices: ['postgresql', 'mysql', 'mongodb', 'sqlite', 'none'],
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
type: 'list',
|
|
352
|
+
name: 'orm',
|
|
353
|
+
message: 'ORM:',
|
|
354
|
+
choices: ['prisma', 'drizzle', 'typeorm', 'sqlalchemy', 'none'],
|
|
355
|
+
},
|
|
356
|
+
]);
|
|
357
|
+
techStack.database = dbAnswers;
|
|
358
|
+
|
|
359
|
+
// Testing
|
|
360
|
+
const testingAnswers = await inquirer.prompt([
|
|
361
|
+
{
|
|
362
|
+
type: 'list',
|
|
363
|
+
name: 'e2e',
|
|
364
|
+
message: 'E2E testing framework:',
|
|
365
|
+
choices: ['playwright', 'cypress', 'puppeteer', 'none'],
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
type: 'list',
|
|
369
|
+
name: 'unit',
|
|
370
|
+
message: 'Unit testing framework:',
|
|
371
|
+
choices: ['vitest', 'jest', 'mocha', 'pytest', 'none'],
|
|
372
|
+
},
|
|
373
|
+
]);
|
|
374
|
+
techStack.testing.e2e.framework = testingAnswers.e2e;
|
|
375
|
+
techStack.testing.unit.framework = testingAnswers.unit;
|
|
376
|
+
|
|
377
|
+
// URLs
|
|
378
|
+
techStack.urls.local = {
|
|
379
|
+
frontend: `http://localhost:${frontendAnswers.port}`,
|
|
380
|
+
backend: `http://localhost:${backendAnswers.port}`,
|
|
381
|
+
api: `http://localhost:${backendAnswers.port}/api`,
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
return techStack;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Configure integrations (GitHub, tunnel, deployment)
|
|
389
|
+
*/
|
|
390
|
+
async function configureIntegrations(techStack) {
|
|
391
|
+
// GitHub integration
|
|
392
|
+
const { hasGitHub } = await inquirer.prompt([
|
|
393
|
+
{
|
|
394
|
+
type: 'confirm',
|
|
395
|
+
name: 'hasGitHub',
|
|
396
|
+
message: 'Configure GitHub integration?',
|
|
397
|
+
default: !!techStack.versionControl?.provider,
|
|
398
|
+
},
|
|
399
|
+
]);
|
|
400
|
+
|
|
401
|
+
if (hasGitHub) {
|
|
402
|
+
const githubAnswers = await inquirer.prompt([
|
|
403
|
+
{
|
|
404
|
+
type: 'input',
|
|
405
|
+
name: 'owner',
|
|
406
|
+
message: 'GitHub username/org:',
|
|
407
|
+
default: techStack.versionControl?.owner || '',
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
type: 'input',
|
|
411
|
+
name: 'repo',
|
|
412
|
+
message: 'Repository name:',
|
|
413
|
+
default: techStack.versionControl?.repo || '',
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
type: 'list',
|
|
417
|
+
name: 'projectBoard',
|
|
418
|
+
message: 'Project board type:',
|
|
419
|
+
choices: ['github-projects', 'jira', 'linear', 'trello', 'none'],
|
|
420
|
+
default: 'github-projects',
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
type: 'number',
|
|
424
|
+
name: 'projectNumber',
|
|
425
|
+
message: 'GitHub Project number (if using GitHub Projects):',
|
|
426
|
+
default: null,
|
|
427
|
+
when: (answers) => answers.projectBoard === 'github-projects',
|
|
428
|
+
},
|
|
429
|
+
]);
|
|
430
|
+
|
|
431
|
+
techStack.versionControl = {
|
|
432
|
+
...techStack.versionControl,
|
|
433
|
+
provider: 'github',
|
|
434
|
+
owner: githubAnswers.owner,
|
|
435
|
+
repo: githubAnswers.repo,
|
|
436
|
+
projectBoard: {
|
|
437
|
+
type: githubAnswers.projectBoard,
|
|
438
|
+
number: githubAnswers.projectNumber || null,
|
|
439
|
+
},
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Tunnel configuration
|
|
444
|
+
const { hasTunnel } = await inquirer.prompt([
|
|
445
|
+
{
|
|
446
|
+
type: 'confirm',
|
|
447
|
+
name: 'hasTunnel',
|
|
448
|
+
message: 'Configure tunnel service (ngrok, etc.)?',
|
|
449
|
+
default: false,
|
|
450
|
+
},
|
|
451
|
+
]);
|
|
452
|
+
|
|
453
|
+
if (hasTunnel) {
|
|
454
|
+
const tunnelAnswers = await inquirer.prompt([
|
|
455
|
+
{
|
|
456
|
+
type: 'list',
|
|
457
|
+
name: 'service',
|
|
458
|
+
message: 'Tunnel service:',
|
|
459
|
+
choices: ['ngrok', 'localtunnel', 'cloudflare-tunnel', 'none'],
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
type: 'input',
|
|
463
|
+
name: 'subdomain',
|
|
464
|
+
message: 'Subdomain (if reserved):',
|
|
465
|
+
default: '',
|
|
466
|
+
when: (answers) => answers.service === 'ngrok',
|
|
467
|
+
},
|
|
468
|
+
]);
|
|
469
|
+
|
|
470
|
+
techStack.devEnvironment = {
|
|
471
|
+
...techStack.devEnvironment,
|
|
472
|
+
tunnel: {
|
|
473
|
+
service: tunnelAnswers.service,
|
|
474
|
+
subdomain: tunnelAnswers.subdomain || null,
|
|
475
|
+
adminPort: tunnelAnswers.service === 'ngrok' ? 4040 : null,
|
|
476
|
+
},
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
if (tunnelAnswers.subdomain) {
|
|
480
|
+
techStack.urls.tunnel = {
|
|
481
|
+
frontend: `https://${tunnelAnswers.subdomain}.ngrok.dev`,
|
|
482
|
+
backend: `https://${tunnelAnswers.subdomain}.ngrok.dev`,
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Deployment configuration
|
|
488
|
+
const { hasDeployment } = await inquirer.prompt([
|
|
489
|
+
{
|
|
490
|
+
type: 'confirm',
|
|
491
|
+
name: 'hasDeployment',
|
|
492
|
+
message: 'Configure deployment platforms?',
|
|
493
|
+
default: false,
|
|
494
|
+
},
|
|
495
|
+
]);
|
|
496
|
+
|
|
497
|
+
if (hasDeployment) {
|
|
498
|
+
const deployAnswers = await inquirer.prompt([
|
|
499
|
+
{
|
|
500
|
+
type: 'list',
|
|
501
|
+
name: 'frontend',
|
|
502
|
+
message: 'Frontend deployment:',
|
|
503
|
+
choices: ['cloudflare', 'vercel', 'netlify', 'github-pages', 'self-hosted', 'none'],
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
type: 'input',
|
|
507
|
+
name: 'frontendUrl',
|
|
508
|
+
message: 'Frontend production URL:',
|
|
509
|
+
default: '',
|
|
510
|
+
when: (answers) => answers.frontend !== 'none',
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
type: 'list',
|
|
514
|
+
name: 'backend',
|
|
515
|
+
message: 'Backend deployment:',
|
|
516
|
+
choices: ['railway', 'heroku', 'render', 'fly', 'self-hosted', 'none'],
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
type: 'input',
|
|
520
|
+
name: 'backendUrl',
|
|
521
|
+
message: 'Backend production URL:',
|
|
522
|
+
default: '',
|
|
523
|
+
when: (answers) => answers.backend !== 'none',
|
|
524
|
+
},
|
|
525
|
+
]);
|
|
526
|
+
|
|
527
|
+
techStack.deployment = {
|
|
528
|
+
frontend: {
|
|
529
|
+
platform: deployAnswers.frontend,
|
|
530
|
+
productionUrl: deployAnswers.frontendUrl || '',
|
|
531
|
+
},
|
|
532
|
+
backend: {
|
|
533
|
+
platform: deployAnswers.backend,
|
|
534
|
+
productionUrl: deployAnswers.backendUrl || '',
|
|
535
|
+
},
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
techStack.urls.production = {
|
|
539
|
+
frontend: deployAnswers.frontendUrl || '',
|
|
540
|
+
backend: deployAnswers.backendUrl || '',
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
return techStack;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Generate command templates
|
|
549
|
+
*/
|
|
550
|
+
async function generateCommandTemplates(projectRoot, techStack) {
|
|
551
|
+
const spinner = ora('Generating command templates...').start();
|
|
552
|
+
|
|
553
|
+
const commandsDir = join(projectRoot, '.claude', 'commands');
|
|
554
|
+
if (!existsSync(commandsDir)) {
|
|
555
|
+
mkdirSync(commandsDir, { recursive: true });
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Generate TESTING_RULES.md
|
|
559
|
+
const testingRulesContent = generateTestingRules(techStack);
|
|
560
|
+
writeFileSync(
|
|
561
|
+
join(projectRoot, '.claude', 'task-lists', 'TESTING_RULES.md'),
|
|
562
|
+
testingRulesContent,
|
|
563
|
+
'utf8'
|
|
564
|
+
);
|
|
565
|
+
|
|
566
|
+
// Ensure task-lists dir exists
|
|
567
|
+
const taskListsDir = join(projectRoot, '.claude', 'task-lists');
|
|
568
|
+
if (!existsSync(taskListsDir)) {
|
|
569
|
+
mkdirSync(taskListsDir, { recursive: true });
|
|
570
|
+
}
|
|
571
|
+
writeFileSync(
|
|
572
|
+
join(taskListsDir, 'TESTING_RULES.md'),
|
|
573
|
+
testingRulesContent,
|
|
574
|
+
'utf8'
|
|
575
|
+
);
|
|
576
|
+
|
|
577
|
+
spinner.succeed('Command templates generated');
|
|
578
|
+
console.log(chalk.green(' ✓ .claude/task-lists/TESTING_RULES.md'));
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* Generate TESTING_RULES.md from tech stack
|
|
583
|
+
*/
|
|
584
|
+
function generateTestingRules(techStack) {
|
|
585
|
+
const tunnelUrl = techStack.urls?.tunnel?.frontend || '{{TUNNEL_URL}}';
|
|
586
|
+
const prodUrl = techStack.urls?.production?.frontend || '{{PRODUCTION_URL}}';
|
|
587
|
+
const backendUrl = techStack.urls?.production?.backend || '{{BACKEND_URL}}';
|
|
588
|
+
const selectors = techStack.testing?.selectors || {};
|
|
589
|
+
|
|
590
|
+
return `# Testing Rules (Auto-Generated)
|
|
591
|
+
|
|
592
|
+
## Environment URLs
|
|
593
|
+
|
|
594
|
+
| Environment | Frontend | Backend |
|
|
595
|
+
|-------------|----------|---------|
|
|
596
|
+
| Local | http://localhost:${techStack.frontend?.port || 5173} | http://localhost:${techStack.backend?.port || 8000} |
|
|
597
|
+
| Tunnel | ${tunnelUrl} | ${tunnelUrl} |
|
|
598
|
+
| Production | ${prodUrl} | ${backendUrl} |
|
|
599
|
+
|
|
600
|
+
## Login Credentials
|
|
601
|
+
|
|
602
|
+
- **Username Variable**: \`${techStack.testing?.credentials?.usernameEnvVar || 'TEST_USER_USERNAME'}\`
|
|
603
|
+
- **Password Variable**: \`${techStack.testing?.credentials?.passwordEnvVar || 'TEST_USER_PASSWORD'}\`
|
|
604
|
+
|
|
605
|
+
## Login Selectors
|
|
606
|
+
|
|
607
|
+
| Element | Selector |
|
|
608
|
+
|---------|----------|
|
|
609
|
+
| Username | \`${selectors.username || '[data-testid="username-input"]'}\` |
|
|
610
|
+
| Password | \`${selectors.password || '[data-testid="password-input"]'}\` |
|
|
611
|
+
| Login Button | \`${selectors.loginButton || '[data-testid="login-submit"]'}\` |
|
|
612
|
+
| Success Indicator | \`${selectors.loginSuccess || '[data-testid="dashboard"]'}\` |
|
|
613
|
+
|
|
614
|
+
## E2E Testing
|
|
615
|
+
|
|
616
|
+
- **Framework**: ${techStack.testing?.e2e?.framework || 'playwright'}
|
|
617
|
+
- **Config**: ${techStack.testing?.e2e?.configFile || 'playwright.config.ts'}
|
|
618
|
+
- **Command**: \`${techStack.testing?.e2e?.testCommand || 'npx playwright test'}\`
|
|
619
|
+
|
|
620
|
+
## Unit Testing
|
|
621
|
+
|
|
622
|
+
- **Framework**: ${techStack.testing?.unit?.framework || 'vitest'}
|
|
623
|
+
- **Command**: \`${techStack.testing?.unit?.testCommand || 'npm test'}\`
|
|
624
|
+
|
|
625
|
+
## Deployment
|
|
626
|
+
|
|
627
|
+
- **Frontend Platform**: ${techStack.deployment?.frontend?.platform || 'not configured'}
|
|
628
|
+
- **Backend Platform**: ${techStack.deployment?.backend?.platform || 'not configured'}
|
|
629
|
+
|
|
630
|
+
## Task Workflow
|
|
631
|
+
|
|
632
|
+
1. Debug with curl/fetch first
|
|
633
|
+
2. ${techStack.testing?.e2e?.framework ? `Run ${techStack.testing.e2e.framework} tests` : 'Manual testing'}
|
|
634
|
+
3. Commit after each task
|
|
635
|
+
|
|
636
|
+
---
|
|
637
|
+
*Generated by gtask-init on ${new Date().toISOString()}*
|
|
638
|
+
`;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
export default { runGtaskInit };
|