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,958 @@
1
+ /**
2
+ * Interactive ASCII Menu System
3
+ */
4
+
5
+ import chalk from 'chalk';
6
+ import inquirer from 'inquirer';
7
+ import { runSetup } from '../commands/setup.js';
8
+ import { runCreate } from '../commands/create.js';
9
+ import { runList } from '../commands/list.js';
10
+ import { runInstall } from '../commands/install.js';
11
+ import { runDecompose } from '../commands/decompose.js';
12
+ import { runSync } from '../commands/sync.js';
13
+ import { runTestSetup } from '../commands/test-setup.js';
14
+ import { runTest } from '../commands/test-run.js';
15
+ import { runCreateAgent } from '../commands/create-agent.js';
16
+ import { runClaudeSettings } from '../commands/claude-settings.js';
17
+ import { runCreatePhaseDev, showPhasDevMainMenu } from '../commands/create-phase-dev.js';
18
+ import { runExploreMcp, showExploreMcpMenu } from '../commands/explore-mcp.js';
19
+ import { runClaudeAudit, showClaudeAuditMenu } from '../commands/claude-audit.js';
20
+ import { runRoadmap, showRoadmapMenu } from '../commands/roadmap.js';
21
+ import { hasTestingConfig } from '../testing/config.js';
22
+ import { showHelp } from '../commands/help.js';
23
+ import { hasValidConfig, getVersion, loadTechStack, saveTechStack } from '../utils.js';
24
+
25
+ /**
26
+ * ASCII Art Banner
27
+ */
28
+ const BANNER = `
29
+ ╔════════════════════════════════════════════════════════════════════════════╗
30
+ ║ ║
31
+ ║ ╔═╗╦ ╔═╗╦ ╦╔╦╗╔═╗ ╔═╗╔╦╗╦ ╦╔═╗╔╗╔╔═╗╔═╗╔╦╗ ╔═╗╔╦╗╔═╗╦═╗╔╦╗╔═╗╦═╗ ║
32
+ ║ ║ ║ ╠═╣║ ║ ║║║╣ ╠═╣ ║║╚╗╔╝╠═╣║║║║ ║╣ ║║ ╚═╗ ║ ╠═╣╠╦╝ ║ ║╣ ╠╦╝ ║
33
+ ║ ╚═╝╩═╝╩ ╩╚═╝═╩╝╚═╝ ╩ ╩═╩╝ ╚╝ ╩ ╩╝╚╝╚═╝╚═╝═╩╝ ╚═╝ ╩ ╩ ╩╩╚═ ╩ ╚═╝╩╚═ ║
34
+ ║ ║
35
+ ║ Advanced Claude Code CLI Toolkit - Agents, MCP, GitHub & More ║
36
+ ║ ║
37
+ ╚════════════════════════════════════════════════════════════════════════════╝`;
38
+
39
+ /**
40
+ * Show Project Settings submenu
41
+ */
42
+ export async function showProjectSettingsMenu() {
43
+ console.log('');
44
+ console.log(chalk.cyan('╔═══════════════════════════════════════════════════════════════════════════════╗'));
45
+ console.log(chalk.cyan('║') + chalk.bold(' PROJECT CONFIGURATION ') + chalk.cyan('║'));
46
+ console.log(chalk.cyan('╠═══════════════════════════════════════════════════════════════════════════════╣'));
47
+ console.log(chalk.cyan('║') + ' ' + chalk.cyan('║'));
48
+ console.log(chalk.cyan('║') + ' [1] GitHub Project Board ' + chalk.cyan('║'));
49
+ console.log(chalk.cyan('║') + chalk.dim(' └─ Connect to GitHub Projects v2 for issue tracking ') + chalk.cyan('║'));
50
+ console.log(chalk.cyan('║') + ' ' + chalk.cyan('║'));
51
+ console.log(chalk.cyan('║') + ' [2] Deployment Platforms ' + chalk.cyan('║'));
52
+ console.log(chalk.cyan('║') + chalk.dim(' └─ Configure Railway, Cloudflare, Vercel, or self-hosted ') + chalk.cyan('║'));
53
+ console.log(chalk.cyan('║') + ' ' + chalk.cyan('║'));
54
+ console.log(chalk.cyan('║') + ' [3] Tunnel Services ' + chalk.cyan('║'));
55
+ console.log(chalk.cyan('║') + chalk.dim(' └─ Set up ngrok, localtunnel, or cloudflare-tunnel ') + chalk.cyan('║'));
56
+ console.log(chalk.cyan('║') + ' ' + chalk.cyan('║'));
57
+ console.log(chalk.cyan('║') + ' [4] Token Management ' + chalk.cyan('║'));
58
+ console.log(chalk.cyan('║') + chalk.dim(' └─ Customize daily budget and thresholds ') + chalk.cyan('║'));
59
+ console.log(chalk.cyan('║') + ' ' + chalk.cyan('║'));
60
+ console.log(chalk.cyan('║') + ' [5] Happy Mode ' + chalk.cyan('║'));
61
+ console.log(chalk.cyan('║') + chalk.dim(' └─ Configure mobile app integration ') + chalk.cyan('║'));
62
+ console.log(chalk.cyan('║') + ' ' + chalk.cyan('║'));
63
+ console.log(chalk.cyan('║') + ' [B] Back to main menu ' + chalk.cyan('║'));
64
+ console.log(chalk.cyan('║') + ' ' + chalk.cyan('║'));
65
+ console.log(chalk.cyan('╚═══════════════════════════════════════════════════════════════════════════════╝'));
66
+ console.log('');
67
+
68
+ const { settingsAction } = await inquirer.prompt([
69
+ {
70
+ type: 'list',
71
+ name: 'settingsAction',
72
+ message: 'Select a configuration area:',
73
+ choices: [
74
+ { name: '1. GitHub Project Board', value: 'github' },
75
+ { name: '2. Deployment Platforms', value: 'deployment' },
76
+ { name: '3. Tunnel Services', value: 'tunnel' },
77
+ { name: '4. Token Management', value: 'token' },
78
+ { name: '5. Happy Mode', value: 'happy' },
79
+ new inquirer.Separator(),
80
+ { name: 'Back to main menu', value: 'back' },
81
+ ],
82
+ },
83
+ ]);
84
+
85
+ if (settingsAction === 'back') {
86
+ return;
87
+ }
88
+
89
+ // Load current tech-stack.json
90
+ const techStack = loadTechStack();
91
+
92
+ switch (settingsAction) {
93
+ case 'github':
94
+ await configureGitHub(techStack);
95
+ break;
96
+ case 'deployment':
97
+ await configureDeployment(techStack);
98
+ break;
99
+ case 'tunnel':
100
+ await configureTunnel(techStack);
101
+ break;
102
+ case 'token':
103
+ await configureToken(techStack);
104
+ break;
105
+ case 'happy':
106
+ await configureHappy(techStack);
107
+ break;
108
+ }
109
+
110
+ // Show submenu again
111
+ await showProjectSettingsMenu();
112
+ }
113
+
114
+ /**
115
+ * Configure GitHub Project Board
116
+ */
117
+ async function configureGitHub(techStack) {
118
+ console.log('');
119
+ showHeader('GitHub Project Board Configuration');
120
+
121
+ const current = techStack.versionControl || {};
122
+ const currentBoard = current.projectBoard || {};
123
+
124
+ console.log(chalk.dim(' Current settings:'));
125
+ console.log(chalk.dim(` Provider: ${current.provider || 'not set'}`));
126
+ console.log(chalk.dim(` Owner: ${current.owner || 'not set'}`));
127
+ console.log(chalk.dim(` Repo: ${current.repo || 'not set'}`));
128
+ console.log(chalk.dim(` Project #: ${currentBoard.number || 'not set'}`));
129
+ console.log('');
130
+
131
+ const answers = await inquirer.prompt([
132
+ {
133
+ type: 'input',
134
+ name: 'owner',
135
+ message: 'GitHub owner (username or org):',
136
+ default: current.owner || '',
137
+ },
138
+ {
139
+ type: 'input',
140
+ name: 'repo',
141
+ message: 'Repository name:',
142
+ default: current.repo || '',
143
+ },
144
+ {
145
+ type: 'number',
146
+ name: 'projectNumber',
147
+ message: 'GitHub Project number (from URL):',
148
+ default: currentBoard.number || null,
149
+ },
150
+ {
151
+ type: 'input',
152
+ name: 'defaultBranch',
153
+ message: 'Default branch:',
154
+ default: current.defaultBranch || 'main',
155
+ },
156
+ ]);
157
+
158
+ // Update tech-stack
159
+ techStack.versionControl = {
160
+ ...current,
161
+ provider: 'github',
162
+ owner: answers.owner,
163
+ repo: answers.repo,
164
+ defaultBranch: answers.defaultBranch,
165
+ projectBoard: {
166
+ type: 'github-projects',
167
+ number: answers.projectNumber,
168
+ url: answers.owner && answers.projectNumber
169
+ ? `https://github.com/users/${answers.owner}/projects/${answers.projectNumber}`
170
+ : null,
171
+ },
172
+ };
173
+
174
+ // Remove from pending configuration
175
+ if (techStack._pendingConfiguration) {
176
+ techStack._pendingConfiguration = techStack._pendingConfiguration.filter((f) => f !== 'githubIntegration');
177
+ }
178
+
179
+ saveTechStack(techStack);
180
+ showSuccess('GitHub configuration saved!');
181
+ }
182
+
183
+ /**
184
+ * Configure Deployment Platforms
185
+ */
186
+ async function configureDeployment(techStack) {
187
+ console.log('');
188
+ showHeader('Deployment Platform Configuration');
189
+
190
+ const { target } = await inquirer.prompt([
191
+ {
192
+ type: 'list',
193
+ name: 'target',
194
+ message: 'Which deployment target do you want to configure?',
195
+ choices: [
196
+ { name: 'Frontend (static sites, SPAs)', value: 'frontend' },
197
+ { name: 'Backend (APIs, servers)', value: 'backend' },
198
+ { name: 'Both', value: 'both' },
199
+ { name: 'Back', value: 'back' },
200
+ ],
201
+ },
202
+ ]);
203
+
204
+ if (target === 'back') return;
205
+
206
+ if (target === 'frontend' || target === 'both') {
207
+ const frontendAnswers = await inquirer.prompt([
208
+ {
209
+ type: 'list',
210
+ name: 'platform',
211
+ message: 'Frontend deployment platform:',
212
+ choices: [
213
+ { name: 'Cloudflare Pages', value: 'cloudflare' },
214
+ { name: 'Vercel', value: 'vercel' },
215
+ { name: 'Netlify', value: 'netlify' },
216
+ { name: 'GitHub Pages', value: 'github-pages' },
217
+ { name: 'Railway', value: 'railway' },
218
+ { name: 'Self-hosted', value: 'self-hosted' },
219
+ { name: 'None / Skip', value: 'none' },
220
+ ],
221
+ },
222
+ {
223
+ type: 'input',
224
+ name: 'projectName',
225
+ message: 'Project name (for deployment commands):',
226
+ when: (ans) => ans.platform !== 'none',
227
+ },
228
+ {
229
+ type: 'input',
230
+ name: 'productionUrl',
231
+ message: 'Production URL:',
232
+ when: (ans) => ans.platform !== 'none',
233
+ },
234
+ ]);
235
+
236
+ techStack.deployment = techStack.deployment || {};
237
+ techStack.deployment.frontend = {
238
+ platform: frontendAnswers.platform,
239
+ projectName: frontendAnswers.projectName || null,
240
+ productionUrl: frontendAnswers.productionUrl || null,
241
+ deployCommand: getDeployCommand('frontend', frontendAnswers),
242
+ };
243
+ }
244
+
245
+ if (target === 'backend' || target === 'both') {
246
+ const backendAnswers = await inquirer.prompt([
247
+ {
248
+ type: 'list',
249
+ name: 'platform',
250
+ message: 'Backend deployment platform:',
251
+ choices: [
252
+ { name: 'Railway', value: 'railway' },
253
+ { name: 'Heroku', value: 'heroku' },
254
+ { name: 'Render', value: 'render' },
255
+ { name: 'Fly.io', value: 'fly' },
256
+ { name: 'Vercel', value: 'vercel' },
257
+ { name: 'DigitalOcean', value: 'digitalocean' },
258
+ { name: 'Self-hosted (SSH)', value: 'self-hosted' },
259
+ { name: 'None / Skip', value: 'none' },
260
+ ],
261
+ },
262
+ {
263
+ type: 'input',
264
+ name: 'projectId',
265
+ message: 'Project ID (from platform dashboard):',
266
+ when: (ans) => ['railway', 'render', 'fly'].includes(ans.platform),
267
+ },
268
+ {
269
+ type: 'input',
270
+ name: 'serviceId',
271
+ message: 'Service ID:',
272
+ when: (ans) => ans.platform === 'railway',
273
+ },
274
+ {
275
+ type: 'input',
276
+ name: 'environmentId',
277
+ message: 'Environment ID:',
278
+ when: (ans) => ans.platform === 'railway',
279
+ },
280
+ {
281
+ type: 'input',
282
+ name: 'productionUrl',
283
+ message: 'Production URL:',
284
+ when: (ans) => ans.platform !== 'none',
285
+ },
286
+ ]);
287
+
288
+ // Handle self-hosted config
289
+ let selfHostedConfig = null;
290
+ if (backendAnswers.platform === 'self-hosted') {
291
+ const sshAnswers = await inquirer.prompt([
292
+ { type: 'input', name: 'sshUser', message: 'SSH username:' },
293
+ { type: 'input', name: 'sshHost', message: 'SSH host:' },
294
+ { type: 'number', name: 'sshPort', message: 'SSH port:', default: 22 },
295
+ { type: 'input', name: 'appPath', message: 'App path on server:' },
296
+ { type: 'input', name: 'deployScript', message: 'Deploy script/command:', default: './deploy.sh' },
297
+ ]);
298
+ selfHostedConfig = sshAnswers;
299
+ }
300
+
301
+ techStack.deployment = techStack.deployment || {};
302
+ techStack.deployment.backend = {
303
+ platform: backendAnswers.platform,
304
+ projectId: backendAnswers.projectId || null,
305
+ serviceId: backendAnswers.serviceId || null,
306
+ environmentId: backendAnswers.environmentId || null,
307
+ productionUrl: backendAnswers.productionUrl || null,
308
+ selfHostedConfig,
309
+ };
310
+ }
311
+
312
+ // Remove from pending configuration
313
+ if (techStack._pendingConfiguration) {
314
+ techStack._pendingConfiguration = techStack._pendingConfiguration.filter((f) => f !== 'deploymentAutomation');
315
+ }
316
+
317
+ saveTechStack(techStack);
318
+ showSuccess('Deployment configuration saved!');
319
+ }
320
+
321
+ /**
322
+ * Get deploy command based on platform
323
+ */
324
+ function getDeployCommand(type, answers) {
325
+ if (type === 'frontend') {
326
+ switch (answers.platform) {
327
+ case 'cloudflare':
328
+ return `npx wrangler pages deploy dist --project-name=${answers.projectName || '{{PROJECT_NAME}}'}`;
329
+ case 'vercel':
330
+ return 'vercel --prod';
331
+ case 'netlify':
332
+ return 'netlify deploy --prod';
333
+ case 'github-pages':
334
+ return 'npm run deploy'; // Typically uses gh-pages package
335
+ default:
336
+ return null;
337
+ }
338
+ }
339
+ return null;
340
+ }
341
+
342
+ /**
343
+ * Configure Tunnel Services
344
+ */
345
+ async function configureTunnel(techStack) {
346
+ console.log('');
347
+ showHeader('Tunnel Service Configuration');
348
+
349
+ console.log(chalk.dim(' Tunnel services expose your local development server to the internet.'));
350
+ console.log(chalk.dim(' Useful for mobile testing, webhooks, or sharing work-in-progress.\n'));
351
+
352
+ const current = techStack.devEnvironment?.tunnel || {};
353
+
354
+ const answers = await inquirer.prompt([
355
+ {
356
+ type: 'list',
357
+ name: 'service',
358
+ message: 'Which tunnel service would you like to use?',
359
+ choices: [
360
+ { name: 'None - I don\'t need tunnel access', value: 'none' },
361
+ { name: 'ngrok - Popular tunneling service (requires ngrok CLI)', value: 'ngrok' },
362
+ { name: 'localtunnel - Free, no signup required', value: 'localtunnel' },
363
+ { name: 'cloudflare-tunnel - Enterprise-grade (requires CF account)', value: 'cloudflare-tunnel' },
364
+ { name: 'serveo - Free SSH-based tunneling', value: 'serveo' },
365
+ { name: 'Other - I\'ll configure manually', value: 'other' },
366
+ ],
367
+ default: current.service || 'none',
368
+ },
369
+ {
370
+ type: 'input',
371
+ name: 'subdomain',
372
+ message: 'Reserved subdomain (optional):',
373
+ when: (ans) => ['ngrok', 'localtunnel'].includes(ans.service),
374
+ default: current.subdomain || '',
375
+ },
376
+ {
377
+ type: 'number',
378
+ name: 'adminPort',
379
+ message: 'Admin/API port (for ngrok):',
380
+ when: (ans) => ans.service === 'ngrok',
381
+ default: current.adminPort || 4040,
382
+ },
383
+ ]);
384
+
385
+ // Build start command based on service
386
+ let startCommand = null;
387
+ const frontendPort = techStack.frontend?.port || 5173;
388
+
389
+ switch (answers.service) {
390
+ case 'ngrok':
391
+ startCommand = answers.subdomain
392
+ ? `ngrok http ${frontendPort} --subdomain=${answers.subdomain}`
393
+ : `ngrok http ${frontendPort}`;
394
+ break;
395
+ case 'localtunnel':
396
+ startCommand = answers.subdomain
397
+ ? `lt --port ${frontendPort} --subdomain ${answers.subdomain}`
398
+ : `lt --port ${frontendPort}`;
399
+ break;
400
+ case 'cloudflare-tunnel':
401
+ startCommand = `cloudflared tunnel --url http://localhost:${frontendPort}`;
402
+ break;
403
+ case 'serveo':
404
+ startCommand = `ssh -R 80:localhost:${frontendPort} serveo.net`;
405
+ break;
406
+ }
407
+
408
+ techStack.devEnvironment = techStack.devEnvironment || {};
409
+ techStack.devEnvironment.tunnel = {
410
+ service: answers.service,
411
+ subdomain: answers.subdomain || null,
412
+ url: null, // Set when tunnel is running
413
+ startCommand,
414
+ adminPort: answers.adminPort || null,
415
+ };
416
+
417
+ // Remove from pending configuration
418
+ if (techStack._pendingConfiguration) {
419
+ techStack._pendingConfiguration = techStack._pendingConfiguration.filter((f) => f !== 'tunnelServices');
420
+ }
421
+
422
+ saveTechStack(techStack);
423
+ showSuccess('Tunnel configuration saved!', [
424
+ '',
425
+ answers.service !== 'none' ? `Start command: ${startCommand}` : 'Tunnel disabled',
426
+ ]);
427
+ }
428
+
429
+ /**
430
+ * Configure Token Management
431
+ */
432
+ async function configureToken(techStack) {
433
+ console.log('');
434
+ showHeader('Token Budget Management Configuration');
435
+
436
+ const current = techStack.tokenManagement || {};
437
+ const thresholds = current.thresholds || {};
438
+
439
+ const answers = await inquirer.prompt([
440
+ {
441
+ type: 'confirm',
442
+ name: 'enabled',
443
+ message: 'Enable token budget tracking?',
444
+ default: current.enabled || false,
445
+ },
446
+ {
447
+ type: 'number',
448
+ name: 'dailyBudget',
449
+ message: 'Daily token budget:',
450
+ when: (ans) => ans.enabled,
451
+ default: current.dailyBudget || 200000,
452
+ },
453
+ {
454
+ type: 'number',
455
+ name: 'compactThreshold',
456
+ message: 'Compact suggestion threshold (0-1):',
457
+ when: (ans) => ans.enabled,
458
+ default: thresholds.compact || 0.75,
459
+ },
460
+ {
461
+ type: 'number',
462
+ name: 'archiveThreshold',
463
+ message: 'Archive suggestion threshold (0-1):',
464
+ when: (ans) => ans.enabled,
465
+ default: thresholds.archive || 0.85,
466
+ },
467
+ {
468
+ type: 'number',
469
+ name: 'respawnThreshold',
470
+ message: 'Force respawn threshold (0-1):',
471
+ when: (ans) => ans.enabled,
472
+ default: thresholds.respawn || 0.90,
473
+ },
474
+ ]);
475
+
476
+ techStack.tokenManagement = {
477
+ enabled: answers.enabled,
478
+ dailyBudget: answers.dailyBudget || 200000,
479
+ thresholds: answers.enabled ? {
480
+ compact: answers.compactThreshold,
481
+ archive: answers.archiveThreshold,
482
+ respawn: answers.respawnThreshold,
483
+ } : thresholds,
484
+ trackingFile: current.trackingFile || '.claude/hooks/cache/token-usage.json',
485
+ };
486
+
487
+ saveTechStack(techStack);
488
+ showSuccess('Token management configuration saved!');
489
+ }
490
+
491
+ /**
492
+ * Configure Happy Mode
493
+ */
494
+ async function configureHappy(techStack) {
495
+ console.log('');
496
+ showHeader('Happy Mode Configuration');
497
+
498
+ console.log(chalk.dim(' Happy Mode enables integration with the Happy Coder mobile app'));
499
+ console.log(chalk.dim(' for remote session control, checkpoints, and mobile-optimized responses.\n'));
500
+
501
+ const current = techStack.happyMode || {};
502
+ const notifications = current.notifications || {};
503
+
504
+ const answers = await inquirer.prompt([
505
+ {
506
+ type: 'confirm',
507
+ name: 'enabled',
508
+ message: 'Enable Happy Mode integration?',
509
+ default: current.enabled || false,
510
+ },
511
+ {
512
+ type: 'input',
513
+ name: 'dashboardUrl',
514
+ message: 'Dashboard URL (if self-hosted, leave blank for cloud):',
515
+ when: (ans) => ans.enabled,
516
+ default: current.dashboardUrl || '',
517
+ },
518
+ {
519
+ type: 'number',
520
+ name: 'checkpointInterval',
521
+ message: 'Checkpoint interval (minutes):',
522
+ when: (ans) => ans.enabled,
523
+ default: current.checkpointInterval || 10,
524
+ },
525
+ {
526
+ type: 'list',
527
+ name: 'verbosity',
528
+ message: 'Response verbosity for mobile:',
529
+ when: (ans) => ans.enabled,
530
+ choices: [
531
+ { name: 'Full - Complete responses', value: 'full' },
532
+ { name: 'Condensed - Summarized responses (recommended)', value: 'condensed' },
533
+ { name: 'Minimal - Bare essentials only', value: 'minimal' },
534
+ ],
535
+ default: current.verbosity || 'condensed',
536
+ },
537
+ {
538
+ type: 'confirm',
539
+ name: 'notifyTaskComplete',
540
+ message: 'Notify on task completion?',
541
+ when: (ans) => ans.enabled,
542
+ default: notifications.onTaskComplete !== false,
543
+ },
544
+ {
545
+ type: 'confirm',
546
+ name: 'notifyError',
547
+ message: 'Notify on errors?',
548
+ when: (ans) => ans.enabled,
549
+ default: notifications.onError !== false,
550
+ },
551
+ ]);
552
+
553
+ techStack.happyMode = {
554
+ enabled: answers.enabled,
555
+ dashboardUrl: answers.dashboardUrl || null,
556
+ checkpointInterval: answers.checkpointInterval || 10,
557
+ verbosity: answers.verbosity || 'condensed',
558
+ notifications: answers.enabled ? {
559
+ onTaskComplete: answers.notifyTaskComplete,
560
+ onError: answers.notifyError,
561
+ onCheckpoint: false,
562
+ } : notifications,
563
+ };
564
+
565
+ // Remove from pending configuration
566
+ if (techStack._pendingConfiguration) {
567
+ techStack._pendingConfiguration = techStack._pendingConfiguration.filter((f) => f !== 'happyMode');
568
+ }
569
+
570
+ saveTechStack(techStack);
571
+ showSuccess('Happy Mode configuration saved!');
572
+ }
573
+
574
+ /**
575
+ * Show main interactive menu
576
+ */
577
+ export async function showMainMenu() {
578
+ console.clear();
579
+ console.log(chalk.cyan(BANNER));
580
+ console.log('');
581
+
582
+ const configured = hasValidConfig();
583
+
584
+ // Show config status
585
+ if (configured) {
586
+ console.log(chalk.green(' ✓ Configuration loaded'));
587
+ } else {
588
+ console.log(chalk.yellow(' ⚠ Not configured - run Setup first'));
589
+ }
590
+ console.log(chalk.dim(` v${getVersion()}`));
591
+ console.log('');
592
+
593
+ const choices = [
594
+ {
595
+ name: `${chalk.green('1)')} ${chalk.bold('Create New Task')} Create issue with codebase analysis`,
596
+ value: 'create',
597
+ short: 'Create Task',
598
+ },
599
+ {
600
+ name: `${chalk.cyan('2)')} ${chalk.bold('Decompose Issue')} Break down issue into tasks`,
601
+ value: 'decompose',
602
+ short: 'Decompose',
603
+ },
604
+ {
605
+ name: `${chalk.magenta('3)')} ${chalk.bold('Sync Tasks')} Sync progress with GitHub`,
606
+ value: 'sync',
607
+ short: 'Sync',
608
+ },
609
+ new inquirer.Separator(),
610
+ {
611
+ name: `${chalk.blue('4)')} ${chalk.bold('Setup / Configure')} Connect to your GitHub project`,
612
+ value: 'setup',
613
+ short: 'Setup',
614
+ },
615
+ {
616
+ name: `${chalk.dim('5)')} ${chalk.bold('List Recent Tasks')} View issues you\'ve created`,
617
+ value: 'list',
618
+ short: 'List',
619
+ },
620
+ {
621
+ name: `${chalk.yellow('6)')} ${chalk.bold('Install Claude Command')} Add to .claude/commands/`,
622
+ value: 'install',
623
+ short: 'Install',
624
+ },
625
+ new inquirer.Separator(),
626
+ {
627
+ name: `${chalk.red('T)')} ${chalk.bold('Testing Setup')} Configure testing mode & credentials`,
628
+ value: 'test-setup',
629
+ short: 'Test Setup',
630
+ },
631
+ {
632
+ name: `${chalk.red('R)')} ${chalk.bold('Run Tests')} Run tests (Ralph Loop / Manual / Watch)`,
633
+ value: 'test-run',
634
+ short: 'Run Tests',
635
+ },
636
+ new inquirer.Separator(),
637
+ {
638
+ name: `${chalk.magenta('A)')} ${chalk.bold('Agent Creator')} Create agents, hooks, skills, commands`,
639
+ value: 'agent-creator',
640
+ short: 'Agent Creator',
641
+ },
642
+ {
643
+ name: `${chalk.blue('C)')} ${chalk.bold('Claude Settings')} Permission modes, agent-only launcher`,
644
+ value: 'claude-settings',
645
+ short: 'Claude Settings',
646
+ },
647
+ {
648
+ name: `${chalk.yellow('P)')} ${chalk.bold('Phase Dev Plan')} Create phased development plan (95%+)`,
649
+ value: 'phase-dev',
650
+ short: 'Phase Dev',
651
+ },
652
+ {
653
+ name: `${chalk.green('M)')} ${chalk.bold('MCP Explorer')} Discover & install MCP servers`,
654
+ value: 'explore-mcp',
655
+ short: 'MCP Explorer',
656
+ },
657
+ {
658
+ name: `${chalk.cyan('V)')} ${chalk.bold('Claude Audit')} Verify CLAUDE.md & .claude/ best practices`,
659
+ value: 'claude-audit',
660
+ short: 'Claude Audit',
661
+ },
662
+ {
663
+ name: `${chalk.magenta('R)')} ${chalk.bold('Roadmap Integration')} Sync roadmaps with GitHub Project Board`,
664
+ value: 'roadmap',
665
+ short: 'Roadmap',
666
+ },
667
+ new inquirer.Separator(),
668
+ {
669
+ name: `${chalk.yellow('S)')} ${chalk.bold('Project Settings')} Configure deployment, GitHub, tunnels`,
670
+ value: 'project-settings',
671
+ short: 'Settings',
672
+ },
673
+ {
674
+ name: `${chalk.dim('7)')} ${chalk.bold('Help & Examples')} Documentation and examples`,
675
+ value: 'help',
676
+ short: 'Help',
677
+ },
678
+ {
679
+ name: `${chalk.dim('Q)')} ${chalk.bold('Exit')}`,
680
+ value: 'exit',
681
+ short: 'Exit',
682
+ },
683
+ ];
684
+
685
+ const { action } = await inquirer.prompt([
686
+ {
687
+ type: 'list',
688
+ name: 'action',
689
+ message: 'Select an option:',
690
+ choices,
691
+ pageSize: 10,
692
+ },
693
+ ]);
694
+
695
+ console.log('');
696
+
697
+ switch (action) {
698
+ case 'create':
699
+ if (!configured) {
700
+ console.log(
701
+ chalk.yellow('You need to configure your GitHub project first.')
702
+ );
703
+ console.log('');
704
+ const { proceed } = await inquirer.prompt([
705
+ {
706
+ type: 'confirm',
707
+ name: 'proceed',
708
+ message: 'Would you like to run setup now?',
709
+ default: true,
710
+ },
711
+ ]);
712
+ if (proceed) {
713
+ await runSetup({});
714
+ // After setup, return to menu
715
+ await showMainMenu();
716
+ }
717
+ } else {
718
+ await runCreate({});
719
+ await returnToMenu();
720
+ }
721
+ break;
722
+
723
+ case 'decompose':
724
+ if (!configured) {
725
+ console.log(chalk.yellow('You need to configure your GitHub project first.'));
726
+ await returnToMenu();
727
+ } else {
728
+ await runDecompose({});
729
+ await returnToMenu();
730
+ }
731
+ break;
732
+
733
+ case 'sync':
734
+ if (!configured) {
735
+ console.log(chalk.yellow('You need to configure your GitHub project first.'));
736
+ await returnToMenu();
737
+ } else {
738
+ // Show sync submenu
739
+ const { syncAction } = await inquirer.prompt([
740
+ {
741
+ type: 'list',
742
+ name: 'syncAction',
743
+ message: 'Sync action:',
744
+ choices: [
745
+ { name: 'Status - Show tracked issues', value: 'status' },
746
+ { name: 'Pull - Fetch from GitHub', value: 'pull' },
747
+ { name: 'Push - Update GitHub', value: 'push' },
748
+ { name: 'Watch - Interactive tracking', value: 'watch' },
749
+ { name: 'Back to menu', value: 'back' },
750
+ ],
751
+ },
752
+ ]);
753
+ if (syncAction !== 'back') {
754
+ await runSync({ subcommand: syncAction });
755
+ }
756
+ await returnToMenu();
757
+ }
758
+ break;
759
+
760
+ case 'setup':
761
+ await runSetup({});
762
+ await returnToMenu();
763
+ break;
764
+
765
+ case 'list':
766
+ if (!configured) {
767
+ console.log(
768
+ chalk.yellow(
769
+ 'You need to configure your GitHub project first to list tasks.'
770
+ )
771
+ );
772
+ await returnToMenu();
773
+ } else {
774
+ await runList({});
775
+ await returnToMenu();
776
+ }
777
+ break;
778
+
779
+ case 'install':
780
+ await runInstall({});
781
+ await returnToMenu();
782
+ break;
783
+
784
+ case 'help':
785
+ showHelp();
786
+ await returnToMenu();
787
+ break;
788
+
789
+ case 'test-setup':
790
+ await runTestSetup({});
791
+ await returnToMenu();
792
+ break;
793
+
794
+ case 'test-run':
795
+ // Check if testing is configured
796
+ if (!hasTestingConfig()) {
797
+ console.log(chalk.yellow('Testing not configured. Running setup first...'));
798
+ console.log('');
799
+ await runTestSetup({});
800
+ } else {
801
+ // Show test mode submenu
802
+ const { testMode } = await inquirer.prompt([
803
+ {
804
+ type: 'list',
805
+ name: 'testMode',
806
+ message: 'How would you like to run tests?',
807
+ choices: [
808
+ { name: 'Default - Use configured mode', value: 'default' },
809
+ { name: 'Ralph Loop - Test-fix cycle until all pass', value: 'ralph' },
810
+ { name: 'Manual - Run tests once', value: 'manual' },
811
+ { name: 'Watch - Interactive UI mode', value: 'watch' },
812
+ { name: 'Back to menu', value: 'back' },
813
+ ],
814
+ },
815
+ ]);
816
+ if (testMode !== 'back') {
817
+ const options = testMode === 'default' ? {} : { mode: testMode };
818
+ await runTest(options);
819
+ }
820
+ }
821
+ await returnToMenu();
822
+ break;
823
+
824
+ case 'agent-creator':
825
+ await runCreateAgent({});
826
+ await returnToMenu();
827
+ break;
828
+
829
+ case 'claude-settings':
830
+ await runClaudeSettings({});
831
+ await returnToMenu();
832
+ break;
833
+
834
+ case 'phase-dev':
835
+ await showPhasDevMainMenu();
836
+ await returnToMenu();
837
+ break;
838
+
839
+ case 'explore-mcp':
840
+ await showExploreMcpMenu();
841
+ await returnToMenu();
842
+ break;
843
+
844
+ case 'claude-audit':
845
+ await showClaudeAuditMenu();
846
+ await returnToMenu();
847
+ break;
848
+
849
+ case 'roadmap':
850
+ await showRoadmapMenu();
851
+ await returnToMenu();
852
+ break;
853
+
854
+ case 'project-settings':
855
+ await showProjectSettingsMenu();
856
+ await returnToMenu();
857
+ break;
858
+
859
+ case 'exit':
860
+ console.log(chalk.dim('Goodbye!'));
861
+ process.exit(0);
862
+ }
863
+ }
864
+
865
+ /**
866
+ * Prompt to return to main menu
867
+ */
868
+ async function returnToMenu() {
869
+ console.log('');
870
+ const { back } = await inquirer.prompt([
871
+ {
872
+ type: 'confirm',
873
+ name: 'back',
874
+ message: 'Return to main menu?',
875
+ default: true,
876
+ },
877
+ ]);
878
+
879
+ if (back) {
880
+ await showMainMenu();
881
+ } else {
882
+ console.log(chalk.dim('Goodbye!'));
883
+ process.exit(0);
884
+ }
885
+ }
886
+
887
+ /**
888
+ * Show a styled header
889
+ */
890
+ export function showHeader(title) {
891
+ const width = 60;
892
+ const padding = Math.max(0, Math.floor((width - title.length - 2) / 2));
893
+ const line = '═'.repeat(width);
894
+
895
+ console.log(chalk.cyan(`╔${line}╗`));
896
+ console.log(
897
+ chalk.cyan('║') +
898
+ ' '.repeat(padding) +
899
+ chalk.bold(title) +
900
+ ' '.repeat(width - padding - title.length) +
901
+ chalk.cyan('║')
902
+ );
903
+ console.log(chalk.cyan(`╚${line}╝`));
904
+ console.log('');
905
+ }
906
+
907
+ /**
908
+ * Show a success message in a box
909
+ */
910
+ export function showSuccess(title, details = []) {
911
+ const width = 65;
912
+ const line = '═'.repeat(width);
913
+
914
+ console.log('');
915
+ console.log(chalk.green(`╔${line}╗`));
916
+ console.log(
917
+ chalk.green('║ ') +
918
+ chalk.green.bold('✅ ' + title.padEnd(width - 5)) +
919
+ chalk.green('║')
920
+ );
921
+
922
+ if (details.length > 0) {
923
+ console.log(chalk.green('║' + ' '.repeat(width) + '║'));
924
+ for (const detail of details) {
925
+ const paddedDetail = (' ' + detail).padEnd(width);
926
+ console.log(chalk.green('║') + paddedDetail + chalk.green('║'));
927
+ }
928
+ }
929
+
930
+ console.log(chalk.green(`╚${line}╝`));
931
+ console.log('');
932
+ }
933
+
934
+ /**
935
+ * Show an error message
936
+ */
937
+ export function showError(title, message) {
938
+ console.log('');
939
+ console.log(chalk.red(`✗ ${chalk.bold(title)}`));
940
+ if (message) {
941
+ console.log(chalk.red(` ${message}`));
942
+ }
943
+ console.log('');
944
+ }
945
+
946
+ /**
947
+ * Show a warning message
948
+ */
949
+ export function showWarning(message) {
950
+ console.log(chalk.yellow(`⚠ ${message}`));
951
+ }
952
+
953
+ /**
954
+ * Show info message
955
+ */
956
+ export function showInfo(message) {
957
+ console.log(chalk.blue(`ℹ ${message}`));
958
+ }