claude-cli-advanced-starter-pack 1.8.5 → 1.8.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-cli-advanced-starter-pack",
3
- "version": "1.8.5",
3
+ "version": "1.8.6",
4
4
  "description": "Advanced Claude Code CLI toolkit - agents, hooks, skills, MCP servers, phased development, and GitHub integration",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/cli/menu.js CHANGED
@@ -799,7 +799,7 @@ export async function showMainMenu() {
799
799
  name: 'action',
800
800
  message: 'Select an option:',
801
801
  choices,
802
- pageSize: 10,
802
+ pageSize: 20,
803
803
  },
804
804
  ]);
805
805
 
@@ -7,8 +7,33 @@
7
7
 
8
8
  import chalk from 'chalk';
9
9
  import ora from 'ora';
10
- import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
11
- import { join } from 'path';
10
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, copyFileSync } from 'fs';
11
+ import { join, basename } from 'path';
12
+
13
+ /**
14
+ * Create a backup of CLAUDE.md before modification
15
+ * @param {string} filePath - Path to CLAUDE.md
16
+ * @param {string} cwd - Working directory
17
+ * @returns {string|null} Backup path or null if failed
18
+ */
19
+ function backupClaudeMd(filePath, cwd) {
20
+ if (!existsSync(filePath)) return null;
21
+
22
+ const backupDir = join(cwd, '.claude', 'backups');
23
+ if (!existsSync(backupDir)) {
24
+ mkdirSync(backupDir, { recursive: true });
25
+ }
26
+
27
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
28
+ const backupPath = join(backupDir, `CLAUDE.md.${timestamp}.bak`);
29
+
30
+ try {
31
+ copyFileSync(filePath, backupPath);
32
+ return backupPath;
33
+ } catch {
34
+ return null;
35
+ }
36
+ }
12
37
 
13
38
  /**
14
39
  * MCP section marker
@@ -158,6 +183,12 @@ ${mcpSection}
158
183
  };
159
184
  }
160
185
 
186
+ // Backup existing CLAUDE.md before modification
187
+ const backupPath = backupClaudeMd(claudeMdPath, cwd);
188
+ if (backupPath) {
189
+ spinner.text = 'Backed up CLAUDE.md...';
190
+ }
191
+
161
192
  // Check if MCP section exists
162
193
  const hasStartMarker = content.includes(MCP_SECTION_START);
163
194
  const hasEndMarker = content.includes(MCP_SECTION_END);
@@ -193,12 +224,13 @@ ${mcpSection}
193
224
  }
194
225
 
195
226
  writeFileSync(claudeMdPath, content, 'utf8');
196
- spinner.succeed('Updated CLAUDE.md with MCP documentation');
227
+ spinner.succeed(`Updated CLAUDE.md with MCP documentation${backupPath ? ' (backup created)' : ''}`);
197
228
 
198
229
  return {
199
230
  success: true,
200
231
  action: hasStartMarker ? 'updated' : 'appended',
201
232
  path: claudeMdPath,
233
+ backupPath: backupPath || null,
202
234
  };
203
235
  } catch (error) {
204
236
  spinner.fail(`Failed to update CLAUDE.md: ${error.message}`);
@@ -225,6 +257,9 @@ export function removeMcpSection(cwd = process.cwd()) {
225
257
  const hasEndMarker = content.includes(MCP_SECTION_END);
226
258
 
227
259
  if (hasStartMarker && hasEndMarker) {
260
+ // Backup before removal
261
+ backupClaudeMd(claudeMdPath, cwd);
262
+
228
263
  const regex = new RegExp(
229
264
  `\\n?${MCP_SECTION_START}[\\s\\S]*?${MCP_SECTION_END}\\n?`,
230
265
  'g'
@@ -219,6 +219,12 @@ const AVAILABLE_COMMANDS = [
219
219
  category: 'Claude Code',
220
220
  selected: true,
221
221
  },
222
+ {
223
+ name: 'detect-tech-stack',
224
+ description: 'Re-run tech stack detection and update configuration',
225
+ category: 'Analysis',
226
+ selected: true,
227
+ },
222
228
  {
223
229
  name: 'roadmap-sync',
224
230
  description: 'Sync roadmaps with GitHub Project Board',
@@ -2795,3 +2801,86 @@ npx claude-cli-advanced-starter-pack init --force
2795
2801
 
2796
2802
  return content;
2797
2803
  }
2804
+
2805
+ /**
2806
+ * Verify and fix legacy installations (pre-v1.0.8)
2807
+ * Issue #8: Ensures update-check hook is properly configured
2808
+ *
2809
+ * @param {string} projectDir - Project directory to verify
2810
+ * @returns {Object} Verification result with fixes applied
2811
+ */
2812
+ export async function verifyLegacyInstallation(projectDir = process.cwd()) {
2813
+ const fixes = [];
2814
+ const issues = [];
2815
+
2816
+ const claudeDir = join(projectDir, '.claude');
2817
+ const hooksDir = join(claudeDir, 'hooks');
2818
+ const settingsPath = join(claudeDir, 'settings.json');
2819
+ const updateCheckHookPath = join(hooksDir, 'ccasp-update-check.js');
2820
+
2821
+ // Check if this is a CCASP installation
2822
+ if (!existsSync(claudeDir)) {
2823
+ return { isLegacy: false, message: 'No .claude folder found' };
2824
+ }
2825
+
2826
+ // Check 1: Does the update-check hook file exist?
2827
+ if (!existsSync(updateCheckHookPath)) {
2828
+ issues.push('Missing ccasp-update-check.js hook file');
2829
+
2830
+ // Fix: Create the hook file
2831
+ if (!existsSync(hooksDir)) {
2832
+ mkdirSync(hooksDir, { recursive: true });
2833
+ }
2834
+
2835
+ const templatePath = join(__dirname, '..', '..', 'templates', 'hooks', 'ccasp-update-check.template.js');
2836
+ if (existsSync(templatePath)) {
2837
+ const hookContent = readFileSync(templatePath, 'utf8');
2838
+ writeFileSync(updateCheckHookPath, hookContent, 'utf8');
2839
+ fixes.push('Created ccasp-update-check.js hook file');
2840
+ }
2841
+ }
2842
+
2843
+ // Check 2: Is the hook registered in settings.json?
2844
+ if (existsSync(settingsPath)) {
2845
+ try {
2846
+ const settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
2847
+
2848
+ // Check if UserPromptSubmit hook exists with update-check
2849
+ const hasUpdateHook = settings.hooks?.UserPromptSubmit?.some(
2850
+ (h) => h.hooks?.some((hook) => hook.command?.includes('ccasp-update-check'))
2851
+ );
2852
+
2853
+ if (!hasUpdateHook) {
2854
+ issues.push('Update-check hook not registered in settings.json');
2855
+
2856
+ // Fix: Add the hook to settings.json
2857
+ if (!settings.hooks) settings.hooks = {};
2858
+ if (!settings.hooks.UserPromptSubmit) {
2859
+ settings.hooks.UserPromptSubmit = [];
2860
+ }
2861
+
2862
+ settings.hooks.UserPromptSubmit.push({
2863
+ matcher: '',
2864
+ hooks: [{
2865
+ type: 'command',
2866
+ command: 'node .claude/hooks/ccasp-update-check.js',
2867
+ }],
2868
+ });
2869
+
2870
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
2871
+ fixes.push('Registered update-check hook in settings.json');
2872
+ }
2873
+ } catch {
2874
+ issues.push('Could not parse settings.json');
2875
+ }
2876
+ }
2877
+
2878
+ return {
2879
+ isLegacy: issues.length > 0,
2880
+ issues,
2881
+ fixes,
2882
+ message: fixes.length > 0
2883
+ ? `Fixed ${fixes.length} legacy installation issue(s)`
2884
+ : 'Installation is up to date',
2885
+ };
2886
+ }
@@ -578,43 +578,60 @@ function showSetupHeader() {
578
578
  }
579
579
 
580
580
  /**
581
- * Quick setup options - numbered for easy mobile input
581
+ * Streamlined setup options - 3 core paths for easy mobile input
582
+ * Issue #8: Simplified from 7 options to reduce scrolling
582
583
  */
583
584
  const SETUP_OPTIONS = [
584
585
  {
585
- name: `${chalk.yellow('1.')} Quick Start ${chalk.dim('- Detect stack + init .claude')}`,
586
- value: 'quick',
587
- short: 'Quick Start',
586
+ name: `${chalk.green('1.')} Auto Install ${chalk.dim('- Full features + backup (recommended)')}`,
587
+ value: 'auto',
588
+ short: 'Auto Install',
588
589
  },
589
590
  {
590
- name: `${chalk.yellow('2.')} Full Setup ${chalk.dim('- All features + customization')}`,
591
- value: 'full',
592
- short: 'Full Setup',
593
- },
594
- {
595
- name: `${chalk.yellow('3.')} GitHub Setup ${chalk.dim('- Connect project board')}`,
591
+ name: `${chalk.yellow('2.')} GitHub Setup ${chalk.dim('- Connect project board')}`,
596
592
  value: 'github',
597
593
  short: 'GitHub',
598
594
  },
599
595
  {
600
- name: `${chalk.yellow('4.')} View Templates ${chalk.dim('- Browse available items')}`,
596
+ name: `${chalk.dim('3.')} More Options ${chalk.dim('- Templates, releases, remove')}`,
597
+ value: 'more',
598
+ short: 'More',
599
+ },
600
+ {
601
+ name: `${chalk.dim('0.')} Exit`,
602
+ value: 'exit',
603
+ short: 'Exit',
604
+ },
605
+ ];
606
+
607
+ /**
608
+ * Advanced options submenu - accessed via "More Options"
609
+ */
610
+ const ADVANCED_OPTIONS = [
611
+ {
612
+ name: `${chalk.yellow('1.')} Custom Setup ${chalk.dim('- Choose specific features')}`,
613
+ value: 'custom',
614
+ short: 'Custom',
615
+ },
616
+ {
617
+ name: `${chalk.yellow('2.')} View Templates ${chalk.dim('- Browse available items')}`,
601
618
  value: 'templates',
602
619
  short: 'Templates',
603
620
  },
604
621
  {
605
- name: `${chalk.yellow('5.')} Prior Releases ${chalk.dim('- Review & add features from past versions')}`,
622
+ name: `${chalk.yellow('3.')} Prior Releases ${chalk.dim('- Add features from past versions')}`,
606
623
  value: 'releases',
607
624
  short: 'Releases',
608
625
  },
609
626
  {
610
- name: `${chalk.yellow('6.')} Remove CCASP ${chalk.dim('- Uninstall from this project')}`,
627
+ name: `${chalk.yellow('4.')} Remove CCASP ${chalk.dim('- Uninstall from this project')}`,
611
628
  value: 'remove',
612
629
  short: 'Remove',
613
630
  },
614
631
  {
615
- name: `${chalk.yellow('0.')} Exit`,
616
- value: 'exit',
617
- short: 'Exit',
632
+ name: `${chalk.dim('0.')} Back`,
633
+ value: 'back',
634
+ short: 'Back',
618
635
  },
619
636
  ];
620
637
 
@@ -1292,7 +1309,7 @@ export async function runSetupWizard(options = {}) {
1292
1309
  console.log(chalk.green('✓ CLAUDE.md exists\n'));
1293
1310
  }
1294
1311
 
1295
- // Main menu loop
1312
+ // Main menu loop - streamlined 3-path flow (Issue #8)
1296
1313
  let running = true;
1297
1314
  while (running) {
1298
1315
  const { action } = await inquirer.prompt([
@@ -1301,28 +1318,67 @@ export async function runSetupWizard(options = {}) {
1301
1318
  name: 'action',
1302
1319
  message: 'What would you like to do?',
1303
1320
  choices: SETUP_OPTIONS,
1304
- pageSize: 12,
1321
+ pageSize: 6, // All options visible without scrolling
1305
1322
  },
1306
1323
  ]);
1307
1324
 
1308
1325
  switch (action) {
1309
- case 'quick':
1310
- const quickSuccess = await runQuickSetup();
1311
- if (quickSuccess) running = false;
1312
- break;
1313
-
1314
- case 'full':
1315
- await runInit({ interactive: true });
1326
+ case 'auto':
1327
+ // Auto Install: Full features with mandatory backup (Issue #8 requirement)
1328
+ console.log(chalk.cyan('\n📦 Auto Install Mode - Full features with backup\n'));
1329
+ await runInit({
1330
+ interactive: false,
1331
+ backup: true, // Mandatory backup
1332
+ force: true, // Overwrite enabled for testing phase
1333
+ skipPrompts: true,
1334
+ preset: 'full' // All features
1335
+ });
1316
1336
  showRestartReminder();
1317
1337
  running = false;
1318
1338
  break;
1319
1339
 
1320
1340
  case 'github':
1321
1341
  await runGitHubSetup({});
1322
- // GitHub setup modifies .claude/ config
1323
1342
  showRestartReminder();
1324
1343
  break;
1325
1344
 
1345
+ case 'more':
1346
+ // Advanced options submenu
1347
+ await showAdvancedOptions();
1348
+ break;
1349
+
1350
+ case 'exit':
1351
+ running = false;
1352
+ console.log(chalk.dim('\nRun `ccasp wizard` anytime to return.\n'));
1353
+ break;
1354
+ }
1355
+ }
1356
+ }
1357
+
1358
+ /**
1359
+ * Advanced options submenu - Issue #8: Moved less-used options here
1360
+ */
1361
+ async function showAdvancedOptions() {
1362
+ let inSubmenu = true;
1363
+
1364
+ while (inSubmenu) {
1365
+ const { action } = await inquirer.prompt([
1366
+ {
1367
+ type: 'list',
1368
+ name: 'action',
1369
+ message: 'Advanced Options:',
1370
+ choices: ADVANCED_OPTIONS,
1371
+ pageSize: 6,
1372
+ },
1373
+ ]);
1374
+
1375
+ switch (action) {
1376
+ case 'custom':
1377
+ await runInit({ interactive: true });
1378
+ showRestartReminder();
1379
+ inSubmenu = false;
1380
+ break;
1381
+
1326
1382
  case 'templates':
1327
1383
  await showTemplates();
1328
1384
  break;
@@ -1334,13 +1390,12 @@ export async function runSetupWizard(options = {}) {
1334
1390
  case 'remove':
1335
1391
  const removed = await runRemove();
1336
1392
  if (removed) {
1337
- running = false; // Exit wizard after removal
1393
+ inSubmenu = false;
1338
1394
  }
1339
1395
  break;
1340
1396
 
1341
- case 'exit':
1342
- running = false;
1343
- console.log(chalk.dim('\nRun `ccasp wizard` anytime to return.\n'));
1397
+ case 'back':
1398
+ inSubmenu = false;
1344
1399
  break;
1345
1400
  }
1346
1401
  }
@@ -1,5 +1,20 @@
1
1
  {
2
2
  "releases": [
3
+ {
4
+ "version": "1.8.6",
5
+ "date": "2026-01-31",
6
+ "summary": "Release notes pending",
7
+ "highlights": [],
8
+ "newFeatures": {
9
+ "commands": [],
10
+ "agents": [],
11
+ "skills": [],
12
+ "hooks": [],
13
+ "other": []
14
+ },
15
+ "breaking": [],
16
+ "deprecated": []
17
+ },
3
18
  {
4
19
  "version": "1.8.5",
5
20
  "date": "2026-01-31",
@@ -19,8 +19,9 @@ const PACKAGE_NAME = 'claude-cli-advanced-starter-pack';
19
19
  // Cache duration: 1 hour (in milliseconds)
20
20
  const CACHE_DURATION = 60 * 60 * 1000;
21
21
 
22
- // Update notification duration: 1 day (in milliseconds)
23
- const UPDATE_NOTIFICATION_DURATION = 24 * 60 * 60 * 1000;
22
+ // Update notification reminder: Show reminder again after 7 days
23
+ // Issue #8: Changed from 1-day suppression to 7-day reminder
24
+ const UPDATE_NOTIFICATION_REMINDER = 7 * 24 * 60 * 60 * 1000;
24
25
 
25
26
  /**
26
27
  * Get the current installed version from package.json
@@ -313,19 +314,52 @@ export function compareVersions(v1, v2) {
313
314
  /**
314
315
  * Check npm registry for the latest version
315
316
  * Returns null if check fails (network error, etc.)
317
+ * Issue #8: Added npm registry API fallback for Windows compatibility
316
318
  */
317
319
  export async function checkLatestVersion() {
320
+ // Try npm CLI first
318
321
  try {
319
- // Use npm view command to get latest version
320
322
  const result = execSync(`npm view ${PACKAGE_NAME} version`, {
321
323
  encoding: 'utf8',
322
- timeout: 10000, // 10 second timeout
324
+ timeout: 10000,
323
325
  stdio: ['pipe', 'pipe', 'pipe'],
324
326
  });
325
327
 
326
- return result.trim();
328
+ const version = result.trim();
329
+ if (version && /^\d+\.\d+\.\d+/.test(version)) {
330
+ return version;
331
+ }
332
+ } catch {
333
+ // npm CLI failed, try fallback
334
+ }
335
+
336
+ // Fallback: Direct npm registry API call (Issue #8 fix)
337
+ try {
338
+ const https = await import('https');
339
+ return new Promise((resolve) => {
340
+ const req = https.default.get(
341
+ `https://registry.npmjs.org/${PACKAGE_NAME}/latest`,
342
+ { timeout: 8000 },
343
+ (res) => {
344
+ let data = '';
345
+ res.on('data', (chunk) => (data += chunk));
346
+ res.on('end', () => {
347
+ try {
348
+ const pkg = JSON.parse(data);
349
+ resolve(pkg.version || null);
350
+ } catch {
351
+ resolve(null);
352
+ }
353
+ });
354
+ }
355
+ );
356
+ req.on('error', () => resolve(null));
357
+ req.on('timeout', () => {
358
+ req.destroy();
359
+ resolve(null);
360
+ });
361
+ });
327
362
  } catch {
328
- // Silently fail - network might be unavailable
329
363
  return null;
330
364
  }
331
365
  }
@@ -436,17 +470,9 @@ export function shouldShowUpdateNotification(state, latestVersion) {
436
470
  return false;
437
471
  }
438
472
 
439
- // Check if we have a cached check result with timestamp
440
- if (state.lastCheckResult && state.lastCheckTimestamp) {
441
- const timeSinceCheck = Date.now() - state.lastCheckTimestamp;
442
-
443
- // If check is more than 1 day old, don't show notification
444
- // (user needs to run check again to see new updates)
445
- if (timeSinceCheck > UPDATE_NOTIFICATION_DURATION) {
446
- return false;
447
- }
448
- }
449
-
473
+ // Issue #8: Always show notification if update is available
474
+ // The old logic suppressed notifications after 1 day, which was counterproductive
475
+ // Now we always show if there's an update, regardless of cache age
450
476
  return true;
451
477
  }
452
478
 
@@ -197,6 +197,78 @@ gh issue edit [NUMBER] --body "$(gh issue view [NUMBER] --json body -q .body)
197
197
 
198
198
  ---
199
199
 
200
+ ### Step 7: After Task Completion - Close Issue Prompt
201
+
202
+ **CRITICAL: After ALL TodoWrite tasks are marked complete AND a commit is created, ALWAYS offer to close the issue.**
203
+
204
+ This step triggers when:
205
+ 1. All tasks in TodoWrite are marked `completed`
206
+ 2. A git commit has been made with changes
207
+
208
+ Display completion summary:
209
+
210
+ ```
211
+ ╔═══════════════════════════════════════════════════════════════╗
212
+ ║ ✅ All Tasks Completed ║
213
+ ╠═══════════════════════════════════════════════════════════════╣
214
+ ║ ║
215
+ ║ Issue: #[NUMBER] - [TITLE] ║
216
+ ║ Commit: [SHORT_SHA] - [COMMIT_MSG_FIRST_LINE] ║
217
+ ║ Tasks: [X] completed ║
218
+ ║ ║
219
+ ╠═══════════════════════════════════════════════════════════════╣
220
+ ║ [C] Close issue with comment ║
221
+ ║ [P] Push to origin + close issue ║
222
+ ║ [K] Keep issue open ║
223
+ ╚═══════════════════════════════════════════════════════════════╝
224
+ ```
225
+
226
+ Then ask:
227
+
228
+ ```
229
+ header: "Issue"
230
+ question: "All tasks complete. Close issue #[NUMBER]?"
231
+ options:
232
+ - label: "C - Close with comment"
233
+ description: "Add completion summary and close"
234
+ - label: "P - Push + Close"
235
+ description: "Push commit to origin, then close"
236
+ - label: "K - Keep open"
237
+ description: "Leave issue open for follow-up"
238
+ ```
239
+
240
+ **Handle Close Actions:**
241
+
242
+ **C (Close with comment):**
243
+ ```bash
244
+ gh issue close [NUMBER] --comment "All tasks completed in commit [SHA].
245
+
246
+ ## Completed Tasks
247
+ - ✅ Task 1
248
+ - ✅ Task 2
249
+ ...
250
+
251
+ Ready for release."
252
+ ```
253
+
254
+ **P (Push + Close):**
255
+ ```bash
256
+ git push origin HEAD
257
+ gh issue close [NUMBER] --comment "All tasks completed and pushed in commit [SHA].
258
+
259
+ ## Completed Tasks
260
+ - ✅ Task 1
261
+ - ✅ Task 2
262
+ ...
263
+
264
+ Ready for release."
265
+ ```
266
+
267
+ **K (Keep open):**
268
+ Display: "Issue #[NUMBER] kept open for follow-up."
269
+
270
+ ---
271
+
200
272
  ## ERROR HANDLING
201
273
 
202
274
  | Error | Action |
@@ -874,6 +874,79 @@ After all tasks are complete, display a summary:
874
874
 
875
875
  ---
876
876
 
877
+ ## CLOSE ISSUE PROMPT (When Working From GitHub Issue)
878
+
879
+ **CRITICAL: If this task list was created for a GitHub issue, ALWAYS offer to close the issue after ALL TodoWrite tasks are marked complete AND a commit is created.**
880
+
881
+ This step triggers when:
882
+ 1. The task list originated from a GitHub issue (via `/create-task-list for issue #N` or `/menu-issues-list`)
883
+ 2. All tasks in TodoWrite are marked `completed`
884
+ 3. A git commit has been made with changes
885
+
886
+ Display completion summary:
887
+
888
+ ```
889
+ ╔═══════════════════════════════════════════════════════════════╗
890
+ ║ ✅ All Tasks Completed ║
891
+ ╠═══════════════════════════════════════════════════════════════╣
892
+ ║ ║
893
+ ║ Issue: #[NUMBER] - [TITLE] ║
894
+ ║ Commit: [SHORT_SHA] - [COMMIT_MSG_FIRST_LINE] ║
895
+ ║ Tasks: [X] completed ║
896
+ ║ ║
897
+ ╠═══════════════════════════════════════════════════════════════╣
898
+ ║ [C] Close issue with comment ║
899
+ ║ [P] Push to origin + close issue ║
900
+ ║ [K] Keep issue open ║
901
+ ╚═══════════════════════════════════════════════════════════════╝
902
+ ```
903
+
904
+ Then ask:
905
+
906
+ ```
907
+ header: "Issue"
908
+ question: "All tasks complete. Close issue #[NUMBER]?"
909
+ options:
910
+ - label: "C - Close with comment"
911
+ description: "Add completion summary and close"
912
+ - label: "P - Push + Close"
913
+ description: "Push commit to origin, then close"
914
+ - label: "K - Keep open"
915
+ description: "Leave issue open for follow-up"
916
+ ```
917
+
918
+ **Handle Close Actions:**
919
+
920
+ **C (Close with comment):**
921
+ ```bash
922
+ gh issue close [NUMBER] --comment "All tasks completed in commit [SHA].
923
+
924
+ ## Completed Tasks
925
+ - ✅ Task 1
926
+ - ✅ Task 2
927
+ ...
928
+
929
+ Ready for release."
930
+ ```
931
+
932
+ **P (Push + Close):**
933
+ ```bash
934
+ git push origin HEAD
935
+ gh issue close [NUMBER] --comment "All tasks completed and pushed in commit [SHA].
936
+
937
+ ## Completed Tasks
938
+ - ✅ Task 1
939
+ - ✅ Task 2
940
+ ...
941
+
942
+ Ready for release."
943
+ ```
944
+
945
+ **K (Keep open):**
946
+ Display: "Issue #[NUMBER] kept open for follow-up."
947
+
948
+ ---
949
+
877
950
  ## ERROR HANDLING
878
951
 
879
952
  | Situation | Action |
@@ -0,0 +1,137 @@
1
+ ---
2
+ description: Re-run tech stack detection and update configuration
3
+ ---
4
+
5
+ # /detect-tech-stack - Tech Stack Analysis
6
+
7
+ Re-analyze the project's tech stack and update `.claude/config/tech-stack.json`.
8
+
9
+ ## Purpose
10
+
11
+ Issue #8: Tech stack detection previously only ran during terminal phase (ccasp init).
12
+ This command allows re-running detection from within Claude CLI to:
13
+ - Detect new dependencies added since initial setup
14
+ - Update framework versions
15
+ - Refresh configuration for agents, skills, and hooks
16
+
17
+ ## Execution Steps
18
+
19
+ ### Step 1: Read Current Tech Stack
20
+
21
+ ```bash
22
+ cat .claude/config/tech-stack.json 2>/dev/null || echo "{}"
23
+ ```
24
+
25
+ Save as `previousStack` for comparison.
26
+
27
+ ### Step 2: Detect Current Stack
28
+
29
+ Analyze the following files to build the new tech stack:
30
+
31
+ **Package Managers:**
32
+ - `package.json` → Node.js/npm project
33
+ - `pyproject.toml` or `requirements.txt` → Python project
34
+ - `Cargo.toml` → Rust project
35
+ - `go.mod` → Go project
36
+ - `pom.xml` or `build.gradle` → Java project
37
+
38
+ **Frontend Frameworks (from package.json):**
39
+ - `react` → React
40
+ - `vue` → Vue.js
41
+ - `@angular/core` → Angular
42
+ - `svelte` → Svelte
43
+ - `next` → Next.js
44
+ - `nuxt` → Nuxt.js
45
+ - `vite` → Vite bundler
46
+
47
+ **Backend Frameworks:**
48
+ - `express` → Express.js
49
+ - `fastify` → Fastify
50
+ - `@nestjs/core` → NestJS
51
+ - `fastapi` (Python) → FastAPI
52
+ - `django` (Python) → Django
53
+ - `flask` (Python) → Flask
54
+
55
+ **Testing Frameworks:**
56
+ - `jest` → Jest
57
+ - `vitest` → Vitest
58
+ - `playwright` → Playwright
59
+ - `cypress` → Cypress
60
+ - `pytest` (Python) → Pytest
61
+
62
+ **Database:**
63
+ - `prisma` → Prisma ORM
64
+ - `drizzle-orm` → Drizzle
65
+ - `mongoose` → MongoDB
66
+ - `pg` or `postgres` → PostgreSQL
67
+ - `mysql2` → MySQL
68
+
69
+ **Deployment:**
70
+ Check for config files:
71
+ - `wrangler.toml` → Cloudflare
72
+ - `railway.json` or `railway.toml` → Railway
73
+ - `vercel.json` → Vercel
74
+ - `netlify.toml` → Netlify
75
+ - `Dockerfile` → Docker
76
+
77
+ ### Step 3: Compare and Report Changes
78
+
79
+ Compare `previousStack` with newly detected stack:
80
+
81
+ ```
82
+ ╔═══════════════════════════════════════════════════════════════╗
83
+ ║ 📊 Tech Stack Analysis ║
84
+ ╠═══════════════════════════════════════════════════════════════╣
85
+ ║ ║
86
+ ║ Language: {{language}} ║
87
+ ║ Frontend: {{frontend.framework}} + {{frontend.bundler}} ║
88
+ ║ Backend: {{backend.framework}} ║
89
+ ║ Database: {{database.type}} ║
90
+ ║ Testing: {{testing.frameworks}} ║
91
+ ║ Deploy: {{deployment.platform}} ║
92
+ ║ ║
93
+ ╠═══════════════════════════════════════════════════════════════╣
94
+ ║ Changes Detected: ║
95
+ ║ [+] Added: (list new dependencies) ║
96
+ ║ [-] Removed: (list removed dependencies) ║
97
+ ║ [~] Updated: (list version changes) ║
98
+ ║ ║
99
+ ╚═══════════════════════════════════════════════════════════════╝
100
+ ```
101
+
102
+ ### Step 4: Update tech-stack.json
103
+
104
+ Write the updated configuration:
105
+
106
+ ```bash
107
+ # Backup current config
108
+ cp .claude/config/tech-stack.json .claude/config/tech-stack.json.bak
109
+
110
+ # Write new config (use actual detected values)
111
+ ```
112
+
113
+ ### Step 5: Suggest Agent/Skill Updates
114
+
115
+ Based on detected changes, recommend:
116
+
117
+ | Change | Recommendation |
118
+ |--------|----------------|
119
+ | Added Playwright | Enable E2E testing skill |
120
+ | Added Prisma | Enable database agent |
121
+ | Added React 19 | Update component patterns |
122
+ | New test framework | Configure test runner |
123
+
124
+ ## Output
125
+
126
+ After completion, display:
127
+
128
+ 1. **Summary** of detected tech stack
129
+ 2. **Diff** showing what changed since last detection
130
+ 3. **Recommendations** for updating CCASP configuration
131
+ 4. **Restart reminder** if significant changes detected
132
+
133
+ ## Related Commands
134
+
135
+ - `/claude-audit` - Audit CLAUDE.md configuration
136
+ - `/update-smart` - Smart update manager
137
+ - `/project-impl` - Project implementation agent
@@ -260,6 +260,78 @@ gh issue edit [NUMBER] --body "$(gh issue view [NUMBER] --json body -q .body)
260
260
 
261
261
  ---
262
262
 
263
+ ### Step 6: After Task Completion - Close Issue Prompt
264
+
265
+ **CRITICAL: After ALL TodoWrite tasks are marked complete AND a commit is created, ALWAYS offer to close the issue.**
266
+
267
+ This step triggers when:
268
+ 1. All tasks in TodoWrite are marked `completed`
269
+ 2. A git commit has been made with changes
270
+
271
+ Display completion summary:
272
+
273
+ ```
274
+ ╔═══════════════════════════════════════════════════════════════╗
275
+ ║ ✅ All Tasks Completed ║
276
+ ╠═══════════════════════════════════════════════════════════════╣
277
+ ║ ║
278
+ ║ Issue: #[NUMBER] - [TITLE] ║
279
+ ║ Commit: [SHORT_SHA] - [COMMIT_MSG_FIRST_LINE] ║
280
+ ║ Tasks: [X] completed ║
281
+ ║ ║
282
+ ╠═══════════════════════════════════════════════════════════════╣
283
+ ║ [C] Close issue with comment ║
284
+ ║ [P] Push to origin + close issue ║
285
+ ║ [K] Keep issue open ║
286
+ ╚═══════════════════════════════════════════════════════════════╝
287
+ ```
288
+
289
+ Then ask:
290
+
291
+ ```
292
+ header: "Issue"
293
+ question: "All tasks complete. Close issue #[NUMBER]?"
294
+ options:
295
+ - label: "C - Close with comment"
296
+ description: "Add completion summary and close"
297
+ - label: "P - Push + Close"
298
+ description: "Push commit to origin, then close"
299
+ - label: "K - Keep open"
300
+ description: "Leave issue open for follow-up"
301
+ ```
302
+
303
+ **Handle Close Actions:**
304
+
305
+ **C (Close with comment):**
306
+ ```bash
307
+ gh issue close [NUMBER] --comment "All tasks completed in commit [SHA].
308
+
309
+ ## Completed Tasks
310
+ - ✅ Task 1
311
+ - ✅ Task 2
312
+ ...
313
+
314
+ Ready for release."
315
+ ```
316
+
317
+ **P (Push + Close):**
318
+ ```bash
319
+ git push origin HEAD
320
+ gh issue close [NUMBER] --comment "All tasks completed and pushed in commit [SHA].
321
+
322
+ ## Completed Tasks
323
+ - ✅ Task 1
324
+ - ✅ Task 2
325
+ ...
326
+
327
+ Ready for release."
328
+ ```
329
+
330
+ **K (Keep open):**
331
+ Display: "Issue #[NUMBER] kept open for follow-up."
332
+
333
+ ---
334
+
263
335
  ## MOBILE OPTIMIZATION
264
336
 
265
337
  - Single character inputs (A, B, C, S, V, X)
@@ -89,15 +89,50 @@ function getCurrentVersion() {
89
89
 
90
90
  /**
91
91
  * Check npm for latest version
92
+ * Issue #8: Added npm registry API fallback for Windows compatibility
92
93
  */
93
94
  function checkLatestVersion() {
95
+ // Try npm CLI first
94
96
  try {
95
97
  const result = execSync(`npm view ${PACKAGE_NAME} version`, {
96
98
  encoding: 'utf8',
97
99
  timeout: 10000,
98
100
  stdio: ['pipe', 'pipe', 'pipe'],
99
101
  });
100
- return result.trim();
102
+ const version = result.trim();
103
+ if (version && /^\d+\.\d+\.\d+/.test(version)) {
104
+ return version;
105
+ }
106
+ } catch {
107
+ // npm CLI failed, try fallback
108
+ }
109
+
110
+ // Fallback: Direct npm registry API call
111
+ try {
112
+ const https = require('https');
113
+ return new Promise((resolve) => {
114
+ const req = https.get(
115
+ `https://registry.npmjs.org/${PACKAGE_NAME}/latest`,
116
+ { timeout: 8000 },
117
+ (res) => {
118
+ let data = '';
119
+ res.on('data', (chunk) => (data += chunk));
120
+ res.on('end', () => {
121
+ try {
122
+ const pkg = JSON.parse(data);
123
+ resolve(pkg.version || null);
124
+ } catch {
125
+ resolve(null);
126
+ }
127
+ });
128
+ }
129
+ );
130
+ req.on('error', () => resolve(null));
131
+ req.on('timeout', () => {
132
+ req.destroy();
133
+ resolve(null);
134
+ });
135
+ });
101
136
  } catch {
102
137
  return null;
103
138
  }