From cc5a5716cf2768a4c06eed7513d1afbc22897f33 Mon Sep 17 00:00:00 2001 From: catlog22 Date: Wed, 28 Jan 2026 08:25:59 +0800 Subject: [PATCH] fix(skills): improve robustness of enable/disable operations - Add rollback in moveDirectory when rmSync fails after cpSync - Add transaction rollback in disable/enableSkill when config save fails - Surface config corruption by throwing on JSON parse errors - Add robust JSON error parsing with fallback in frontend - Add loading state and double-click prevention for toggle button --- ccw/src/core/routes/skills-routes.ts | 27 ++++++++---- .../dashboard-js/views/skills-manager.js | 43 ++++++++++++++++--- 2 files changed, 54 insertions(+), 16 deletions(-) diff --git a/ccw/src/core/routes/skills-routes.ts b/ccw/src/core/routes/skills-routes.ts index 45055ad7..d2756730 100644 --- a/ccw/src/core/routes/skills-routes.ts +++ b/ccw/src/core/routes/skills-routes.ts @@ -64,19 +64,28 @@ function getDisabledSkillsConfigPath(location: SkillLocation, projectPath: strin /** * Load disabled skills configuration + * Throws on JSON parse errors to surface config corruption */ function loadDisabledSkillsConfig(location: SkillLocation, projectPath: string): DisabledSkillsConfig { const configPath = getDisabledSkillsConfigPath(location, projectPath); - try { - if (existsSync(configPath)) { - const content = readFileSync(configPath, 'utf8'); - const config = JSON.parse(content); - return { skills: config.skills || {} }; - } - } catch (error) { - console.error(`[Skills] Failed to load disabled skills config: ${error}`); + + if (!existsSync(configPath)) { + return { skills: {} }; + } + + try { + const content = readFileSync(configPath, 'utf8'); + const config = JSON.parse(content); + return { skills: config.skills || {} }; + } catch (error) { + // Throw on JSON parse errors to surface config corruption + if (error instanceof SyntaxError) { + throw new Error(`Config file corrupted: ${configPath}`); + } + // Log and return empty for other errors (permission, etc.) + console.error(`[Skills] Failed to load disabled skills config: ${error}`); + return { skills: {} }; } - return { skills: {} }; } /** diff --git a/ccw/src/templates/dashboard-js/views/skills-manager.js b/ccw/src/templates/dashboard-js/views/skills-manager.js index 65a05eec..c13dd2c7 100644 --- a/ccw/src/templates/dashboard-js/views/skills-manager.js +++ b/ccw/src/templates/dashboard-js/views/skills-manager.js @@ -219,6 +219,7 @@ function renderSkillCard(skill, location, isDisabled = false) { ${location}