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,534 @@
1
+ /**
2
+ * Sync Command
3
+ *
4
+ * Synchronize task progress between local state and GitHub issues
5
+ */
6
+
7
+ import chalk from 'chalk';
8
+ import inquirer from 'inquirer';
9
+ import ora from 'ora';
10
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from 'fs';
11
+ import { join } from 'path';
12
+ import { showHeader, showSuccess, showError, showWarning, showInfo } from '../cli/menu.js';
13
+ import { loadConfigSync, execCommand } from '../utils.js';
14
+ import { getIssue, addIssueComment, updateProjectItemField, getProjectItemId } from '../github/client.js';
15
+ import { parseIssueBody, toClaudeTaskList, toMarkdownChecklist, getCompletionStats } from '../analysis/checklist-parser.js';
16
+
17
+ // State directory
18
+ const STATE_DIR = join(process.cwd(), '.gtask');
19
+
20
+ /**
21
+ * Run the sync command
22
+ */
23
+ export async function runSync(options) {
24
+ const subcommand = options.subcommand || 'status';
25
+ const issueNumber = options.issue ? parseInt(options.issue, 10) : null;
26
+
27
+ switch (subcommand) {
28
+ case 'pull':
29
+ return await syncPull(issueNumber, options);
30
+ case 'push':
31
+ return await syncPush(issueNumber, options);
32
+ case 'watch':
33
+ return await syncWatch(issueNumber, options);
34
+ case 'status':
35
+ default:
36
+ return await syncStatus(issueNumber, options);
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Show sync status
42
+ */
43
+ async function syncStatus(issueNumber, options) {
44
+ showHeader('Task Sync Status');
45
+
46
+ const states = loadAllTaskStates();
47
+
48
+ if (states.length === 0) {
49
+ showWarning('No tracked issues found.');
50
+ console.log(chalk.dim('Run "gtask decompose <issue>" to start tracking.'));
51
+ return;
52
+ }
53
+
54
+ console.log(chalk.bold('Tracked Issues:\n'));
55
+ console.log(
56
+ chalk.dim(
57
+ `${'#'.padEnd(8)} ${'Title'.padEnd(40)} ${'Progress'.padEnd(12)} ${'Last Sync'}`
58
+ )
59
+ );
60
+ console.log(chalk.dim('─'.repeat(80)));
61
+
62
+ for (const state of states) {
63
+ const stats = getCompletionStats(state.tasks);
64
+ const progress = `${stats.completed}/${stats.total} (${stats.percentage}%)`;
65
+ const lastSync = state.lastSyncedAt
66
+ ? new Date(state.lastSyncedAt).toLocaleDateString()
67
+ : 'never';
68
+ const title = (state.issueTitle || '').slice(0, 38);
69
+
70
+ const progressColor =
71
+ stats.percentage === 100
72
+ ? chalk.green
73
+ : stats.percentage > 50
74
+ ? chalk.yellow
75
+ : chalk.white;
76
+
77
+ console.log(
78
+ `${chalk.cyan(`#${state.issueNumber}`.padEnd(8))} ${title.padEnd(40)} ${progressColor(
79
+ progress.padEnd(12)
80
+ )} ${chalk.dim(lastSync)}`
81
+ );
82
+ }
83
+
84
+ console.log('');
85
+ console.log(chalk.dim('Commands:'));
86
+ console.log(chalk.dim(' gtask sync pull <issue> - Pull latest from GitHub'));
87
+ console.log(chalk.dim(' gtask sync push <issue> - Push progress to GitHub'));
88
+ console.log(chalk.dim(' gtask sync watch <issue> - Auto-sync on changes'));
89
+ }
90
+
91
+ /**
92
+ * Pull task state from GitHub issue
93
+ */
94
+ async function syncPull(issueNumber, options) {
95
+ showHeader('Pull from GitHub');
96
+
97
+ if (!issueNumber) {
98
+ const { num } = await inquirer.prompt([
99
+ {
100
+ type: 'input',
101
+ name: 'num',
102
+ message: 'Issue number:',
103
+ validate: (input) => parseInt(input, 10) > 0 || 'Enter valid number',
104
+ },
105
+ ]);
106
+ issueNumber = parseInt(num, 10);
107
+ }
108
+
109
+ const { config } = loadConfigSync();
110
+ if (!config?.project_board) {
111
+ showError('Not configured');
112
+ return;
113
+ }
114
+
115
+ const { owner, repo } = config.project_board;
116
+
117
+ const spinner = ora(`Fetching issue #${issueNumber}...`).start();
118
+ const issue = getIssue(owner, repo, issueNumber);
119
+
120
+ if (!issue) {
121
+ spinner.fail('Issue not found');
122
+ return;
123
+ }
124
+
125
+ spinner.text = 'Parsing issue...';
126
+ const parsed = parseIssueBody(issue.body);
127
+ const tasks = toClaudeTaskList(parsed);
128
+
129
+ // Load existing state
130
+ const existingState = loadTaskState(issueNumber);
131
+ const stats = getCompletionStats(tasks);
132
+
133
+ spinner.succeed(`Pulled ${tasks.length} tasks from #${issueNumber}`);
134
+
135
+ // Check for conflicts
136
+ if (existingState && existingState.tasks) {
137
+ const localStats = getCompletionStats(existingState.tasks);
138
+
139
+ if (localStats.completed > stats.completed) {
140
+ showWarning(
141
+ `Local has more progress (${localStats.completed}/${localStats.total}) than GitHub (${stats.completed}/${stats.total})`
142
+ );
143
+
144
+ const { resolve } = await inquirer.prompt([
145
+ {
146
+ type: 'list',
147
+ name: 'resolve',
148
+ message: 'How to resolve?',
149
+ choices: [
150
+ { name: 'Keep local (more progress)', value: 'local' },
151
+ { name: 'Use GitHub (overwrite local)', value: 'github' },
152
+ { name: 'Merge (keep completed from both)', value: 'merge' },
153
+ ],
154
+ },
155
+ ]);
156
+
157
+ if (resolve === 'local') {
158
+ console.log(chalk.green('āœ“ Keeping local state'));
159
+ return existingState;
160
+ } else if (resolve === 'merge') {
161
+ // Merge: mark as completed if either has it completed
162
+ for (let i = 0; i < tasks.length; i++) {
163
+ if (existingState.tasks[i]?.status === 'completed') {
164
+ tasks[i].status = 'completed';
165
+ }
166
+ }
167
+ console.log(chalk.green('āœ“ Merged states'));
168
+ }
169
+ }
170
+ }
171
+
172
+ // Save state
173
+ const state = {
174
+ issueNumber,
175
+ issueTitle: issue.title,
176
+ owner,
177
+ repo,
178
+ tasks,
179
+ createdAt: existingState?.createdAt || new Date().toISOString(),
180
+ lastSyncedAt: new Date().toISOString(),
181
+ pullSource: 'github',
182
+ };
183
+
184
+ saveTaskState(state);
185
+
186
+ showSuccess('Pull Complete', [
187
+ `Issue: #${issueNumber} - ${issue.title}`,
188
+ `Tasks: ${stats.total} (${stats.completed} completed)`,
189
+ `Progress: ${stats.percentage}%`,
190
+ ]);
191
+
192
+ return state;
193
+ }
194
+
195
+ /**
196
+ * Push task progress to GitHub
197
+ */
198
+ async function syncPush(issueNumber, options) {
199
+ showHeader('Push to GitHub');
200
+
201
+ if (!issueNumber) {
202
+ // Show available issues
203
+ const states = loadAllTaskStates();
204
+ if (states.length === 0) {
205
+ showError('No tracked issues', 'Run "gtask decompose <issue>" first.');
206
+ return;
207
+ }
208
+
209
+ const { selectedIssue } = await inquirer.prompt([
210
+ {
211
+ type: 'list',
212
+ name: 'selectedIssue',
213
+ message: 'Select issue to push:',
214
+ choices: states.map((s) => ({
215
+ name: `#${s.issueNumber} - ${s.issueTitle} (${getCompletionStats(s.tasks).percentage}%)`,
216
+ value: s.issueNumber,
217
+ })),
218
+ },
219
+ ]);
220
+ issueNumber = selectedIssue;
221
+ }
222
+
223
+ const state = loadTaskState(issueNumber);
224
+ if (!state) {
225
+ showError(`No local state for issue #${issueNumber}`);
226
+ return;
227
+ }
228
+
229
+ const { config } = loadConfigSync();
230
+ const { owner, repo, project_number: projectNumber, project_id: projectId } =
231
+ config.project_board;
232
+
233
+ const stats = getCompletionStats(state.tasks);
234
+
235
+ // Generate update comment
236
+ const spinner = ora('Generating progress update...').start();
237
+
238
+ const completedTasks = state.tasks
239
+ .filter((t) => t.status === 'completed')
240
+ .map((t, i) => ` āœ… ${t.content}`)
241
+ .join('\n');
242
+
243
+ const pendingTasks = state.tasks
244
+ .filter((t) => t.status !== 'completed')
245
+ .map((t) => ` ⬜ ${t.content}`)
246
+ .join('\n');
247
+
248
+ // Get recent commits
249
+ const commitResult = execCommand('git log --oneline -5 2>/dev/null');
250
+ const recentCommits = commitResult.success
251
+ ? commitResult.output
252
+ .split('\n')
253
+ .slice(0, 3)
254
+ .map((c) => ` - ${c}`)
255
+ .join('\n')
256
+ : ' (no recent commits)';
257
+
258
+ const commentBody = `## Progress Update
259
+
260
+ **Status**: ${stats.percentage}% complete (${stats.completed}/${stats.total} tasks)
261
+ **Updated**: ${new Date().toISOString()}
262
+
263
+ ### Completed Tasks
264
+ ${completedTasks || ' (none yet)'}
265
+
266
+ ### Remaining Tasks
267
+ ${pendingTasks || ' (all done!)'}
268
+
269
+ ### Recent Commits
270
+ ${recentCommits}
271
+
272
+ ---
273
+ *Synced by GitHub Task Kit*`;
274
+
275
+ spinner.text = 'Posting to GitHub...';
276
+ const success = addIssueComment(owner, repo, issueNumber, commentBody);
277
+
278
+ if (!success) {
279
+ spinner.fail('Failed to post comment');
280
+ return;
281
+ }
282
+
283
+ // Update project board status if 100%
284
+ if (stats.percentage === 100 && projectId && config.field_ids?.status) {
285
+ spinner.text = 'Updating project board...';
286
+ const itemId = getProjectItemId(owner, projectNumber, issueNumber);
287
+
288
+ if (itemId && config.status_options?.done) {
289
+ updateProjectItemField(
290
+ projectId,
291
+ itemId,
292
+ config.field_ids.status,
293
+ config.status_options.done,
294
+ 'single-select'
295
+ );
296
+ }
297
+ }
298
+
299
+ // Update local state
300
+ state.lastSyncedAt = new Date().toISOString();
301
+ saveTaskState(state);
302
+
303
+ spinner.succeed('Pushed to GitHub');
304
+
305
+ showSuccess('Push Complete', [
306
+ `Issue: #${issueNumber}`,
307
+ `Progress: ${stats.percentage}%`,
308
+ `Comment posted with ${stats.completed} completed tasks`,
309
+ stats.percentage === 100 ? 'Project board status updated to Done' : '',
310
+ ].filter(Boolean));
311
+ }
312
+
313
+ /**
314
+ * Watch for changes and auto-sync
315
+ */
316
+ async function syncWatch(issueNumber, options) {
317
+ showHeader('Watch Mode');
318
+
319
+ if (!issueNumber) {
320
+ const states = loadAllTaskStates();
321
+ if (states.length === 0) {
322
+ showError('No tracked issues');
323
+ return;
324
+ }
325
+
326
+ const { selectedIssue } = await inquirer.prompt([
327
+ {
328
+ type: 'list',
329
+ name: 'selectedIssue',
330
+ message: 'Select issue to watch:',
331
+ choices: states.map((s) => ({
332
+ name: `#${s.issueNumber} - ${s.issueTitle}`,
333
+ value: s.issueNumber,
334
+ })),
335
+ },
336
+ ]);
337
+ issueNumber = selectedIssue;
338
+ }
339
+
340
+ const state = loadTaskState(issueNumber);
341
+ if (!state) {
342
+ showError(`No local state for issue #${issueNumber}`);
343
+ return;
344
+ }
345
+
346
+ console.log(chalk.cyan(`Watching issue #${issueNumber}: ${state.issueTitle}`));
347
+ console.log(chalk.dim('Press Ctrl+C to stop\n'));
348
+
349
+ // Display interactive task list
350
+ let tasks = state.tasks;
351
+ let lastStats = getCompletionStats(tasks);
352
+
353
+ const displayTasks = () => {
354
+ console.clear();
355
+ console.log(chalk.cyan.bold(`\n Issue #${issueNumber}: ${state.issueTitle}\n`));
356
+
357
+ for (let i = 0; i < tasks.length; i++) {
358
+ const task = tasks[i];
359
+ const status =
360
+ task.status === 'completed'
361
+ ? chalk.green('āœ“')
362
+ : task.status === 'in_progress'
363
+ ? chalk.yellow('ā—‰')
364
+ : chalk.dim('ā—‹');
365
+ const num = chalk.dim(`${i + 1}.`);
366
+ console.log(` ${status} ${num} ${task.content}`);
367
+ }
368
+
369
+ const stats = getCompletionStats(tasks);
370
+ console.log('');
371
+ console.log(
372
+ chalk.dim(` Progress: ${stats.completed}/${stats.total} (${stats.percentage}%)`)
373
+ );
374
+ console.log('');
375
+ console.log(chalk.dim(' Commands:'));
376
+ console.log(chalk.dim(' c <num> - Complete task'));
377
+ console.log(chalk.dim(' s <num> - Start task (in progress)'));
378
+ console.log(chalk.dim(' r <num> - Reset task (pending)'));
379
+ console.log(chalk.dim(' p - Push to GitHub'));
380
+ console.log(chalk.dim(' q - Quit'));
381
+ console.log('');
382
+ };
383
+
384
+ displayTasks();
385
+
386
+ // Interactive loop
387
+ const readline = await import('readline');
388
+ const rl = readline.createInterface({
389
+ input: process.stdin,
390
+ output: process.stdout,
391
+ });
392
+
393
+ const { config } = loadConfigSync();
394
+
395
+ const processCommand = async (line) => {
396
+ const [cmd, arg] = line.trim().split(/\s+/);
397
+ const taskNum = parseInt(arg, 10) - 1;
398
+
399
+ switch (cmd?.toLowerCase()) {
400
+ case 'c':
401
+ case 'complete':
402
+ if (taskNum >= 0 && taskNum < tasks.length) {
403
+ tasks[taskNum].status = 'completed';
404
+ state.tasks = tasks;
405
+ saveTaskState(state);
406
+ console.log(chalk.green(`āœ“ Task ${taskNum + 1} completed`));
407
+ }
408
+ break;
409
+
410
+ case 's':
411
+ case 'start':
412
+ if (taskNum >= 0 && taskNum < tasks.length) {
413
+ tasks[taskNum].status = 'in_progress';
414
+ state.tasks = tasks;
415
+ saveTaskState(state);
416
+ console.log(chalk.yellow(`ā—‰ Task ${taskNum + 1} in progress`));
417
+ }
418
+ break;
419
+
420
+ case 'r':
421
+ case 'reset':
422
+ if (taskNum >= 0 && taskNum < tasks.length) {
423
+ tasks[taskNum].status = 'pending';
424
+ state.tasks = tasks;
425
+ saveTaskState(state);
426
+ console.log(chalk.dim(`ā—‹ Task ${taskNum + 1} reset`));
427
+ }
428
+ break;
429
+
430
+ case 'p':
431
+ case 'push':
432
+ await syncPush(issueNumber, {});
433
+ break;
434
+
435
+ case 'q':
436
+ case 'quit':
437
+ rl.close();
438
+ process.exit(0);
439
+ break;
440
+
441
+ default:
442
+ if (cmd) {
443
+ console.log(chalk.red('Unknown command'));
444
+ }
445
+ }
446
+
447
+ // Check if we should auto-sync
448
+ const newStats = getCompletionStats(tasks);
449
+ if (newStats.completed !== lastStats.completed) {
450
+ // Progress changed, maybe auto-push
451
+ if (newStats.percentage === 100) {
452
+ console.log(chalk.green('\nšŸŽ‰ All tasks complete! Pushing to GitHub...'));
453
+ await syncPush(issueNumber, {});
454
+ }
455
+ lastStats = newStats;
456
+ }
457
+
458
+ setTimeout(displayTasks, 500);
459
+ };
460
+
461
+ rl.on('line', processCommand);
462
+
463
+ // Handle Ctrl+C
464
+ rl.on('close', () => {
465
+ console.log(chalk.dim('\nStopped watching.'));
466
+ process.exit(0);
467
+ });
468
+ }
469
+
470
+ /**
471
+ * Save task state to disk
472
+ */
473
+ export function saveTaskState(state) {
474
+ if (!existsSync(STATE_DIR)) {
475
+ mkdirSync(STATE_DIR, { recursive: true });
476
+ }
477
+
478
+ const filePath = join(STATE_DIR, `issue-${state.issueNumber}.json`);
479
+ writeFileSync(filePath, JSON.stringify(state, null, 2), 'utf8');
480
+ }
481
+
482
+ /**
483
+ * Load task state from disk
484
+ */
485
+ export function loadTaskState(issueNumber) {
486
+ const filePath = join(STATE_DIR, `issue-${issueNumber}.json`);
487
+
488
+ if (!existsSync(filePath)) {
489
+ return null;
490
+ }
491
+
492
+ try {
493
+ return JSON.parse(readFileSync(filePath, 'utf8'));
494
+ } catch {
495
+ return null;
496
+ }
497
+ }
498
+
499
+ /**
500
+ * Load all task states
501
+ */
502
+ export function loadAllTaskStates() {
503
+ if (!existsSync(STATE_DIR)) {
504
+ return [];
505
+ }
506
+
507
+ const files = readdirSync(STATE_DIR).filter((f) => f.startsWith('issue-') && f.endsWith('.json'));
508
+ const states = [];
509
+
510
+ for (const file of files) {
511
+ try {
512
+ const content = readFileSync(join(STATE_DIR, file), 'utf8');
513
+ states.push(JSON.parse(content));
514
+ } catch {
515
+ // Skip invalid files
516
+ }
517
+ }
518
+
519
+ return states.sort((a, b) => b.issueNumber - a.issueNumber);
520
+ }
521
+
522
+ /**
523
+ * Update a specific task's status
524
+ */
525
+ export function updateTaskStatus(issueNumber, taskIndex, status) {
526
+ const state = loadTaskState(issueNumber);
527
+ if (!state || !state.tasks[taskIndex]) {
528
+ return false;
529
+ }
530
+
531
+ state.tasks[taskIndex].status = status;
532
+ saveTaskState(state);
533
+ return true;
534
+ }