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

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/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  ║ ║ ║ ╠═╣║ ║ ║║║╣ ╠═╣ ║║╚╗╔╝╠═╣║║║║ ║╣ ║║ ╚═╗ ║ ╠═╣╠╦╝ ║ ║╣ ╠╦╝ ║
10
10
  ║ ╚═╝╩═╝╩ ╩╚═╝═╩╝╚═╝ ╩ ╩═╩╝ ╚╝ ╩ ╩╝╚╝╚═╝╚═╝═╩╝ ╚═╝ ╩ ╩ ╩╩╚═ ╩ ╚═╝╩╚═ ║
11
11
  ║ ║
12
- ║ v1.8.0 • Production Ready ║
12
+ ║ v1.8.3 • Production Ready ║
13
13
  ║ ║
14
14
  ╚═══════════════════════════════════════════════════════════════════════════════╝
15
15
  ```
@@ -65,7 +65,11 @@ CCASP is a **two-phase toolkit** that extends Claude Code CLI capabilities:
65
65
 
66
66
  ---
67
67
 
68
- ## What's New in v1.8.0
68
+ ## What's New in v1.8.3
69
+
70
+ **Latest updates:**
71
+ - **`ccasp init --dev`**: Dev mode for rapid template testing - reuses existing tech-stack.json, processes all templates, preserves custom commands
72
+ - **`npm-deploy.js --auto`**: Auto mode for CI/CD - skips confirmation prompts for automation
69
73
 
70
74
  **60+ components** across 8 implementation phases:
71
75
 
package/bin/gtask.js CHANGED
@@ -36,6 +36,8 @@ import { runInstallScripts } from '../src/commands/install-scripts.js';
36
36
  import { runPanel, launchPanel } from '../src/commands/panel.js';
37
37
  import { runInstallPanelHook } from '../src/commands/install-panel-hook.js';
38
38
  import { runUninstall } from '../src/commands/uninstall.js';
39
+ import { runGlobalUninstall } from '../src/commands/global-uninstall.js';
40
+ import { runGlobalReinstall } from '../src/commands/global-reinstall.js';
39
41
  import { getVersion, checkPrerequisites } from '../src/utils.js';
40
42
 
41
43
  program
@@ -48,6 +50,8 @@ program
48
50
  .command('init')
49
51
  .description('Deploy Claude CLI Advanced Starter Pack to current project')
50
52
  .option('--force', 'Overwrite existing commands')
53
+ .option('--no-register', 'Do not register project in global registry')
54
+ .option('--dev', 'Development mode: reuse existing tech-stack.json, process templates, skip prompts')
51
55
  .action(async (options) => {
52
56
  await runInit(options);
53
57
  });
@@ -62,6 +66,26 @@ program
62
66
  await runUninstall(options);
63
67
  });
64
68
 
69
+ // Global uninstall command - remove CCASP from ALL projects
70
+ program
71
+ .command('global-uninstall')
72
+ .description('Remove CCASP from all registered projects and clear global config')
73
+ .option('--force', 'Skip confirmation prompts')
74
+ .option('--all', 'Remove entire .claude/ directory from each project')
75
+ .action(async (options) => {
76
+ await runGlobalUninstall(options);
77
+ });
78
+
79
+ // Global reinstall command - reinstall CCASP across all projects
80
+ program
81
+ .command('global-reinstall')
82
+ .description('Reinstall CCASP globally (use --projects to reinstall in all projects)')
83
+ .option('--force', 'Skip confirmation prompts')
84
+ .option('--projects', 'Reinstall CCASP in all registered projects')
85
+ .action(async (options) => {
86
+ await runGlobalReinstall(options);
87
+ });
88
+
65
89
  // Interactive menu (default when no command)
66
90
  program
67
91
  .command('menu', { isDefault: true })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-cli-advanced-starter-pack",
3
- "version": "1.8.3",
3
+ "version": "1.8.5",
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
@@ -20,11 +20,13 @@ import { runCreatePhaseDev, showPhasDevMainMenu } from '../commands/create-phase
20
20
  import { runExploreMcp, showExploreMcpMenu } from '../commands/explore-mcp.js';
21
21
  import { runClaudeAudit, showClaudeAuditMenu } from '../commands/claude-audit.js';
22
22
  import { runRoadmap, showRoadmapMenu } from '../commands/roadmap.js';
23
- import { launchPanel } from '../commands/panel.js';
23
+ import { launchPanel, launchPanelInline } from '../commands/panel.js';
24
24
  import { hasTestingConfig } from '../testing/config.js';
25
25
  import { showHelp } from '../commands/help.js';
26
26
  import { hasValidConfig, getVersion, loadTechStack, saveTechStack } from '../utils.js';
27
27
  import { performVersionCheck, formatUpdateBanner } from '../utils/version-check.js';
28
+ import { isHappyMode, shouldUseMobileUI } from '../utils/happy-detect.js';
29
+ import { showMobileMenu, showMobilePanel, showMobileSettings, mobileReturnPrompt } from './mobile-menu.js';
28
30
 
29
31
  /**
30
32
  * Get bypass permissions status from settings.json
@@ -655,8 +657,15 @@ async function configureHappy(techStack) {
655
657
 
656
658
  /**
657
659
  * Show main interactive menu
660
+ * Automatically detects Happy CLI and switches to mobile-optimized UI
658
661
  */
659
662
  export async function showMainMenu() {
663
+ // Check if we should use mobile UI (Happy CLI detected or happyMode.enabled)
664
+ const techStack = loadTechStack();
665
+ if (shouldUseMobileUI(techStack)) {
666
+ return showMobileMainMenu();
667
+ }
668
+
660
669
  console.clear();
661
670
  console.log(chalk.cyan(BANNER));
662
671
  console.log('');
@@ -1063,3 +1072,164 @@ export function showWarning(message) {
1063
1072
  export function showInfo(message) {
1064
1073
  console.log(chalk.blue(`ℹ ${message}`));
1065
1074
  }
1075
+
1076
+ /**
1077
+ * Mobile-optimized main menu handler
1078
+ * Routes actions from the mobile menu to their handlers
1079
+ */
1080
+ async function showMobileMainMenu() {
1081
+ const action = await showMobileMenu();
1082
+
1083
+ switch (action) {
1084
+ case 'create':
1085
+ const configured = hasValidConfig();
1086
+ if (!configured) {
1087
+ console.log(chalk.yellow('Setup required first.'));
1088
+ const { proceed } = await inquirer.prompt([
1089
+ { type: 'confirm', name: 'proceed', message: 'Run setup?', default: true }
1090
+ ]);
1091
+ if (proceed) await runSetup({});
1092
+ } else {
1093
+ await runCreate({});
1094
+ }
1095
+ break;
1096
+
1097
+ case 'decompose':
1098
+ if (!hasValidConfig()) {
1099
+ console.log(chalk.yellow('Setup required first.'));
1100
+ } else {
1101
+ await runDecompose({});
1102
+ }
1103
+ break;
1104
+
1105
+ case 'sync':
1106
+ if (!hasValidConfig()) {
1107
+ console.log(chalk.yellow('Setup required first.'));
1108
+ } else {
1109
+ await runSync({ subcommand: 'status' });
1110
+ }
1111
+ break;
1112
+
1113
+ case 'setup':
1114
+ await runSetup({});
1115
+ break;
1116
+
1117
+ case 'list':
1118
+ if (!hasValidConfig()) {
1119
+ console.log(chalk.yellow('Setup required first.'));
1120
+ } else {
1121
+ await runList({});
1122
+ }
1123
+ break;
1124
+
1125
+ case 'install':
1126
+ await runInstall({});
1127
+ break;
1128
+
1129
+ case 'panel-inline':
1130
+ // Show panel inline instead of launching new window
1131
+ await showMobilePanelLoop();
1132
+ break;
1133
+
1134
+ case 'test-setup':
1135
+ await runTestSetup({});
1136
+ break;
1137
+
1138
+ case 'agent-creator':
1139
+ await runCreateAgent({});
1140
+ break;
1141
+
1142
+ case 'explore-mcp':
1143
+ await showExploreMcpMenu();
1144
+ break;
1145
+
1146
+ case 'project-settings':
1147
+ await showMobileSettingsLoop();
1148
+ break;
1149
+
1150
+ case 'help':
1151
+ showHelp();
1152
+ break;
1153
+
1154
+ case 'exit':
1155
+ console.log(chalk.dim('Goodbye!'));
1156
+ process.exit(0);
1157
+ }
1158
+
1159
+ // Return to menu unless exiting
1160
+ if (action !== 'exit') {
1161
+ const back = await mobileReturnPrompt();
1162
+ if (back) {
1163
+ await showMobileMainMenu();
1164
+ } else {
1165
+ console.log(chalk.dim('Goodbye!'));
1166
+ process.exit(0);
1167
+ }
1168
+ }
1169
+ }
1170
+
1171
+ /**
1172
+ * Mobile panel loop - inline panel without new window
1173
+ */
1174
+ async function showMobilePanelLoop() {
1175
+ while (true) {
1176
+ const action = await showMobilePanel();
1177
+
1178
+ if (action === 'back') {
1179
+ return;
1180
+ }
1181
+
1182
+ // Copy command to clipboard and show instructions
1183
+ console.log('');
1184
+ console.log(chalk.cyan(`Command: ${action}`));
1185
+ console.log(chalk.dim('Paste in Claude Code'));
1186
+ console.log('');
1187
+
1188
+ // Try to copy to clipboard
1189
+ try {
1190
+ const { copyToClipboard } = await import('../panel/queue.js');
1191
+ if (copyToClipboard(action)) {
1192
+ console.log(chalk.green('✓ Copied to clipboard'));
1193
+ }
1194
+ } catch {
1195
+ // Clipboard not available
1196
+ }
1197
+
1198
+ await inquirer.prompt([
1199
+ { type: 'input', name: 'continue', message: 'Enter to continue...' }
1200
+ ]);
1201
+ }
1202
+ }
1203
+
1204
+ /**
1205
+ * Mobile settings loop
1206
+ */
1207
+ async function showMobileSettingsLoop() {
1208
+ while (true) {
1209
+ const action = await showMobileSettings();
1210
+
1211
+ if (action === 'back') {
1212
+ return;
1213
+ }
1214
+
1215
+ const techStack = loadTechStack();
1216
+
1217
+ switch (action) {
1218
+ case 'github':
1219
+ await configureGitHub(techStack);
1220
+ break;
1221
+ case 'deployment':
1222
+ await configureDeployment(techStack);
1223
+ break;
1224
+ case 'tunnel':
1225
+ await configureTunnel(techStack);
1226
+ break;
1227
+ case 'token':
1228
+ await configureToken(techStack);
1229
+ break;
1230
+ case 'happy':
1231
+ await configureHappy(techStack);
1232
+ break;
1233
+ }
1234
+ }
1235
+ }
@@ -0,0 +1,230 @@
1
+ /**
2
+ * Mobile-Optimized Menu System for Happy CLI
3
+ *
4
+ * Renders menus optimized for mobile screens (max 40 chars).
5
+ * Single-column layout, no overflow, minimal decorations.
6
+ */
7
+
8
+ import chalk from 'chalk';
9
+ import inquirer from 'inquirer';
10
+ import { getVersion, loadTechStack, hasValidConfig } from '../utils.js';
11
+ import { isHappyMode, getMobileWidth } from '../utils/happy-detect.js';
12
+
13
+ // Mobile-friendly banner (40 chars max)
14
+ const MOBILE_BANNER = `
15
+ ${chalk.cyan('╔══════════════════════════════════╗')}
16
+ ${chalk.cyan('║')} ${chalk.bold('CCASP')} ${chalk.dim('v' + getVersion().slice(0, 5))}${' '.repeat(17)}${chalk.cyan('║')}
17
+ ${chalk.cyan('║')} ${chalk.dim('Mobile Menu')}${' '.repeat(21)}${chalk.cyan('║')}
18
+ ${chalk.cyan('╚══════════════════════════════════╝')}`;
19
+
20
+ // Compact panel banner for mobile
21
+ const MOBILE_PANEL_BANNER = `
22
+ ${chalk.cyan('╔══════════════════════════════════╗')}
23
+ ${chalk.cyan('║')} ${chalk.bold('CCASP Panel')}${' '.repeat(20)}${chalk.cyan('║')}
24
+ ${chalk.cyan('╚══════════════════════════════════╝')}`;
25
+
26
+ /**
27
+ * Truncate text to fit mobile width
28
+ * @param {string} text - Text to truncate
29
+ * @param {number} maxLen - Maximum length
30
+ * @returns {string} Truncated text
31
+ */
32
+ function truncate(text, maxLen = 30) {
33
+ if (!text) return '';
34
+ // Strip ANSI codes for length calculation
35
+ const plainText = text.replace(/\x1B\[[0-9;]*m/g, '');
36
+ if (plainText.length <= maxLen) return text;
37
+ return text.slice(0, maxLen - 2) + '..';
38
+ }
39
+
40
+ /**
41
+ * Format a menu item for mobile (single line)
42
+ * @param {string} key - Shortcut key
43
+ * @param {string} label - Item label
44
+ * @returns {string} Formatted menu item
45
+ */
46
+ function formatMobileItem(key, label) {
47
+ const truncLabel = truncate(label, 28);
48
+ return `${chalk.yellow(key + ')')} ${truncLabel}`;
49
+ }
50
+
51
+ /**
52
+ * Show mobile-optimized main menu
53
+ */
54
+ export async function showMobileMenu() {
55
+ console.clear();
56
+ console.log(MOBILE_BANNER);
57
+
58
+ const configured = hasValidConfig();
59
+
60
+ // Status line
61
+ if (configured) {
62
+ console.log(chalk.green(' ✓ Configured'));
63
+ } else {
64
+ console.log(chalk.yellow(' ⚠ Not configured'));
65
+ }
66
+ console.log('');
67
+
68
+ // Single-column menu items
69
+ const choices = [
70
+ { name: formatMobileItem('1', 'Create Task'), value: 'create' },
71
+ { name: formatMobileItem('2', 'Decompose Issue'), value: 'decompose' },
72
+ { name: formatMobileItem('3', 'Sync Tasks'), value: 'sync' },
73
+ new inquirer.Separator(chalk.dim('─'.repeat(34))),
74
+ { name: formatMobileItem('4', 'Setup'), value: 'setup' },
75
+ { name: formatMobileItem('5', 'List Tasks'), value: 'list' },
76
+ { name: formatMobileItem('6', 'Install Command'), value: 'install' },
77
+ new inquirer.Separator(chalk.dim('─'.repeat(34))),
78
+ { name: formatMobileItem('P', 'Panel (inline)'), value: 'panel-inline' },
79
+ { name: formatMobileItem('T', 'Test Setup'), value: 'test-setup' },
80
+ { name: formatMobileItem('A', 'Agent Creator'), value: 'agent-creator' },
81
+ { name: formatMobileItem('M', 'MCP Explorer'), value: 'explore-mcp' },
82
+ new inquirer.Separator(chalk.dim('─'.repeat(34))),
83
+ { name: formatMobileItem('S', 'Settings'), value: 'project-settings' },
84
+ { name: formatMobileItem('?', 'Help'), value: 'help' },
85
+ { name: formatMobileItem('Q', 'Exit'), value: 'exit' },
86
+ ];
87
+
88
+ const { action } = await inquirer.prompt([
89
+ {
90
+ type: 'list',
91
+ name: 'action',
92
+ message: 'Select:',
93
+ choices,
94
+ pageSize: 12,
95
+ },
96
+ ]);
97
+
98
+ return action;
99
+ }
100
+
101
+ /**
102
+ * Show mobile-optimized panel menu (inline, no new window)
103
+ */
104
+ export async function showMobilePanel() {
105
+ console.clear();
106
+ console.log(MOBILE_PANEL_BANNER);
107
+ console.log('');
108
+
109
+ // Single-column panel items
110
+ const choices = [
111
+ new inquirer.Separator(chalk.cyan(' Agents & Skills')),
112
+ { name: formatMobileItem('A', 'Create Agent'), value: '/create-agent' },
113
+ { name: formatMobileItem('H', 'Create Hook'), value: '/create-hook' },
114
+ { name: formatMobileItem('S', 'Create Skill'), value: '/create-skill' },
115
+ { name: formatMobileItem('M', 'Explore MCP'), value: '/explore-mcp' },
116
+ new inquirer.Separator(chalk.dim('─'.repeat(34))),
117
+ new inquirer.Separator(chalk.cyan(' Resources')),
118
+ { name: formatMobileItem('1', 'View Agents'), value: '/view-agents' },
119
+ { name: formatMobileItem('2', 'View Skills'), value: '/view-skills' },
120
+ { name: formatMobileItem('3', 'View Hooks'), value: '/view-hooks' },
121
+ { name: formatMobileItem('4', 'All Commands'), value: '/INDEX' },
122
+ new inquirer.Separator(chalk.dim('─'.repeat(34))),
123
+ new inquirer.Separator(chalk.cyan(' Quick Actions')),
124
+ { name: formatMobileItem('P', 'Phase Dev Plan'), value: '/phase-dev-plan' },
125
+ { name: formatMobileItem('G', 'GitHub Task'), value: '/github-task' },
126
+ { name: formatMobileItem('T', 'Run E2E Tests'), value: '/e2e-test' },
127
+ new inquirer.Separator(chalk.dim('─'.repeat(34))),
128
+ { name: formatMobileItem('B', 'Back'), value: 'back' },
129
+ ];
130
+
131
+ const { action } = await inquirer.prompt([
132
+ {
133
+ type: 'list',
134
+ name: 'action',
135
+ message: 'Select:',
136
+ choices,
137
+ pageSize: 15,
138
+ },
139
+ ]);
140
+
141
+ return action;
142
+ }
143
+
144
+ /**
145
+ * Show mobile-optimized project settings
146
+ */
147
+ export async function showMobileSettings() {
148
+ console.clear();
149
+ console.log(chalk.cyan('╔══════════════════════════════════╗'));
150
+ console.log(chalk.cyan('║') + chalk.bold(' Settings') + ' '.repeat(23) + chalk.cyan('║'));
151
+ console.log(chalk.cyan('╚══════════════════════════════════╝'));
152
+ console.log('');
153
+
154
+ const choices = [
155
+ { name: formatMobileItem('1', 'GitHub Board'), value: 'github' },
156
+ { name: formatMobileItem('2', 'Deployment'), value: 'deployment' },
157
+ { name: formatMobileItem('3', 'Tunnel'), value: 'tunnel' },
158
+ { name: formatMobileItem('4', 'Token Budget'), value: 'token' },
159
+ { name: formatMobileItem('5', 'Happy Mode'), value: 'happy' },
160
+ new inquirer.Separator(chalk.dim('─'.repeat(34))),
161
+ { name: formatMobileItem('B', 'Back'), value: 'back' },
162
+ ];
163
+
164
+ const { action } = await inquirer.prompt([
165
+ {
166
+ type: 'list',
167
+ name: 'action',
168
+ message: 'Select:',
169
+ choices,
170
+ pageSize: 10,
171
+ },
172
+ ]);
173
+
174
+ return action;
175
+ }
176
+
177
+ /**
178
+ * Show a mobile-friendly success message
179
+ * @param {string} message - Success message
180
+ */
181
+ export function showMobileSuccess(message) {
182
+ console.log('');
183
+ console.log(chalk.green(`✓ ${truncate(message, 32)}`));
184
+ console.log('');
185
+ }
186
+
187
+ /**
188
+ * Show a mobile-friendly error message
189
+ * @param {string} message - Error message
190
+ */
191
+ export function showMobileError(message) {
192
+ console.log('');
193
+ console.log(chalk.red(`✗ ${truncate(message, 32)}`));
194
+ console.log('');
195
+ }
196
+
197
+ /**
198
+ * Show a mobile-friendly info box
199
+ * @param {string} title - Box title
200
+ * @param {string[]} lines - Content lines
201
+ */
202
+ export function showMobileBox(title, lines = []) {
203
+ const width = getMobileWidth() - 4; // Account for borders
204
+ console.log('');
205
+ console.log(chalk.cyan('┌' + '─'.repeat(width) + '┐'));
206
+ console.log(chalk.cyan('│') + ` ${truncate(title, width - 2)}`.padEnd(width) + chalk.cyan('│'));
207
+ console.log(chalk.cyan('├' + '─'.repeat(width) + '┤'));
208
+ for (const line of lines) {
209
+ console.log(chalk.cyan('│') + ` ${truncate(line, width - 2)}`.padEnd(width) + chalk.cyan('│'));
210
+ }
211
+ console.log(chalk.cyan('└' + '─'.repeat(width) + '┘'));
212
+ console.log('');
213
+ }
214
+
215
+ /**
216
+ * Prompt to return to menu (mobile version)
217
+ * @returns {Promise<boolean>} True if user wants to return
218
+ */
219
+ export async function mobileReturnPrompt() {
220
+ console.log('');
221
+ const { back } = await inquirer.prompt([
222
+ {
223
+ type: 'confirm',
224
+ name: 'back',
225
+ message: 'Back to menu?',
226
+ default: true,
227
+ },
228
+ ]);
229
+ return back;
230
+ }