fix(.claude/): resolve npm install failure for claude-dir-installer

Fixes issue where npm install -g or ccs update would fail to copy
.claude/ directory to ~/.ccs/.claude/, causing symlink installation
errors. ClaudeDirInstaller now ensures source exists before
ClaudeSymlinkManager attempts linking.
This commit is contained in:
kaitranntt
2025-11-16 06:48:39 -05:00
parent fb3deeff38
commit d8645c71e4
9 changed files with 125 additions and 6 deletions
+1 -1
View File
@@ -1 +1 @@
4.1.0
4.1.1
+6
View File
@@ -255,6 +255,12 @@ async function handleDoctorCommand() {
}
async function handleUpdateCommand() {
// First, copy .claude/ directory from package to ~/.ccs/.claude/
const ClaudeDirInstaller = require('./utils/claude-dir-installer');
const installer = new ClaudeDirInstaller();
installer.install();
// Then, create symlinks from ~/.ccs/.claude/ to ~/.claude/
const ClaudeSymlinkManager = require('./utils/claude-symlink-manager');
const manager = new ClaudeSymlinkManager();
+102
View File
@@ -0,0 +1,102 @@
#!/usr/bin/env node
'use strict';
const fs = require('fs');
const path = require('path');
const os = require('os');
/**
* ClaudeDirInstaller - Manages copying .claude/ directory from package to ~/.ccs/.claude/
* v4.1.1: Fix for npm install not copying .claude/ directory
*/
class ClaudeDirInstaller {
constructor() {
this.homeDir = os.homedir();
this.ccsClaudeDir = path.join(this.homeDir, '.ccs', '.claude');
}
/**
* Copy .claude/ directory from package to ~/.ccs/.claude/
* @param {string} packageDir - Package installation directory (default: auto-detect)
*/
install(packageDir) {
try {
// Auto-detect package directory if not provided
if (!packageDir) {
// Try to find package root by going up from this file
packageDir = path.join(__dirname, '..', '..');
}
const packageClaudeDir = path.join(packageDir, '.claude');
if (!fs.existsSync(packageClaudeDir)) {
console.log('[!] Package .claude/ directory not found');
console.log(` Searched in: ${packageClaudeDir}`);
console.log(' This may be a development installation');
return false;
}
console.log('[i] Installing CCS .claude/ items...');
// Remove old version before copying new one
if (fs.existsSync(this.ccsClaudeDir)) {
fs.rmSync(this.ccsClaudeDir, { recursive: true, force: true });
}
// Use fs.cpSync for recursive copy (Node.js 16.7.0+)
// Fallback to manual copy for older Node.js versions
if (fs.cpSync) {
fs.cpSync(packageClaudeDir, this.ccsClaudeDir, { recursive: true });
} else {
// Fallback for Node.js < 16.7.0
this._copyDirRecursive(packageClaudeDir, this.ccsClaudeDir);
}
console.log('[OK] Copied .claude/ items to ~/.ccs/.claude/');
return true;
} catch (err) {
console.warn('[!] Failed to copy .claude/ directory:', err.message);
console.warn(' CCS items may not be available');
return false;
}
}
/**
* Recursively copy directory (fallback for Node.js < 16.7.0)
* @param {string} src - Source directory
* @param {string} dest - Destination directory
* @private
*/
_copyDirRecursive(src, dest) {
// Create destination directory
if (!fs.existsSync(dest)) {
fs.mkdirSync(dest, { recursive: true });
}
// Read source directory
const entries = fs.readdirSync(src, { withFileTypes: true });
for (const entry of entries) {
const srcPath = path.join(src, entry.name);
const destPath = path.join(dest, entry.name);
if (entry.isDirectory()) {
// Recursively copy subdirectory
this._copyDirRecursive(srcPath, destPath);
} else {
// Copy file
fs.copyFileSync(srcPath, destPath);
}
}
}
/**
* Check if ~/.ccs/.claude/ exists and is valid
* @returns {boolean} True if directory exists
*/
isInstalled() {
return fs.existsSync(this.ccsClaudeDir);
}
}
module.exports = ClaudeDirInstaller;
+1 -1
View File
@@ -31,7 +31,7 @@ $InstallMethod = if ($ScriptDir -and ((Test-Path "$ScriptDir\lib\ccs.ps1") -or (
# IMPORTANT: Update this version when releasing new versions!
# This hardcoded version is used for standalone installations (irm | iex)
# For git installations, VERSION file is read if available
$CcsVersion = "4.1.0"
$CcsVersion = "4.1.1"
# Try to read VERSION file for git installations
if ($ScriptDir) {
+1 -1
View File
@@ -32,7 +32,7 @@ fi
# IMPORTANT: Update this version when releasing new versions!
# This hardcoded version is used for standalone installations (curl | bash)
# For git installations, VERSION file is read if available
CCS_VERSION="4.1.0"
CCS_VERSION="4.1.1"
# Try to read VERSION file for git installations
if [[ -f "$SCRIPT_DIR/VERSION" ]]; then
+1 -1
View File
@@ -2,7 +2,7 @@
set -euo pipefail
# Version (updated by scripts/bump-version.sh)
CCS_VERSION="4.1.0"
CCS_VERSION="4.1.1"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly CONFIG_FILE="${CCS_CONFIG:-$HOME/.ccs/config.json}"
readonly PROFILES_JSON="$HOME/.ccs/profiles.json"
+1 -1
View File
@@ -12,7 +12,7 @@ param(
$ErrorActionPreference = "Stop"
# Version (updated by scripts/bump-version.sh)
$CcsVersion = "4.1.0"
$CcsVersion = "4.1.1"
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$ConfigFile = if ($env:CCS_CONFIG) { $env:CCS_CONFIG } else { "$env:USERPROFILE\.ccs\config.json" }
$ProfilesJson = "$env:USERPROFILE\.ccs\profiles.json"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@kaitranntt/ccs",
"version": "4.1.0",
"version": "4.1.1",
"description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6",
"keywords": [
"cli",
+11
View File
@@ -103,6 +103,17 @@ function createConfigFiles() {
}
console.log('');
// Copy .claude/ directory from package to ~/.ccs/.claude/ (v4.1.1)
try {
const ClaudeDirInstaller = require('../bin/utils/claude-dir-installer');
const installer = new ClaudeDirInstaller();
const packageDir = path.join(__dirname, '..');
installer.install(packageDir);
} catch (err) {
console.warn('[!] Failed to install .claude/ directory:', err.message);
console.warn(' CCS items may not be available');
}
// Install CCS items to ~/.claude/ (v4.1.0)
try {
const ClaudeSymlinkManager = require('../bin/utils/claude-symlink-manager');