mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-15 10:20:39 +00:00
fix: address PR #373 review feedback
- postuninstall.js: add file logging for debugging on error - profile-hook-injector.ts: use 'wx' flag for atomic marker creation - profile-hook-injector.ts: include parse error message in debug log - install-command.ts: use actual counts for consistent semantics - Windows tests: align Section 7 with per-profile hook architecture
This commit is contained in:
@@ -34,8 +34,14 @@ function cleanupCcsFiles() {
|
||||
// Note: Do NOT touch ~/.claude/settings.json
|
||||
// Per-profile hooks in ~/.ccs/*.settings.json will be cleaned up
|
||||
// when the user removes ~/.ccs/ directory.
|
||||
} catch {
|
||||
// Silent fail - not critical
|
||||
} catch (err) {
|
||||
// Silent fail - not critical, but log for debugging
|
||||
try {
|
||||
const logPath = path.join(CCS_DIR, 'uninstall.log');
|
||||
fs.appendFileSync(logPath, `${new Date().toISOString()}: ${err.message}\n`);
|
||||
} catch {
|
||||
// Ignore logging failures
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,15 +39,13 @@ export async function handleUninstallCommand(): Promise<void> {
|
||||
const hookRemoved = uninstallWebSearchHook();
|
||||
if (hookRemoved) {
|
||||
console.log(ok('Removed WebSearch hook'));
|
||||
removed++;
|
||||
removed += 1; // Count as 1 item (the hook file)
|
||||
}
|
||||
|
||||
// 2. Remove symlinks from ~/.claude/
|
||||
const symlinkManager = new ClaudeSymlinkManager();
|
||||
const symlinksRemoved = symlinkManager.uninstall();
|
||||
if (symlinksRemoved > 0) {
|
||||
removed++;
|
||||
}
|
||||
removed += symlinksRemoved; // Add actual count of symlinks removed
|
||||
|
||||
// 3. Summary
|
||||
console.log('');
|
||||
|
||||
@@ -63,8 +63,8 @@ function migrateGlobalHook(): void {
|
||||
if (!fs.existsSync(ccsDir)) {
|
||||
fs.mkdirSync(ccsDir, { recursive: true, mode: 0o700 });
|
||||
}
|
||||
// Create marker file
|
||||
fs.writeFileSync(markerPath, new Date().toISOString(), 'utf8');
|
||||
// Create marker file atomically (wx = fail if exists, prevents race condition)
|
||||
fs.writeFileSync(markerPath, new Date().toISOString(), { encoding: 'utf8', flag: 'wx' });
|
||||
} catch (error) {
|
||||
if (process.env.CCS_DEBUG) {
|
||||
console.error(warn(`Migration failed: ${(error as Error).message}`));
|
||||
@@ -114,9 +114,11 @@ export function ensureProfileHooks(profileName: string): boolean {
|
||||
try {
|
||||
const content = fs.readFileSync(settingsPath, 'utf8');
|
||||
settings = JSON.parse(content);
|
||||
} catch {
|
||||
} catch (parseError) {
|
||||
if (process.env.CCS_DEBUG) {
|
||||
console.error(warn(`Malformed ${profileName}.settings.json - creating fresh hooks`));
|
||||
console.error(
|
||||
warn(`Malformed ${profileName}.settings.json: ${(parseError as Error).message}`)
|
||||
);
|
||||
}
|
||||
// Continue with empty settings, will add hooks
|
||||
}
|
||||
|
||||
@@ -428,42 +428,15 @@ Test-Case "Edge case: Missing parent directories" "Handles missing .claude direc
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# TEST SECTION 7: SETTINGS.JSON HOOK CLEANUP
|
||||
# TEST SECTION 7: PER-PROFILE HOOK BEHAVIOR (Global settings untouched)
|
||||
# ============================================================================
|
||||
|
||||
Write-Host ""
|
||||
Write-ColorOutput "========================================" "Yellow"
|
||||
Write-ColorOutput "SECTION 7: SETTINGS.JSON HOOK CLEANUP" "Yellow"
|
||||
Write-ColorOutput "SECTION 7: PER-PROFILE HOOK BEHAVIOR" "Yellow"
|
||||
Write-ColorOutput "========================================" "Yellow"
|
||||
|
||||
Test-Case "Hook cleanup: Removes CCS WebSearch hook from settings.json" "Hook removed" {
|
||||
# Setup: Create settings.json with CCS hook
|
||||
New-Item -ItemType Directory -Path $TestClaudeDir -Force | Out-Null
|
||||
$settingsPath = Join-Path $TestClaudeDir "settings.json"
|
||||
$settingsContent = @'
|
||||
{
|
||||
"hooks": {
|
||||
"PreToolUse": [
|
||||
{ "matcher": "WebSearch", "hooks": [{ "command": "node ~/.ccs/hooks/websearch-transformer.cjs" }] }
|
||||
]
|
||||
}
|
||||
}
|
||||
'@
|
||||
Set-Content -Path $settingsPath -Value $settingsContent
|
||||
|
||||
# Install then uninstall
|
||||
$env:HOME = $TestHome
|
||||
try {
|
||||
& $CcsPath --install | Out-Null
|
||||
& $CcsPath --uninstall | Out-Null
|
||||
} catch { }
|
||||
|
||||
# Check hook removed
|
||||
$content = Get-Content -Path $settingsPath -Raw
|
||||
-not ($content -match "websearch-transformer")
|
||||
}
|
||||
|
||||
Test-Case "Hook cleanup: Preserves user-defined WebSearch hooks" "User hooks kept" {
|
||||
Test-Case "Per-profile: Uninstall does NOT touch global settings.json" "Global settings unchanged" {
|
||||
# Setup: Create settings.json with user hook
|
||||
New-Item -ItemType Directory -Path $TestClaudeDir -Force | Out-Null
|
||||
$settingsPath = Join-Path $TestClaudeDir "settings.json"
|
||||
@@ -484,13 +457,13 @@ Test-Case "Hook cleanup: Preserves user-defined WebSearch hooks" "User hooks kep
|
||||
& $CcsPath --uninstall | Out-Null
|
||||
} catch { }
|
||||
|
||||
# Check user hook preserved
|
||||
# Verify global settings unchanged
|
||||
$content = Get-Content -Path $settingsPath -Raw
|
||||
$content -match "my-custom-hook"
|
||||
}
|
||||
|
||||
Test-Case "Hook cleanup: Handles mixed CCS and user hooks" "Only CCS hook removed" {
|
||||
# Setup: Create settings.json with both CCS and user hooks
|
||||
Test-Case "Per-profile: Uninstall preserves CCS hooks in global settings (not touched)" "Both hooks preserved" {
|
||||
# Setup: Create settings.json with CCS hook (old format - shouldn't be touched)
|
||||
New-Item -ItemType Directory -Path $TestClaudeDir -Force | Out-Null
|
||||
$settingsPath = Join-Path $TestClaudeDir "settings.json"
|
||||
$settingsContent = @'
|
||||
@@ -498,28 +471,41 @@ Test-Case "Hook cleanup: Handles mixed CCS and user hooks" "Only CCS hook remove
|
||||
"hooks": {
|
||||
"PreToolUse": [
|
||||
{ "matcher": "WebSearch", "hooks": [{ "command": "node ~/.ccs/hooks/websearch-transformer.cjs" }] },
|
||||
{ "matcher": "WebSearch", "hooks": [{ "command": "my-custom-hook.sh" }] },
|
||||
{ "matcher": "Bash", "hooks": [{ "command": "bash-hook.sh" }] }
|
||||
{ "matcher": "WebSearch", "hooks": [{ "command": "my-custom-hook.sh" }] }
|
||||
]
|
||||
}
|
||||
}
|
||||
'@
|
||||
Set-Content -Path $settingsPath -Value $settingsContent
|
||||
|
||||
# Uninstall - should NOT modify global settings
|
||||
$env:HOME = $TestHome
|
||||
try {
|
||||
& $CcsPath --uninstall | Out-Null
|
||||
} catch { }
|
||||
|
||||
# Both hooks should still be in global settings (not touched)
|
||||
$content = Get-Content -Path $settingsPath -Raw
|
||||
($content -match "websearch-transformer") -and ($content -match "my-custom-hook")
|
||||
}
|
||||
|
||||
Test-Case "Per-profile: Migration marker cleanup on uninstall" "Marker file removed" {
|
||||
# Setup: Create ~/.ccs/.hook-migrated marker
|
||||
$ccsDir = Join-Path $TestHome ".ccs"
|
||||
New-Item -ItemType Directory -Path $ccsDir -Force | Out-Null
|
||||
Set-Content -Path (Join-Path $ccsDir ".hook-migrated") -Value "2026-01-25T00:00:00.000Z"
|
||||
|
||||
# Uninstall
|
||||
$env:HOME = $TestHome
|
||||
try {
|
||||
& $CcsPath --uninstall | Out-Null
|
||||
} catch { }
|
||||
|
||||
# Check CCS hook removed, user hooks preserved
|
||||
$content = Get-Content -Path $settingsPath -Raw
|
||||
(-not ($content -match "websearch-transformer")) -and
|
||||
($content -match "my-custom-hook") -and
|
||||
($content -match "bash-hook")
|
||||
# Verify marker removed
|
||||
-not (Test-Path (Join-Path $ccsDir ".hook-migrated"))
|
||||
}
|
||||
|
||||
Test-Case "Hook cleanup: Handles missing settings.json" "No error when settings.json missing" {
|
||||
Test-Case "Per-profile: Handles missing settings.json" "No error when settings.json missing" {
|
||||
# Ensure settings.json doesn't exist
|
||||
$settingsPath = Join-Path $TestClaudeDir "settings.json"
|
||||
if (Test-Path $settingsPath) {
|
||||
|
||||
Reference in New Issue
Block a user