From 2e04cf79d73f15d42fe61d0df76941afd3a9a489 Mon Sep 17 00:00:00 2001 From: Val Redchenko Date: Tue, 14 Apr 2026 13:13:22 +0100 Subject: [PATCH] fix: sync claude-code-config with renamed/removed skills and clean broken symlinks The skills config in core/claude-code-config.json was never updated after commit 151c4fe renamed and removed several skills. This caused `smartem-workspace check --fix` to report missing skills and fail to create symlinks for targets that no longer exist. - Rename database-admin -> database - Rename technical-writer -> tech-writer - Rename playwright-skill -> playwright - Remove github (merged into git) - Remove ascii-art (deleted) - Sync bundled copy in packages/smartem-workspace/ Also enhance check.py to detect and remove stale broken symlinks under .claude/skills/ during --fix. The cleanup is constrained to entries that are (1) symlinks, (2) whose target does not exist, and (3) whose name is not in the expected skills list -- so user-created skills and valid symlinks are never touched. Bump smartem-workspace version 0.6.0 -> 0.6.1. --- core/claude-code-config.json | 8 ++--- packages/smartem-workspace/pyproject.toml | 2 +- .../smartem_workspace/__init__.py | 2 +- .../smartem_workspace/commands/check.py | 30 +++++++++++++++++++ .../config/claude-code-config.json | 8 ++--- packages/smartem-workspace/uv.lock | 2 +- 6 files changed, 39 insertions(+), 13 deletions(-) diff --git a/core/claude-code-config.json b/core/claude-code-config.json index e624358..983d1b0 100644 --- a/core/claude-code-config.json +++ b/core/claude-code-config.json @@ -4,13 +4,11 @@ "description": "Claude Code integration configuration for SmartEM workspace", "claudeConfig": { "skills": [ - { "name": "database-admin", "path": "claude-code/shared/skills/database-admin" }, + { "name": "database", "path": "claude-code/shared/skills/database" }, { "name": "devops", "path": "claude-code/shared/skills/devops" }, - { "name": "technical-writer", "path": "claude-code/shared/skills/technical-writer" }, + { "name": "tech-writer", "path": "claude-code/shared/skills/tech-writer" }, { "name": "git", "path": "claude-code/shared/skills/git" }, - { "name": "github", "path": "claude-code/shared/skills/github" }, - { "name": "ascii-art", "path": "claude-code/shared/skills/ascii-art" }, - { "name": "playwright-skill", "path": "claude-code/smartem-frontend/skills/playwright-skill" } + { "name": "playwright", "path": "claude-code/smartem-frontend/skills/playwright" } ], "defaultPermissions": { "allow": [ diff --git a/packages/smartem-workspace/pyproject.toml b/packages/smartem-workspace/pyproject.toml index f9b4402..0de72a1 100644 --- a/packages/smartem-workspace/pyproject.toml +++ b/packages/smartem-workspace/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "smartem-workspace" -version = "0.6.0" +version = "0.6.1" description = "CLI tool to automate SmartEM multi-repo workspace setup" readme = "README.md" license = "Apache-2.0" diff --git a/packages/smartem-workspace/smartem_workspace/__init__.py b/packages/smartem-workspace/smartem_workspace/__init__.py index 0c99949..3f79b83 100644 --- a/packages/smartem-workspace/smartem_workspace/__init__.py +++ b/packages/smartem-workspace/smartem_workspace/__init__.py @@ -1,3 +1,3 @@ """SmartEM workspace setup CLI tool.""" -__version__ = "0.6.0" +__version__ = "0.6.1" diff --git a/packages/smartem-workspace/smartem_workspace/commands/check.py b/packages/smartem-workspace/smartem_workspace/commands/check.py index 747b73c..8cfe6f3 100644 --- a/packages/smartem-workspace/smartem_workspace/commands/check.py +++ b/packages/smartem-workspace/smartem_workspace/commands/check.py @@ -276,11 +276,27 @@ def run_claude_checks(workspace_path: Path, claude_config: ClaudeCodeConfig) -> else: results.append(CheckResult(".claude/skills directory", "ok", "Present")) + expected_skill_names = {skill.name for skill in claude_config.claudeConfig.skills} for skill in claude_config.claudeConfig.skills: skill_link = skills_dir / skill.name skill_target = devtools_path / skill.path results.append(check_symlink(skill_link, skill_target, f"skill: {skill.name}")) + if skills_dir.exists(): + for entry in skills_dir.iterdir(): + if entry.name in expected_skill_names: + continue + if entry.is_symlink() and not entry.exists(): + results.append( + CheckResult( + f"skill: {entry.name}", + "warning", + "Broken symlink (stale)", + fixable=True, + fix_data={"remove_broken_symlink": str(entry)}, + ) + ) + settings_path = workspace_path / ".claude" / "settings.local.json" if settings_path.exists(): results.append(check_json_valid(settings_path, "settings.local.json")) @@ -407,6 +423,20 @@ def apply_fixes(workspace_path: Path, reports: list[CheckReport]) -> tuple[int, console.print(f" [red]Failed to create directory: {e}[/red]") failed += 1 + elif "remove_broken_symlink" in fix_data: + broken_path = Path(fix_data["remove_broken_symlink"]) + try: + if broken_path.is_symlink() and not broken_path.exists(): + broken_path.unlink() + console.print(f" [green]Removed broken symlink: {broken_path.name}[/green]") + fixed += 1 + else: + console.print(f" [yellow]Skipped (no longer broken): {broken_path.name}[/yellow]") + failed += 1 + except OSError as e: + console.print(f" [red]Failed to remove broken symlink: {e}[/red]") + failed += 1 + elif "link" in fix_data and "target" in fix_data: link_path = Path(fix_data["link"]) target_path = Path(fix_data["target"]) diff --git a/packages/smartem-workspace/smartem_workspace/config/claude-code-config.json b/packages/smartem-workspace/smartem_workspace/config/claude-code-config.json index e624358..983d1b0 100644 --- a/packages/smartem-workspace/smartem_workspace/config/claude-code-config.json +++ b/packages/smartem-workspace/smartem_workspace/config/claude-code-config.json @@ -4,13 +4,11 @@ "description": "Claude Code integration configuration for SmartEM workspace", "claudeConfig": { "skills": [ - { "name": "database-admin", "path": "claude-code/shared/skills/database-admin" }, + { "name": "database", "path": "claude-code/shared/skills/database" }, { "name": "devops", "path": "claude-code/shared/skills/devops" }, - { "name": "technical-writer", "path": "claude-code/shared/skills/technical-writer" }, + { "name": "tech-writer", "path": "claude-code/shared/skills/tech-writer" }, { "name": "git", "path": "claude-code/shared/skills/git" }, - { "name": "github", "path": "claude-code/shared/skills/github" }, - { "name": "ascii-art", "path": "claude-code/shared/skills/ascii-art" }, - { "name": "playwright-skill", "path": "claude-code/smartem-frontend/skills/playwright-skill" } + { "name": "playwright", "path": "claude-code/smartem-frontend/skills/playwright" } ], "defaultPermissions": { "allow": [ diff --git a/packages/smartem-workspace/uv.lock b/packages/smartem-workspace/uv.lock index eacc272..8d03d61 100644 --- a/packages/smartem-workspace/uv.lock +++ b/packages/smartem-workspace/uv.lock @@ -496,7 +496,7 @@ wheels = [ [[package]] name = "smartem-workspace" -version = "0.6.0" +version = "0.6.1" source = { editable = "." } dependencies = [ { name = "httpx" },