Skip to content
This repository was archived by the owner on Jun 18, 2026. It is now read-only.

Commit f597e0a

Browse files
author
catlog22
committed
feat: add lite mode for minimal installation of specific commands and skills
1 parent 227244f commit f597e0a

2 files changed

Lines changed: 227 additions & 0 deletions

File tree

ccw/src/cli.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ export async function run(argv: string[]): Promise<void> {
114114
.option('-p, --path <path>', 'Installation path (for Path mode)')
115115
.option('-f, --force', 'Force installation without prompts')
116116
.option('--target <target>', 'Target ecosystem: claude, codex, or all')
117+
.option('--lite', 'Lite mode: install only essential workflow commands and skills')
117118
.option('--skill-hub [skillId]', 'Install skill from skill-hub (use --list to see available)')
118119
.option('--cli <type>', 'Target CLI for skill installation (claude or codex)', 'claude')
119120
.option('--list', 'List available skills in skill-hub')

ccw/src/commands/install.ts

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,32 @@ const EXCLUDED_FILES = ['settings.json', 'settings.local.json', 'CLAUDE.md'];
4141
// CCW marker in CLAUDE.md for tracking ccw-added content
4242
const CCW_REFERENCE_LINE = '- **CCW Instructions**: @~/.claude/CLAUDE.CCW.md';
4343

44+
// Lite mode: minimal installation with specific commands and skills only
45+
const LITE_CLAUDE_COMMANDS = [
46+
'workflow/analyze-with-file.md',
47+
'workflow/debug-with-file.md',
48+
];
49+
50+
const LITE_CLAUDE_SKILLS = [
51+
'workflow-lite-plan',
52+
'workflow-lite-execute',
53+
'team-coordinate',
54+
];
55+
56+
const LITE_CODEX_SKILLS = [
57+
'analyze-with-file',
58+
'debug-with-file',
59+
'workflow-lite-planex',
60+
'team-coordinate',
61+
'csv-wave-pipeline',
62+
];
63+
4464
interface InstallOptions {
4565
mode?: string;
4666
path?: string;
4767
force?: boolean;
4868
target?: string; // 'claude', 'codex', or 'all'
69+
lite?: boolean;
4970
}
5071

5172
interface CopyResult {
@@ -298,6 +319,205 @@ async function backupDirectoryRecursive(
298319
return count;
299320
}
300321

322+
/**
323+
* Lite mode: streamlined installation of specific commands and skills
324+
*/
325+
async function installLite(
326+
sourceDir: string,
327+
installPath: string,
328+
mode: string,
329+
target: string,
330+
version: string
331+
): Promise<void> {
332+
// Build item list for display
333+
const claudeItems = (target === 'claude' || target === 'all')
334+
? [...LITE_CLAUDE_COMMANDS.map(c => `command: ${c}`), ...LITE_CLAUDE_SKILLS.map(s => `skill: ${s}`)]
335+
: [];
336+
const codexItems = (target === 'codex' || target === 'all')
337+
? LITE_CODEX_SKILLS.map(s => `codex skill: ${s}`)
338+
: [];
339+
340+
console.log('');
341+
info('Lite mode — installing minimal workflow set:');
342+
for (const item of [...claudeItems, ...codexItems]) {
343+
console.log(chalk.gray(` • ${item}`));
344+
}
345+
console.log('');
346+
347+
const manifest = createManifest(mode, installPath);
348+
const spinner = createSpinner('Installing lite mode files...').start();
349+
350+
try {
351+
// Install specific commands and skills
352+
const { totalFiles, totalDirs } = await installLiteModeItems(
353+
sourceDir, installPath, mode, target, manifest, spinner
354+
);
355+
356+
let files = totalFiles;
357+
let dirs = totalDirs;
358+
359+
// Install CLAUDE.CCW.md to global
360+
const srcClaudeCcwMd = join(sourceDir, '.claude', 'CLAUDE.CCW.md');
361+
if (existsSync(srcClaudeCcwMd)) {
362+
const globalClaudeDir = join(homedir(), '.claude');
363+
if (!existsSync(globalClaudeDir)) {
364+
mkdirSync(globalClaudeDir, { recursive: true });
365+
}
366+
const destFile = join(globalClaudeDir, 'CLAUDE.CCW.md');
367+
spinner.text = 'Installing CLAUDE.CCW.md to global...';
368+
copyFileSync(srcClaudeCcwMd, destFile);
369+
addFileEntry(manifest, destFile);
370+
files++;
371+
}
372+
373+
// Handle CLAUDE.md reference
374+
const shouldHandleClaudeMd = target === 'claude' || target === 'all';
375+
const claudeMdPath = join(installPath, '.claude', 'CLAUDE.md');
376+
const minimalClaudeMd = '# Project Instructions\n\n- **CCW Instructions**: @~/.claude/CLAUDE.CCW.md\n';
377+
378+
if (shouldHandleClaudeMd) {
379+
if (existsSync(claudeMdPath)) {
380+
const content = readFileSync(claudeMdPath, 'utf8');
381+
if (!content.includes('CLAUDE.CCW.md')) {
382+
// Append reference line
383+
const updated = content.trimEnd() + '\n' + CCW_REFERENCE_LINE + '\n';
384+
writeFileSync(claudeMdPath, updated, 'utf8');
385+
}
386+
} else {
387+
const claudeDir = join(installPath, '.claude');
388+
if (!existsSync(claudeDir)) {
389+
mkdirSync(claudeDir, { recursive: true });
390+
}
391+
writeFileSync(claudeMdPath, minimalClaudeMd, 'utf8');
392+
addFileEntry(manifest, claudeMdPath);
393+
files++;
394+
}
395+
}
396+
397+
// Create version.json
398+
const versionDir = join(installPath, '.claude');
399+
if (!existsSync(versionDir)) mkdirSync(versionDir, { recursive: true });
400+
const versionPath = join(versionDir, 'version.json');
401+
writeFileSync(versionPath, JSON.stringify({
402+
version, installedAt: new Date().toISOString(), mode, installer: 'ccw', lite: true
403+
}, null, 2), 'utf8');
404+
addFileEntry(manifest, versionPath);
405+
files++;
406+
407+
// Initialize CLI tools config
408+
spinner.text = 'Initializing CLI tools configuration...';
409+
const cliToolsInitResult = initCliToolsConfig();
410+
if (cliToolsInitResult.created.length > 0) {
411+
files += cliToolsInitResult.created.length;
412+
}
413+
414+
spinner.succeed('Lite mode installation complete!');
415+
416+
// Save manifest
417+
const manifestPath = saveManifest(manifest);
418+
419+
// Show summary
420+
console.log('');
421+
summaryBox({
422+
title: ' Lite Install Summary ',
423+
lines: [
424+
chalk.green.bold('✓ Lite Installation Successful'),
425+
'',
426+
chalk.white(`Mode: ${chalk.cyan(mode)}`),
427+
chalk.white(`Path: ${chalk.cyan(installPath)}`),
428+
chalk.white(`Target: ${chalk.cyan(target)}`),
429+
chalk.white(`Version: ${chalk.cyan(version)}`),
430+
'',
431+
chalk.gray(`Files installed: ${files}`),
432+
chalk.gray(`Directories created: ${dirs}`),
433+
...(cliToolsInitResult.created.length > 0
434+
? [chalk.gray(`CLI config initialized: ${cliToolsInitResult.created.join(', ')}`)]
435+
: []),
436+
'',
437+
chalk.gray(`Manifest: ${basename(manifestPath)}`),
438+
],
439+
borderColor: 'green'
440+
});
441+
442+
console.log('');
443+
info('Next steps:');
444+
console.log(chalk.gray(' 1. Restart Claude Code or your IDE'));
445+
console.log(chalk.gray(' 2. Try: /workflow-lite-plan or /team-coordinate'));
446+
console.log(chalk.gray(' 3. Run: ccw install (without --lite) for full installation'));
447+
console.log('');
448+
449+
} catch (err) {
450+
spinner.fail('Lite mode installation failed');
451+
error((err as Error).message);
452+
process.exit(1);
453+
}
454+
}
455+
456+
/**
457+
* Install lite mode items: specific commands and skills only
458+
*/
459+
async function installLiteModeItems(
460+
sourceDir: string,
461+
installPath: string,
462+
mode: string,
463+
target: string,
464+
manifest: any,
465+
spinner: Ora
466+
): Promise<{ totalFiles: number; totalDirs: number }> {
467+
let totalFiles = 0;
468+
let totalDirs = 0;
469+
470+
// Install Claude commands and skills
471+
if ((target === 'claude' || target === 'all') && existsSync(join(sourceDir, '.claude'))) {
472+
// Commands
473+
for (const cmd of LITE_CLAUDE_COMMANDS) {
474+
const srcFile = join(sourceDir, '.claude', 'commands', cmd);
475+
if (!existsSync(srcFile)) continue;
476+
477+
const destFile = join(installPath, '.claude', 'commands', cmd);
478+
const destDir = dirname(destFile);
479+
if (!existsSync(destDir)) {
480+
mkdirSync(destDir, { recursive: true });
481+
totalDirs++;
482+
addDirectoryEntry(manifest, destDir);
483+
}
484+
485+
spinner.text = `Installing command: ${cmd}`;
486+
copyFileSync(srcFile, destFile);
487+
addFileEntry(manifest, destFile);
488+
totalFiles++;
489+
}
490+
491+
// Skills
492+
for (const skill of LITE_CLAUDE_SKILLS) {
493+
const srcSkill = join(sourceDir, '.claude', 'skills', skill);
494+
if (!existsSync(srcSkill)) continue;
495+
496+
const destSkill = join(installPath, '.claude', 'skills', skill);
497+
spinner.text = `Installing skill: ${skill}`;
498+
const result = await copyDirectory(srcSkill, destSkill, manifest);
499+
totalFiles += result.files;
500+
totalDirs += result.directories;
501+
}
502+
}
503+
504+
// Install Codex skills
505+
if ((target === 'codex' || target === 'all') && existsSync(join(sourceDir, '.codex'))) {
506+
for (const skill of LITE_CODEX_SKILLS) {
507+
const srcSkill = join(sourceDir, '.codex', 'skills', skill);
508+
if (!existsSync(srcSkill)) continue;
509+
510+
const destSkill = join(installPath, '.codex', 'skills', skill);
511+
spinner.text = `Installing codex skill: ${skill}`;
512+
const result = await copyDirectory(srcSkill, destSkill, manifest);
513+
totalFiles += result.files;
514+
totalDirs += result.directories;
515+
}
516+
}
517+
518+
return { totalFiles, totalDirs };
519+
}
520+
301521
// Get package root directory (ccw/src/commands -> ccw)
302522
function getPackageRoot(): string {
303523
return join(__dirname, '..', '..');
@@ -414,6 +634,12 @@ export async function installCommand(options: InstallOptions): Promise<void> {
414634
process.exit(1);
415635
}
416636

637+
// Lite mode: install only specific commands and skills, then return
638+
if (options.lite) {
639+
await installLite(sourceDir, installPath, mode, target, version);
640+
return;
641+
}
642+
417643
console.log('');
418644
info(`Found ${availableDirs.length} directories to install: ${availableDirs.join(', ')}`);
419645

0 commit comments

Comments
 (0)