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.
Files changed (67) hide show
  1. package/LICENSE +21 -0
  2. package/OVERVIEW.md +597 -0
  3. package/README.md +439 -0
  4. package/bin/gtask.js +282 -0
  5. package/bin/postinstall.js +53 -0
  6. package/package.json +69 -0
  7. package/src/agents/phase-dev-templates.js +1011 -0
  8. package/src/agents/templates.js +668 -0
  9. package/src/analysis/checklist-parser.js +414 -0
  10. package/src/analysis/codebase.js +481 -0
  11. package/src/cli/menu.js +958 -0
  12. package/src/commands/claude-audit.js +1482 -0
  13. package/src/commands/claude-settings.js +2243 -0
  14. package/src/commands/create-agent.js +681 -0
  15. package/src/commands/create-command.js +337 -0
  16. package/src/commands/create-hook.js +262 -0
  17. package/src/commands/create-phase-dev/codebase-analyzer.js +813 -0
  18. package/src/commands/create-phase-dev/documentation-generator.js +352 -0
  19. package/src/commands/create-phase-dev/post-completion.js +404 -0
  20. package/src/commands/create-phase-dev/scale-calculator.js +344 -0
  21. package/src/commands/create-phase-dev/wizard.js +492 -0
  22. package/src/commands/create-phase-dev.js +481 -0
  23. package/src/commands/create-skill.js +313 -0
  24. package/src/commands/create.js +446 -0
  25. package/src/commands/decompose.js +392 -0
  26. package/src/commands/detect-tech-stack.js +768 -0
  27. package/src/commands/explore-mcp/claude-md-updater.js +252 -0
  28. package/src/commands/explore-mcp/mcp-installer.js +346 -0
  29. package/src/commands/explore-mcp/mcp-registry.js +438 -0
  30. package/src/commands/explore-mcp.js +638 -0
  31. package/src/commands/gtask-init.js +641 -0
  32. package/src/commands/help.js +128 -0
  33. package/src/commands/init.js +1890 -0
  34. package/src/commands/install.js +250 -0
  35. package/src/commands/list.js +116 -0
  36. package/src/commands/roadmap.js +750 -0
  37. package/src/commands/setup-wizard.js +482 -0
  38. package/src/commands/setup.js +351 -0
  39. package/src/commands/sync.js +534 -0
  40. package/src/commands/test-run.js +456 -0
  41. package/src/commands/test-setup.js +456 -0
  42. package/src/commands/validate.js +67 -0
  43. package/src/config/tech-stack.defaults.json +182 -0
  44. package/src/config/tech-stack.schema.json +502 -0
  45. package/src/github/client.js +359 -0
  46. package/src/index.js +84 -0
  47. package/src/templates/claude-command.js +244 -0
  48. package/src/templates/issue-body.js +284 -0
  49. package/src/testing/config.js +411 -0
  50. package/src/utils/template-engine.js +398 -0
  51. package/src/utils/validate-templates.js +223 -0
  52. package/src/utils.js +396 -0
  53. package/templates/commands/ccasp-setup.template.md +113 -0
  54. package/templates/commands/context-audit.template.md +97 -0
  55. package/templates/commands/create-task-list.template.md +382 -0
  56. package/templates/commands/deploy-full.template.md +261 -0
  57. package/templates/commands/github-task-start.template.md +99 -0
  58. package/templates/commands/github-update.template.md +69 -0
  59. package/templates/commands/happy-start.template.md +117 -0
  60. package/templates/commands/phase-track.template.md +142 -0
  61. package/templates/commands/tunnel-start.template.md +127 -0
  62. package/templates/commands/tunnel-stop.template.md +106 -0
  63. package/templates/hooks/context-guardian.template.js +173 -0
  64. package/templates/hooks/deployment-orchestrator.template.js +219 -0
  65. package/templates/hooks/github-progress-hook.template.js +197 -0
  66. package/templates/hooks/happy-checkpoint-manager.template.js +222 -0
  67. 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 };