claude-cli-advanced-starter-pack 1.0.12 → 1.0.14

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.0.12",
3
+ "version": "1.0.14",
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": {
@@ -15,6 +15,19 @@ import { fileURLToPath } from 'url';
15
15
  import { showHeader, showSuccess, showError, showWarning, showInfo } from '../cli/menu.js';
16
16
  import { getVersion } from '../utils.js';
17
17
  import { createBackup } from './setup-wizard.js';
18
+ import {
19
+ loadUsageTracking,
20
+ getCustomizedUsedAssets,
21
+ isAssetCustomized,
22
+ } from '../utils/version-check.js';
23
+ import {
24
+ getAssetsNeedingMerge,
25
+ compareAssetVersions,
26
+ getLocalAsset,
27
+ getTemplateAsset,
28
+ generateMergeExplanation,
29
+ formatMergeOptions,
30
+ } from '../utils/smart-merge.js';
18
31
 
19
32
  const __filename = fileURLToPath(import.meta.url);
20
33
  const __dirname = dirname(__filename);
@@ -29,7 +42,7 @@ const OPTIONAL_FEATURES = [
29
42
  label: 'Token Budget Management',
30
43
  description: 'Monitor and manage Claude API token usage with automatic compaction warnings, archive suggestions, and respawn thresholds. Includes hooks that track usage per session.',
31
44
  commands: ['context-audit'],
32
- hooks: ['token-budget-loader', 'context-guardian', 'tool-output-cacher'],
45
+ hooks: ['context-guardian'], // Only include hooks with templates
33
46
  default: false,
34
47
  requiresPostConfig: false,
35
48
  },
@@ -38,7 +51,7 @@ const OPTIONAL_FEATURES = [
38
51
  label: 'Happy Engineering Integration',
39
52
  description: 'Integration with Happy Coder mobile app for remote session control, checkpoint management, and mobile-optimized responses. Requires Happy Coder app installed separately.',
40
53
  commands: ['happy-start'],
41
- hooks: ['happy-checkpoint-manager', 'happy-title-generator', 'happy-mode-detector'],
54
+ hooks: ['happy-checkpoint-manager'], // Only include hooks with templates
42
55
  default: false,
43
56
  requiresPostConfig: true,
44
57
  },
@@ -47,7 +60,7 @@ const OPTIONAL_FEATURES = [
47
60
  label: 'GitHub Project Board Integration',
48
61
  description: 'Connect Claude to your GitHub Project Board for automated issue creation, progress tracking, and PR merge automation. Requires gh CLI authentication.',
49
62
  commands: ['github-update', 'github-task-start'],
50
- hooks: ['github-progress-hook', 'issue-completion-detector'],
63
+ hooks: ['github-progress-hook'], // Only include hooks with templates
51
64
  default: true,
52
65
  requiresPostConfig: true,
53
66
  },
@@ -245,6 +258,12 @@ const AVAILABLE_COMMANDS = [
245
258
  category: 'Maintenance',
246
259
  selected: true,
247
260
  },
261
+ {
262
+ name: 'update-smart',
263
+ description: 'Smart merge manager for customized assets during updates',
264
+ category: 'Maintenance',
265
+ selected: true,
266
+ },
248
267
  ];
249
268
 
250
269
  /**
@@ -1482,7 +1501,7 @@ module.exports = async function ccaspUpdateCheck(context) {
1482
1501
  }
1483
1502
 
1484
1503
  /**
1485
- * Generate settings.json with CCASP update check hook
1504
+ * Generate settings.json with CCASP update check hook and usage tracking
1486
1505
  */
1487
1506
  function generateSettingsJson(projectName) {
1488
1507
  return JSON.stringify({
@@ -1502,6 +1521,17 @@ function generateSettingsJson(projectName) {
1502
1521
  }
1503
1522
  ]
1504
1523
  }
1524
+ ],
1525
+ "PostToolUse": [
1526
+ {
1527
+ "matcher": "Skill|Read",
1528
+ "hooks": [
1529
+ {
1530
+ "type": "command",
1531
+ "command": "node .claude/hooks/usage-tracking.js"
1532
+ }
1533
+ ]
1534
+ }
1505
1535
  ]
1506
1536
  }
1507
1537
  }, null, 2);
@@ -1738,6 +1768,19 @@ export async function runInit(options = {}) {
1738
1768
  console.log(chalk.blue(' ○ hooks/ccasp-update-check.js exists (preserved)'));
1739
1769
  }
1740
1770
 
1771
+ // Deploy the usage tracking hook (tracks command/skill/agent usage for smart merge)
1772
+ const usageTrackingHookPath = join(hooksDir, 'usage-tracking.js');
1773
+ if (!existsSync(usageTrackingHookPath)) {
1774
+ const templatePath = join(__dirname, '..', '..', 'templates', 'hooks', 'usage-tracking.template.js');
1775
+ if (existsSync(templatePath)) {
1776
+ const hookContent = readFileSync(templatePath, 'utf8');
1777
+ writeFileSync(usageTrackingHookPath, hookContent, 'utf8');
1778
+ console.log(chalk.green(' ✓ Created hooks/usage-tracking.js (smart merge tracking)'));
1779
+ }
1780
+ } else {
1781
+ console.log(chalk.blue(' ○ hooks/usage-tracking.js exists (preserved)'));
1782
+ }
1783
+
1741
1784
  console.log('');
1742
1785
 
1743
1786
  // Step 4: Select optional features
@@ -1775,6 +1818,25 @@ export async function runInit(options = {}) {
1775
1818
  const enabledFeatures = OPTIONAL_FEATURES.filter((f) => selectedFeatures.includes(f.name));
1776
1819
  const featuresRequiringConfig = enabledFeatures.filter((f) => f.requiresPostConfig);
1777
1820
 
1821
+ // Collect feature-specific commands and hooks to deploy
1822
+ const featureCommands = [];
1823
+ const featureHooks = [];
1824
+ for (const feature of enabledFeatures) {
1825
+ featureCommands.push(...feature.commands);
1826
+ featureHooks.push(...feature.hooks);
1827
+ }
1828
+
1829
+ if (featureCommands.length > 0) {
1830
+ console.log('');
1831
+ console.log(chalk.green(` ✓ Selected features will add ${featureCommands.length} command(s):`));
1832
+ console.log(chalk.dim(` ${featureCommands.map(c => '/' + c).join(', ')}`));
1833
+ }
1834
+
1835
+ if (featureHooks.length > 0) {
1836
+ console.log(chalk.green(` ✓ Selected features will add ${featureHooks.length} hook(s):`));
1837
+ console.log(chalk.dim(` ${featureHooks.join(', ')}`));
1838
+ }
1839
+
1778
1840
  if (featuresRequiringConfig.length > 0) {
1779
1841
  console.log('');
1780
1842
  console.log(chalk.yellow(' ℹ The following features require configuration after installation:'));
@@ -1835,67 +1897,246 @@ export async function runInit(options = {}) {
1835
1897
  },
1836
1898
  ]);
1837
1899
 
1838
- // Always include required commands
1900
+ // Always include required commands AND feature-specific commands
1839
1901
  const requiredCommands = AVAILABLE_COMMANDS.filter(c => c.required).map(c => c.name);
1840
- const finalCommands = [...new Set([...requiredCommands, ...selectedCommands])];
1902
+ const finalCommands = [...new Set([...requiredCommands, ...selectedCommands, ...featureCommands])];
1841
1903
 
1842
1904
  if (finalCommands.length === 0) {
1843
1905
  showWarning('No commands selected. Nothing to install.');
1844
1906
  return;
1845
1907
  }
1846
1908
 
1909
+ // Show what feature commands were auto-added
1910
+ const autoAddedCommands = featureCommands.filter(c => !selectedCommands.includes(c) && !requiredCommands.includes(c));
1911
+ if (autoAddedCommands.length > 0) {
1912
+ console.log(chalk.cyan(` ℹ Auto-including ${autoAddedCommands.length} feature command(s): ${autoAddedCommands.map(c => '/' + c).join(', ')}`));
1913
+ }
1914
+
1847
1915
  console.log('');
1848
1916
 
1849
1917
  // Step 6: Check for existing commands that would be overwritten
1850
1918
  const commandsToOverwrite = finalCommands.filter(cmd => existingCmdNames.includes(cmd));
1851
1919
 
1920
+ // Track commands that need smart merge handling
1921
+ const smartMergeDecisions = {};
1922
+
1852
1923
  let overwrite = options.force || false;
1853
1924
  if (commandsToOverwrite.length > 0 && !overwrite) {
1854
- console.log(chalk.yellow.bold(' ⚠ The following commands already exist:'));
1855
- for (const cmd of commandsToOverwrite) {
1856
- console.log(chalk.yellow(` • /${cmd}`));
1857
- }
1858
- console.log('');
1925
+ // Check for customized assets that have been used
1926
+ const assetsNeedingMerge = getAssetsNeedingMerge(process.cwd());
1927
+ const customizedCommands = commandsToOverwrite.filter(cmd =>
1928
+ assetsNeedingMerge.commands?.some(a => a.name === cmd)
1929
+ );
1930
+
1931
+ // Show smart merge prompt for customized commands
1932
+ if (customizedCommands.length > 0) {
1933
+ console.log(chalk.cyan.bold('\n 🔀 Smart Merge Available'));
1934
+ console.log(chalk.dim(' The following commands have been customized and used:\n'));
1935
+
1936
+ for (const cmd of customizedCommands) {
1937
+ const assetInfo = assetsNeedingMerge.commands.find(a => a.name === cmd);
1938
+ console.log(chalk.cyan(` • /${cmd}`));
1939
+ console.log(chalk.dim(` Used ${assetInfo.usageData.useCount} time(s), last: ${new Date(assetInfo.usageData.lastUsed).toLocaleDateString()}`));
1940
+ console.log(chalk.dim(` Change: ${assetInfo.comparison.significance.level} significance - ${assetInfo.comparison.summary}`));
1941
+ }
1942
+ console.log('');
1859
1943
 
1860
- const { overwriteChoice } = await inquirer.prompt([
1861
- {
1862
- type: 'list',
1863
- name: 'overwriteChoice',
1864
- message: 'How would you like to handle existing commands?',
1865
- choices: [
1866
- { name: 'Skip existing - only install new commands (recommended)', value: 'skip' },
1867
- { name: 'Overwrite with backup - save existing to .claude/backups/ first', value: 'backup' },
1868
- { name: 'Overwrite all - replace existing (no backup)', value: 'overwrite' },
1869
- { name: 'Cancel installation', value: 'cancel' },
1870
- ],
1871
- },
1872
- ]);
1944
+ const { smartMergeAction } = await inquirer.prompt([
1945
+ {
1946
+ type: 'list',
1947
+ name: 'smartMergeAction',
1948
+ message: 'How would you like to handle your customized commands?',
1949
+ choices: [
1950
+ { name: '🔍 Explore each one - Let Claude explain the changes', value: 'explore' },
1951
+ { name: '📋 Skip all customized - Keep your versions', value: 'skip-customized' },
1952
+ { name: '🔄 Replace all - Use new versions (lose customizations)', value: 'replace-all' },
1953
+ { name: 'Cancel installation', value: 'cancel' },
1954
+ ],
1955
+ },
1956
+ ]);
1957
+
1958
+ if (smartMergeAction === 'cancel') {
1959
+ console.log(chalk.dim('\nCancelled. No changes made.'));
1960
+ return;
1961
+ }
1873
1962
 
1874
- if (overwriteChoice === 'cancel') {
1875
- console.log(chalk.dim('\nCancelled. No changes made.'));
1876
- return;
1963
+ if (smartMergeAction === 'explore') {
1964
+ // Individual exploration for each customized command
1965
+ console.log(chalk.cyan('\n Exploring customized commands...\n'));
1966
+
1967
+ for (const cmd of customizedCommands) {
1968
+ const assetInfo = assetsNeedingMerge.commands.find(a => a.name === cmd);
1969
+ const local = getLocalAsset('commands', cmd, process.cwd());
1970
+ const template = getTemplateAsset('commands', cmd);
1971
+
1972
+ // Show merge explanation
1973
+ console.log(chalk.bold(`\n ┌${'─'.repeat(60)}┐`));
1974
+ console.log(chalk.bold(` │ /${cmd.padEnd(58)} │`));
1975
+ console.log(chalk.bold(` └${'─'.repeat(60)}┘`));
1976
+
1977
+ const explanation = generateMergeExplanation(
1978
+ 'commands',
1979
+ cmd,
1980
+ assetInfo.comparison,
1981
+ local?.content,
1982
+ template?.content
1983
+ );
1984
+
1985
+ // Display condensed explanation
1986
+ console.log(chalk.dim('\n ' + explanation.split('\n').slice(0, 15).join('\n ')));
1987
+
1988
+ const { decision } = await inquirer.prompt([
1989
+ {
1990
+ type: 'list',
1991
+ name: 'decision',
1992
+ message: `What would you like to do with /${cmd}?`,
1993
+ choices: [
1994
+ { name: 'Skip - Keep your customized version', value: 'skip' },
1995
+ { name: 'Backup & Replace - Save yours, use new version', value: 'backup' },
1996
+ { name: 'Replace - Use new version (no backup)', value: 'replace' },
1997
+ { name: 'Show full diff', value: 'diff' },
1998
+ ],
1999
+ },
2000
+ ]);
2001
+
2002
+ if (decision === 'diff') {
2003
+ // Show full diff
2004
+ console.log(chalk.dim('\n--- Your Version ---'));
2005
+ console.log(local?.content?.slice(0, 500) + (local?.content?.length > 500 ? '\n...(truncated)' : ''));
2006
+ console.log(chalk.dim('\n--- Update Version ---'));
2007
+ console.log(template?.content?.slice(0, 500) + (template?.content?.length > 500 ? '\n...(truncated)' : ''));
2008
+
2009
+ // Re-prompt after showing diff
2010
+ const { finalDecision } = await inquirer.prompt([
2011
+ {
2012
+ type: 'list',
2013
+ name: 'finalDecision',
2014
+ message: `Final decision for /${cmd}?`,
2015
+ choices: [
2016
+ { name: 'Skip - Keep your version', value: 'skip' },
2017
+ { name: 'Backup & Replace', value: 'backup' },
2018
+ { name: 'Replace without backup', value: 'replace' },
2019
+ ],
2020
+ },
2021
+ ]);
2022
+ smartMergeDecisions[cmd] = finalDecision;
2023
+ } else {
2024
+ smartMergeDecisions[cmd] = decision;
2025
+ }
2026
+ }
2027
+ } else if (smartMergeAction === 'skip-customized') {
2028
+ // Mark all customized commands as skip
2029
+ for (const cmd of customizedCommands) {
2030
+ smartMergeDecisions[cmd] = 'skip';
2031
+ }
2032
+ console.log(chalk.green(`\n ✓ Will preserve ${customizedCommands.length} customized command(s)`));
2033
+ } else if (smartMergeAction === 'replace-all') {
2034
+ // Mark all customized commands as replace with backup
2035
+ for (const cmd of customizedCommands) {
2036
+ smartMergeDecisions[cmd] = 'backup';
2037
+ }
2038
+ console.log(chalk.yellow(`\n ⚠ Will backup and replace ${customizedCommands.length} customized command(s)`));
2039
+ }
2040
+
2041
+ // Remove customized commands from the standard overwrite flow
2042
+ // (they're handled by smart merge decisions)
2043
+ const nonCustomizedToOverwrite = commandsToOverwrite.filter(c => !customizedCommands.includes(c));
2044
+
2045
+ if (nonCustomizedToOverwrite.length > 0) {
2046
+ console.log(chalk.yellow.bold('\n ⚠ The following non-customized commands also exist:'));
2047
+ for (const cmd of nonCustomizedToOverwrite) {
2048
+ console.log(chalk.yellow(` • /${cmd}`));
2049
+ }
2050
+ }
1877
2051
  }
1878
2052
 
1879
- overwrite = overwriteChoice === 'overwrite' || overwriteChoice === 'backup';
1880
- const createBackups = overwriteChoice === 'backup';
1881
-
1882
- if (!overwrite) {
1883
- // Filter out existing commands (keep only new ones + required)
1884
- const filtered = finalCommands.filter((c) => !existingCmdNames.includes(c) || requiredCommands.includes(c));
1885
- finalCommands.length = 0;
1886
- finalCommands.push(...filtered);
1887
- console.log(chalk.green(`\n ✓ Will install ${finalCommands.length} new command(s), preserving ${commandsToOverwrite.length} existing`));
1888
- } else if (createBackups) {
1889
- console.log(chalk.cyan(`\n ✓ Will backup and overwrite ${commandsToOverwrite.length} existing command(s)`));
1890
- } else {
1891
- console.log(chalk.yellow(`\n ⚠ Will overwrite ${commandsToOverwrite.length} existing command(s)`));
2053
+ // Standard overwrite prompt for non-customized commands
2054
+ const remainingToOverwrite = commandsToOverwrite.filter(c => !smartMergeDecisions[c]);
2055
+
2056
+ if (remainingToOverwrite.length > 0) {
2057
+ if (!customizedCommands || customizedCommands.length === 0) {
2058
+ console.log(chalk.yellow.bold(' ⚠ The following commands already exist:'));
2059
+ for (const cmd of remainingToOverwrite) {
2060
+ console.log(chalk.yellow(` • /${cmd}`));
2061
+ }
2062
+ }
2063
+ console.log('');
2064
+
2065
+ const { overwriteChoice } = await inquirer.prompt([
2066
+ {
2067
+ type: 'list',
2068
+ name: 'overwriteChoice',
2069
+ message: 'How would you like to handle these existing commands?',
2070
+ choices: [
2071
+ { name: 'Skip existing - only install new commands (recommended)', value: 'skip' },
2072
+ { name: 'Overwrite with backup - save existing to .claude/backups/ first', value: 'backup' },
2073
+ { name: 'Overwrite all - replace existing (no backup)', value: 'overwrite' },
2074
+ { name: 'Cancel installation', value: 'cancel' },
2075
+ ],
2076
+ },
2077
+ ]);
2078
+
2079
+ if (overwriteChoice === 'cancel') {
2080
+ console.log(chalk.dim('\nCancelled. No changes made.'));
2081
+ return;
2082
+ }
2083
+
2084
+ overwrite = overwriteChoice === 'overwrite' || overwriteChoice === 'backup';
2085
+
2086
+ // Apply decision to remaining commands
2087
+ for (const cmd of remainingToOverwrite) {
2088
+ smartMergeDecisions[cmd] = overwriteChoice === 'skip' ? 'skip' : (overwriteChoice === 'backup' ? 'backup' : 'replace');
2089
+ }
2090
+
2091
+ if (!overwrite) {
2092
+ // Filter out skipped commands
2093
+ const skippedCommands = Object.entries(smartMergeDecisions)
2094
+ .filter(([, decision]) => decision === 'skip')
2095
+ .map(([cmd]) => cmd);
2096
+ const filtered = finalCommands.filter((c) => !skippedCommands.includes(c) || requiredCommands.includes(c));
2097
+ finalCommands.length = 0;
2098
+ finalCommands.push(...filtered);
2099
+ console.log(chalk.green(`\n ✓ Will install ${finalCommands.length} new command(s), preserving ${skippedCommands.length} existing`));
2100
+ } else if (overwriteChoice === 'backup') {
2101
+ console.log(chalk.cyan(`\n ✓ Will backup and overwrite ${remainingToOverwrite.length} existing command(s)`));
2102
+ } else {
2103
+ console.log(chalk.yellow(`\n ⚠ Will overwrite ${remainingToOverwrite.length} existing command(s)`));
2104
+ }
2105
+ } else if (Object.keys(smartMergeDecisions).length > 0) {
2106
+ // All commands handled by smart merge
2107
+ const skippedCommands = Object.entries(smartMergeDecisions)
2108
+ .filter(([, decision]) => decision === 'skip')
2109
+ .map(([cmd]) => cmd);
2110
+
2111
+ if (skippedCommands.length > 0) {
2112
+ const filtered = finalCommands.filter((c) => !skippedCommands.includes(c) || requiredCommands.includes(c));
2113
+ finalCommands.length = 0;
2114
+ finalCommands.push(...filtered);
2115
+ }
1892
2116
  }
1893
2117
  }
1894
2118
 
1895
2119
  // Track if we should create backups (set outside the if block for use later)
2120
+ // Now also considers smart merge decisions
1896
2121
  const createBackups = options.backup || (typeof overwrite !== 'undefined' && commandsToOverwrite.length > 0 && !options.force);
1897
2122
  let backedUpFiles = [];
1898
2123
 
2124
+ // Helper to check if a command should be backed up based on smart merge decisions
2125
+ const shouldBackupCommand = (cmdName) => {
2126
+ if (smartMergeDecisions[cmdName]) {
2127
+ return smartMergeDecisions[cmdName] === 'backup';
2128
+ }
2129
+ return createBackups;
2130
+ };
2131
+
2132
+ // Helper to check if a command should be skipped based on smart merge decisions
2133
+ const shouldSkipCommand = (cmdName) => {
2134
+ if (smartMergeDecisions[cmdName]) {
2135
+ return smartMergeDecisions[cmdName] === 'skip';
2136
+ }
2137
+ return false;
2138
+ };
2139
+
1899
2140
  // Step 7: Install commands
1900
2141
  console.log(chalk.bold('Step 6: Installing slash commands\n'));
1901
2142
 
@@ -1916,6 +2157,11 @@ export async function runInit(options = {}) {
1916
2157
 
1917
2158
  for (const cmdName of finalCommands) {
1918
2159
  try {
2160
+ // Skip commands that were marked to skip in smart merge
2161
+ if (shouldSkipCommand(cmdName)) {
2162
+ continue;
2163
+ }
2164
+
1919
2165
  const cmdPath = join(commandsDir, `${cmdName}.md`);
1920
2166
 
1921
2167
  let content;
@@ -1938,8 +2184,8 @@ export async function runInit(options = {}) {
1938
2184
  }
1939
2185
  }
1940
2186
 
1941
- // Create backup if overwriting existing file
1942
- if (existsSync(cmdPath) && createBackups) {
2187
+ // Create backup if overwriting existing file (respects smart merge decisions)
2188
+ if (existsSync(cmdPath) && shouldBackupCommand(cmdName)) {
1943
2189
  const backupPath = createBackup(cmdPath);
1944
2190
  if (backupPath) {
1945
2191
  backedUpFiles.push({ original: cmdPath, backup: backupPath });
@@ -1960,6 +2206,45 @@ export async function runInit(options = {}) {
1960
2206
  console.log(chalk.cyan(`\n 📁 Backed up ${backedUpFiles.length} file(s) to .claude/backups/`));
1961
2207
  }
1962
2208
 
2209
+ // Step 6b: Deploy feature-specific hooks
2210
+ const deployedHooks = [];
2211
+ const failedHooks = [];
2212
+
2213
+ if (featureHooks.length > 0) {
2214
+ console.log(chalk.bold('\nStep 6b: Deploying feature hooks\n'));
2215
+
2216
+ for (const hookName of featureHooks) {
2217
+ try {
2218
+ const hookPath = join(hooksDir, `${hookName}.js`);
2219
+
2220
+ // Skip if already exists
2221
+ if (existsSync(hookPath)) {
2222
+ console.log(chalk.blue(` ○ hooks/${hookName}.js exists (preserved)`));
2223
+ continue;
2224
+ }
2225
+
2226
+ // Try to load from templates/hooks/ folder
2227
+ const templatePath = join(__dirname, '..', '..', 'templates', 'hooks', `${hookName}.template.js`);
2228
+ if (existsSync(templatePath)) {
2229
+ const hookContent = readFileSync(templatePath, 'utf8');
2230
+ writeFileSync(hookPath, hookContent, 'utf8');
2231
+ deployedHooks.push(hookName);
2232
+ console.log(chalk.green(` ✓ Created hooks/${hookName}.js`));
2233
+ } else {
2234
+ failedHooks.push({ name: hookName, error: 'No template found' });
2235
+ console.log(chalk.yellow(` ⚠ Skipped hooks/${hookName}.js (no template)`));
2236
+ }
2237
+ } catch (error) {
2238
+ failedHooks.push({ name: hookName, error: error.message });
2239
+ console.log(chalk.red(` ✗ Failed: hooks/${hookName}.js - ${error.message}`));
2240
+ }
2241
+ }
2242
+
2243
+ if (deployedHooks.length > 0) {
2244
+ console.log(chalk.green(`\n ✓ Deployed ${deployedHooks.length} feature hook(s)`));
2245
+ }
2246
+ }
2247
+
1963
2248
  // Step 7: Generate INDEX.md
1964
2249
  const indexPath = join(commandsDir, 'INDEX.md');
1965
2250
  const indexContent = generateIndexFile(installed, projectName);
@@ -2087,6 +2372,15 @@ export async function runInit(options = {}) {
2087
2372
  },
2088
2373
  // Track which features need post-install configuration
2089
2374
  _pendingConfiguration: featuresRequiringConfig.map((f) => f.name),
2375
+ // Track what was deployed for verification
2376
+ _deployment: {
2377
+ commands: installed,
2378
+ featureCommands: featureCommands.filter(c => installed.includes(c)),
2379
+ hooks: deployedHooks,
2380
+ featureHooks: featureHooks,
2381
+ enabledFeatures: selectedFeatures,
2382
+ timestamp: new Date().toISOString(),
2383
+ },
2090
2384
  };
2091
2385
 
2092
2386
  if (!existsSync(techStackPath)) {
@@ -243,8 +243,8 @@ export async function runTestSetup(options) {
243
243
  console.log(chalk.dim(` export ${passwordVar}="your_password"`));
244
244
  }
245
245
  } else if (credentialSource === 'config') {
246
- showWarning('Credentials will be stored in .gtask/testing.json');
247
- console.log(chalk.dim('This file will be added to .gitignore'));
246
+ showWarning('Storing credentials in config is not recommended for security.');
247
+ console.log(chalk.dim('Consider using environment variables instead.'));
248
248
  console.log('');
249
249
 
250
250
  const { username, password } = await inquirer.prompt([
@@ -398,9 +398,9 @@ export async function runTestSetup(options) {
398
398
 
399
399
  const testingConfig = createTestingConfig(config);
400
400
 
401
- // Save main config
401
+ // Save main config to tech-stack.json
402
402
  const configPath = saveTestingConfig(testingConfig);
403
- spinner.text = 'Saved testing.json';
403
+ spinner.text = 'Saved testing config to tech-stack.json';
404
404
 
405
405
  // Save testing rules markdown
406
406
  const { generateRules } = await inquirer.prompt([
@@ -415,7 +415,7 @@ export async function runTestSetup(options) {
415
415
  let rulesPath = null;
416
416
  if (generateRules) {
417
417
  rulesPath = saveTestingRules(testingConfig);
418
- spinner.text = 'Saved TESTING_RULES.md';
418
+ spinner.text = 'Saved TESTING_RULES.md to .claude/task-lists/';
419
419
  }
420
420
 
421
421
  spinner.succeed('Configuration files created');
@@ -427,7 +427,8 @@ export async function runTestSetup(options) {
427
427
  `Credentials: ${CREDENTIAL_SOURCES[config.credentialSource].name}`,
428
428
  `Playwright: ${config.playwrightEnabled ? 'Enabled' : 'Disabled'}`,
429
429
  '',
430
- `Config: ${configPath}`,
430
+ `Config saved to: ${configPath}`,
431
+ 'Testing config is now stored in tech-stack.json under the "testing" section.',
431
432
  ];
432
433
 
433
434
  if (rulesPath) {