From 19648721fc5543559875d559d09b281c2ffe4a2b Mon Sep 17 00:00:00 2001 From: catlog22 Date: Thu, 11 Dec 2025 11:06:15 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=87=8D=E6=9E=84=E5=AE=89=E8=A3=85?= =?UTF-8?q?=E5=92=8C=E5=8D=87=E7=BA=A7=E5=91=BD=E4=BB=A4=E4=BB=A5=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=85=A8=E5=B1=80=E5=AD=90=E7=9B=AE=E5=BD=95=E7=9A=84?= =?UTF-8?q?=E5=AE=89=E8=A3=85=E5=92=8C=E6=8E=92=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CLAUDE.md => .claude/CLAUDE.md | 0 ccw/src/commands/install.js | 30 ++++++++++++++++++++++++++++-- ccw/src/commands/upgrade.js | 33 ++++++++++++++++++++++++++++++--- 3 files changed, 58 insertions(+), 5 deletions(-) rename CLAUDE.md => .claude/CLAUDE.md (100%) diff --git a/CLAUDE.md b/.claude/CLAUDE.md similarity index 100% rename from CLAUDE.md rename to .claude/CLAUDE.md diff --git a/ccw/src/commands/install.js b/ccw/src/commands/install.js index f92626d3..826595ff 100644 --- a/ccw/src/commands/install.js +++ b/ccw/src/commands/install.js @@ -14,6 +14,9 @@ const __dirname = dirname(__filename); // Source directories to install const SOURCE_DIRS = ['.claude', '.codex', '.gemini', '.qwen']; +// Subdirectories that should always be installed to global (~/.claude/) +const GLOBAL_SUBDIRS = ['workflows', 'scripts', 'templates']; + // Get package root directory (ccw/src/commands -> ccw) function getPackageRoot() { return join(__dirname, '..', '..'); @@ -122,13 +125,30 @@ export async function installCommand(options) { let totalDirs = 0; try { + // For Path mode, install workflows to global first + if (mode === 'Path') { + const globalPath = homedir(); + for (const subdir of GLOBAL_SUBDIRS) { + const srcWorkflows = join(sourceDir, '.claude', subdir); + if (existsSync(srcWorkflows)) { + const destWorkflows = join(globalPath, '.claude', subdir); + spinner.text = `Installing ${subdir} to global...`; + const { files, directories } = await copyDirectory(srcWorkflows, destWorkflows, manifest); + totalFiles += files; + totalDirs += directories; + } + } + } + for (const dir of availableDirs) { const srcPath = join(sourceDir, dir); const destPath = join(installPath, dir); spinner.text = `Installing ${dir}...`; - const { files, directories } = await copyDirectory(srcPath, destPath, manifest); + // For Path mode on .claude, exclude global subdirs (they're already installed to global) + const excludeDirs = (mode === 'Path' && dir === '.claude') ? GLOBAL_SUBDIRS : []; + const { files, directories } = await copyDirectory(srcPath, destPath, manifest, excludeDirs); totalFiles += files; totalDirs += directories; } @@ -265,9 +285,10 @@ async function createBackup(installPath, manifest) { * @param {string} src - Source directory * @param {string} dest - Destination directory * @param {Object} manifest - Manifest to track files (optional) + * @param {string[]} excludeDirs - Directory names to exclude (optional) * @returns {Object} - Count of files and directories */ -async function copyDirectory(src, dest, manifest = null) { +async function copyDirectory(src, dest, manifest = null, excludeDirs = []) { let files = 0; let directories = 0; @@ -281,6 +302,11 @@ async function copyDirectory(src, dest, manifest = null) { const entries = readdirSync(src); for (const entry of entries) { + // Skip excluded directories + if (excludeDirs.includes(entry)) { + continue; + } + const srcPath = join(src, entry); const destPath = join(dest, entry); const stat = statSync(srcPath); diff --git a/ccw/src/commands/upgrade.js b/ccw/src/commands/upgrade.js index fc11fbb3..fffa8d99 100644 --- a/ccw/src/commands/upgrade.js +++ b/ccw/src/commands/upgrade.js @@ -1,5 +1,6 @@ import { existsSync, readdirSync, statSync, copyFileSync, readFileSync, writeFileSync, mkdirSync } from 'fs'; import { join, dirname, basename } from 'path'; +import { homedir } from 'os'; import { fileURLToPath } from 'url'; import inquirer from 'inquirer'; import chalk from 'chalk'; @@ -12,6 +13,9 @@ const __dirname = dirname(__filename); // Source directories to install const SOURCE_DIRS = ['.claude', '.codex', '.gemini', '.qwen']; +// Subdirectories that should always be installed to global (~/.claude/) +const GLOBAL_SUBDIRS = ['workflows', 'scripts', 'templates']; + // Get package root directory (ccw/src/commands -> ccw) function getPackageRoot() { return join(__dirname, '..', '..'); @@ -210,6 +214,7 @@ export async function upgradeCommand(options) { */ async function performUpgrade(manifest, sourceDir, version) { const installPath = manifest.installation_path; + const mode = manifest.installation_mode; // Get available source directories const availableDirs = SOURCE_DIRS.filter(dir => existsSync(join(sourceDir, dir))); @@ -219,17 +224,33 @@ async function performUpgrade(manifest, sourceDir, version) { } // Create new manifest - const newManifest = createManifest(manifest.installation_mode, installPath); + const newManifest = createManifest(mode, installPath); let totalFiles = 0; let totalDirs = 0; + // For Path mode, upgrade workflows to global first + if (mode === 'Path') { + const globalPath = homedir(); + for (const subdir of GLOBAL_SUBDIRS) { + const srcWorkflows = join(sourceDir, '.claude', subdir); + if (existsSync(srcWorkflows)) { + const destWorkflows = join(globalPath, '.claude', subdir); + const { files, directories } = await copyDirectory(srcWorkflows, destWorkflows, newManifest); + totalFiles += files; + totalDirs += directories; + } + } + } + // Copy each directory for (const dir of availableDirs) { const srcPath = join(sourceDir, dir); const destPath = join(installPath, dir); - const { files, directories } = await copyDirectory(srcPath, destPath, newManifest); + // For Path mode on .claude, exclude global subdirs (they're already installed to global) + const excludeDirs = (mode === 'Path' && dir === '.claude') ? GLOBAL_SUBDIRS : []; + const { files, directories } = await copyDirectory(srcPath, destPath, newManifest, excludeDirs); totalFiles += files; totalDirs += directories; } @@ -263,9 +284,10 @@ async function performUpgrade(manifest, sourceDir, version) { * @param {string} src - Source directory * @param {string} dest - Destination directory * @param {Object} manifest - Manifest to track files + * @param {string[]} excludeDirs - Directory names to exclude (optional) * @returns {Object} - Count of files and directories */ -async function copyDirectory(src, dest, manifest) { +async function copyDirectory(src, dest, manifest, excludeDirs = []) { let files = 0; let directories = 0; @@ -279,6 +301,11 @@ async function copyDirectory(src, dest, manifest) { const entries = readdirSync(src); for (const entry of entries) { + // Skip excluded directories + if (excludeDirs.includes(entry)) { + continue; + } + const srcPath = join(src, entry); const destPath = join(dest, entry); const stat = statSync(srcPath);