refactor: Remove legacy config format support (v2.11.0)

BREAKING CHANGE: Legacy config format no longer supported

Changes:
- ConfigValidator now only accepts unified format with 'sources' array
- Removed _validate_legacy() method
- Removed convert_legacy_to_unified() and all conversion helpers
- Simplified get_sources_by_type() and has_multiple_sources()
- Updated __main__ to remove legacy format checks
- Converted claude-code.json to unified format
- Deleted blender.json (duplicate of blender-unified.json)
- Clear error message when legacy format detected

Error message shows:
  - Legacy format was removed in v2.11.0
  - Example of old vs new format
  - Migration guide link

Code reduction: -86 lines
All 65 tests passing

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
yusyus
2026-02-08 02:27:22 +03:00
parent 7648601eea
commit 71b7304a9a
4 changed files with 209 additions and 404 deletions

View File

@@ -22,6 +22,12 @@
- ✅ All 65 tests passing
- ✅ Runtime behavior verified
### Legacy Config Format Removal
- ✅ All configs converted to unified format
- ✅ Legacy validation methods removed
- ✅ Clear error messages for old configs
- ✅ Simplified codebase (removed 86 lines)
---
## 📊 Key Metrics
@@ -181,6 +187,7 @@ Status: ✅ ALL PASSED
## 📦 Commits
```
PENDING refactor: Remove legacy config format support (v2.11.0)
c8195bc fix: QA audit - Fix 5 critical bugs in preset system
19fa91e docs: Add comprehensive summary for all 4 phases (v2.11.0)
67c3ab9 feat(cli): Implement formal preset system for analyze command (Phase 4)

View File

@@ -1,198 +0,0 @@
{
"name": "blender",
"description": "Blender 3D creation suite for modeling, animation, rendering, compositing, video editing, and game development. Use for 3D modeling, sculpting, animation, shading, rendering, simulation, video editing, and Python scripting.",
"base_url": "https://docs.blender.org/manual/en/latest/",
"selectors": {
"main_content": "article[role='main']",
"title": "h1",
"code_blocks": "pre code, div.highlight pre"
},
"url_patterns": {
"include": [
"/getting_started/",
"/interface/",
"/editors/",
"/modeling/",
"/sculpt_paint/",
"/grease_pencil/",
"/animation/",
"/physics/",
"/render/",
"/scene_layout/",
"/compositing/",
"/video_editing/",
"/files/",
"/addons/",
"/advanced/",
"/troubleshooting/"
],
"exclude": [
"/_static/",
"/_images/",
"/search.html",
"/genindex.html",
"/glossary.html",
"/index.html$"
]
},
"categories": {
"getting_started": [
"getting_started",
"installing",
"configuration",
"introduction",
"quickstart",
"about"
],
"interface": [
"interface",
"window_system",
"keymap",
"controls",
"operators",
"tools",
"ui",
"navigation"
],
"modeling": [
"modeling",
"mesh",
"curve",
"surface",
"metaball",
"text",
"volume",
"geometry_nodes",
"modifiers",
"mesh_tools",
"edit_mode"
],
"sculpting": [
"sculpt",
"sculpting",
"brush",
"texture_paint",
"vertex_paint",
"weight_paint",
"dynamic_paint"
],
"grease_pencil": [
"grease_pencil",
"2d_animation",
"drawing",
"stroke"
],
"animation": [
"animation",
"keyframe",
"rigging",
"armature",
"constraints",
"drivers",
"shape_keys",
"motion_paths",
"timeline",
"dope_sheet",
"graph_editor",
"nla"
],
"physics": [
"physics",
"simulation",
"particles",
"hair",
"fluid",
"cloth",
"soft_body",
"rigid_body",
"dynamic_paint",
"force_fields"
],
"shading": [
"shading",
"shader",
"material",
"texture",
"nodes",
"shader_nodes",
"lighting",
"world"
],
"rendering": [
"render",
"eevee",
"cycles",
"workbench",
"freestyle",
"camera",
"output",
"color_management",
"optimization"
],
"compositing": [
"compositing",
"compositor",
"nodes",
"color_correction",
"filters",
"matte"
],
"video_editing": [
"video_editing",
"vse",
"sequencer",
"strips",
"effects",
"preview"
],
"scene_layout": [
"scene",
"object",
"collection",
"properties",
"outliner",
"view_layers"
],
"files_assets": [
"files",
"import",
"export",
"asset",
"library",
"data_blocks",
"linking",
"append"
],
"addons": [
"addon",
"plugin",
"extension",
"import_export"
],
"scripting": [
"scripting",
"python",
"api",
"bpy",
"operator",
"custom",
"automation"
],
"advanced": [
"advanced",
"command_line",
"app_templates",
"extensions",
"limits"
],
"troubleshooting": [
"troubleshooting",
"crash",
"recover",
"gpu",
"graphics"
]
},
"rate_limit": 0.5,
"max_pages": 1500
}

View File

@@ -1,84 +1,164 @@
{
"_migration_note": "TODO: Migrate to external skill-seekers-configs repo. Kept temporarily to preserve PR #244 work.",
"name": "claude-code",
"description": "Claude Code CLI and development environment. Use for Claude Code features, tools, workflows, MCP integration, plugins, hooks, configuration, deployment, and AI-assisted development.",
"base_url": "https://code.claude.com/docs/en/",
"start_urls": [
"https://code.claude.com/docs/en/overview",
"https://code.claude.com/docs/en/quickstart",
"https://code.claude.com/docs/en/common-workflows",
"https://code.claude.com/docs/en/claude-code-on-the-web",
"https://code.claude.com/docs/en/desktop",
"https://code.claude.com/docs/en/chrome",
"https://code.claude.com/docs/en/vs-code",
"https://code.claude.com/docs/en/jetbrains",
"https://code.claude.com/docs/en/github-actions",
"https://code.claude.com/docs/en/gitlab-ci-cd",
"https://code.claude.com/docs/en/slack",
"https://code.claude.com/docs/en/sub-agents",
"https://code.claude.com/docs/en/plugins",
"https://code.claude.com/docs/en/discover-plugins",
"https://code.claude.com/docs/en/skills",
"https://code.claude.com/docs/en/output-styles",
"https://code.claude.com/docs/en/hooks-guide",
"https://code.claude.com/docs/en/headless",
"https://code.claude.com/docs/en/mcp",
"https://code.claude.com/docs/en/third-party-integrations",
"https://code.claude.com/docs/en/amazon-bedrock",
"https://code.claude.com/docs/en/google-vertex-ai",
"https://code.claude.com/docs/en/microsoft-foundry",
"https://code.claude.com/docs/en/network-config",
"https://code.claude.com/docs/en/llm-gateway",
"https://code.claude.com/docs/en/devcontainer",
"https://code.claude.com/docs/en/sandboxing",
"https://code.claude.com/docs/en/setup",
"https://code.claude.com/docs/en/iam",
"https://code.claude.com/docs/en/security",
"https://code.claude.com/docs/en/data-usage",
"https://code.claude.com/docs/en/monitoring-usage",
"https://code.claude.com/docs/en/costs",
"https://code.claude.com/docs/en/analytics",
"https://code.claude.com/docs/en/plugin-marketplaces",
"https://code.claude.com/docs/en/settings",
"https://code.claude.com/docs/en/terminal-config",
"https://code.claude.com/docs/en/model-config",
"https://code.claude.com/docs/en/memory",
"https://code.claude.com/docs/en/statusline",
"https://code.claude.com/docs/en/cli-reference",
"https://code.claude.com/docs/en/interactive-mode",
"https://code.claude.com/docs/en/slash-commands",
"https://code.claude.com/docs/en/checkpointing",
"https://code.claude.com/docs/en/hooks",
"https://code.claude.com/docs/en/plugins-reference",
"https://code.claude.com/docs/en/troubleshooting",
"https://code.claude.com/docs/en/legal-and-compliance"
],
"selectors": {
"main_content": "#content-area, #content-container, article, main",
"title": "h1",
"code_blocks": "pre code"
},
"url_patterns": {
"include": ["/docs/en/"],
"exclude": [
"/docs/fr/", "/docs/de/", "/docs/it/", "/docs/ja/", "/docs/es/",
"/docs/ko/", "/docs/zh-CN/", "/docs/zh-TW/", "/docs/ru/",
"/docs/id/", "/docs/pt/", "/changelog", "github.com"
]
},
"categories": {
"getting_started": ["overview", "quickstart", "common-workflows"],
"ide_integrations": ["vs-code", "jetbrains", "desktop", "chrome", "claude-code-on-the-web", "slack"],
"ci_cd": ["github-actions", "gitlab-ci-cd"],
"building": ["sub-agents", "subagent", "plugins", "discover-plugins", "skills", "output-styles", "hooks-guide", "headless", "programmatic"],
"mcp": ["mcp", "model-context-protocol"],
"deployment": ["third-party-integrations", "amazon-bedrock", "google-vertex-ai", "microsoft-foundry", "network-config", "llm-gateway", "devcontainer", "sandboxing"],
"administration": ["setup", "iam", "security", "data-usage", "monitoring-usage", "costs", "analytics", "plugin-marketplaces"],
"configuration": ["settings", "terminal-config", "model-config", "memory", "statusline"],
"reference": ["cli-reference", "interactive-mode", "slash-commands", "checkpointing", "hooks", "plugins-reference"],
"troubleshooting": ["troubleshooting"],
"legal": ["legal-and-compliance"]
},
"rate_limit": 0.5,
"max_pages": 250
}
"merge_mode": "rule-based",
"sources": [
{
"type": "documentation",
"_migration_note": "TODO: Migrate to external skill-seekers-configs repo. Kept temporarily to preserve PR #244 work.",
"base_url": "https://code.claude.com/docs/en/",
"start_urls": [
"https://code.claude.com/docs/en/overview",
"https://code.claude.com/docs/en/quickstart",
"https://code.claude.com/docs/en/common-workflows",
"https://code.claude.com/docs/en/claude-code-on-the-web",
"https://code.claude.com/docs/en/desktop",
"https://code.claude.com/docs/en/chrome",
"https://code.claude.com/docs/en/vs-code",
"https://code.claude.com/docs/en/jetbrains",
"https://code.claude.com/docs/en/github-actions",
"https://code.claude.com/docs/en/gitlab-ci-cd",
"https://code.claude.com/docs/en/slack",
"https://code.claude.com/docs/en/sub-agents",
"https://code.claude.com/docs/en/plugins",
"https://code.claude.com/docs/en/discover-plugins",
"https://code.claude.com/docs/en/skills",
"https://code.claude.com/docs/en/output-styles",
"https://code.claude.com/docs/en/hooks-guide",
"https://code.claude.com/docs/en/headless",
"https://code.claude.com/docs/en/mcp",
"https://code.claude.com/docs/en/third-party-integrations",
"https://code.claude.com/docs/en/amazon-bedrock",
"https://code.claude.com/docs/en/google-vertex-ai",
"https://code.claude.com/docs/en/microsoft-foundry",
"https://code.claude.com/docs/en/network-config",
"https://code.claude.com/docs/en/llm-gateway",
"https://code.claude.com/docs/en/devcontainer",
"https://code.claude.com/docs/en/sandboxing",
"https://code.claude.com/docs/en/setup",
"https://code.claude.com/docs/en/iam",
"https://code.claude.com/docs/en/security",
"https://code.claude.com/docs/en/data-usage",
"https://code.claude.com/docs/en/monitoring-usage",
"https://code.claude.com/docs/en/costs",
"https://code.claude.com/docs/en/analytics",
"https://code.claude.com/docs/en/plugin-marketplaces",
"https://code.claude.com/docs/en/settings",
"https://code.claude.com/docs/en/terminal-config",
"https://code.claude.com/docs/en/model-config",
"https://code.claude.com/docs/en/memory",
"https://code.claude.com/docs/en/statusline",
"https://code.claude.com/docs/en/cli-reference",
"https://code.claude.com/docs/en/interactive-mode",
"https://code.claude.com/docs/en/slash-commands",
"https://code.claude.com/docs/en/checkpointing",
"https://code.claude.com/docs/en/hooks",
"https://code.claude.com/docs/en/plugins-reference",
"https://code.claude.com/docs/en/troubleshooting",
"https://code.claude.com/docs/en/legal-and-compliance"
],
"selectors": {
"main_content": "#content-area, #content-container, article, main",
"title": "h1",
"code_blocks": "pre code"
},
"url_patterns": {
"include": [
"/docs/en/"
],
"exclude": [
"/docs/fr/",
"/docs/de/",
"/docs/it/",
"/docs/ja/",
"/docs/es/",
"/docs/ko/",
"/docs/zh-CN/",
"/docs/zh-TW/",
"/docs/ru/",
"/docs/id/",
"/docs/pt/",
"/changelog",
"github.com"
]
},
"categories": {
"getting_started": [
"overview",
"quickstart",
"common-workflows"
],
"ide_integrations": [
"vs-code",
"jetbrains",
"desktop",
"chrome",
"claude-code-on-the-web",
"slack"
],
"ci_cd": [
"github-actions",
"gitlab-ci-cd"
],
"building": [
"sub-agents",
"subagent",
"plugins",
"discover-plugins",
"skills",
"output-styles",
"hooks-guide",
"headless",
"programmatic"
],
"mcp": [
"mcp",
"model-context-protocol"
],
"deployment": [
"third-party-integrations",
"amazon-bedrock",
"google-vertex-ai",
"microsoft-foundry",
"network-config",
"llm-gateway",
"devcontainer",
"sandboxing"
],
"administration": [
"setup",
"iam",
"security",
"data-usage",
"monitoring-usage",
"costs",
"analytics",
"plugin-marketplaces"
],
"configuration": [
"settings",
"terminal-config",
"model-config",
"memory",
"statusline"
],
"reference": [
"cli-reference",
"interactive-mode",
"slash-commands",
"checkpointing",
"hooks",
"plugins-reference"
],
"troubleshooting": [
"troubleshooting"
],
"legal": [
"legal-and-compliance"
]
},
"rate_limit": 0.5,
"max_pages": 250
}
]
}

View File

@@ -6,8 +6,10 @@ Validates unified config format that supports multiple sources:
- documentation (website scraping)
- github (repository scraping)
- pdf (PDF document scraping)
- local (local codebase analysis)
Also provides backward compatibility detection for legacy configs.
Legacy config format support removed in v2.11.0.
All configs must use unified format with 'sources' array.
"""
import json
@@ -21,7 +23,7 @@ logger = logging.getLogger(__name__)
class ConfigValidator:
"""
Validates unified config format and provides backward compatibility.
Validates unified config format (legacy support removed in v2.11.0).
"""
# Valid source types
@@ -49,7 +51,7 @@ class ConfigValidator:
else:
self.config_path = config_or_path
self.config = self._load_config()
self.is_unified = self._detect_format()
self.is_unified = True # Always unified format now
def _load_config(self) -> dict[str, Any]:
"""Load JSON config file."""
@@ -61,19 +63,9 @@ class ConfigValidator:
except json.JSONDecodeError as e:
raise ValueError(f"Invalid JSON in config file: {e}") from e
def _detect_format(self) -> bool:
"""
Detect if config is unified format or legacy.
Returns:
True if unified format (has 'sources' array)
False if legacy format
"""
return "sources" in self.config and isinstance(self.config["sources"], list)
def validate(self) -> bool:
"""
Validate config based on detected format.
Validate unified config format.
Returns:
True if valid
@@ -81,10 +73,32 @@ class ConfigValidator:
Raises:
ValueError if invalid with detailed error message
"""
if self.is_unified:
return self._validate_unified()
else:
return self._validate_legacy()
# Check if legacy format (no sources array)
if "sources" not in self.config:
raise ValueError(
"\n❌ LEGACY CONFIG FORMAT DETECTED\n\n"
" Legacy config format was removed in v2.11.0.\n"
" All configs must now use unified format with 'sources' array.\n\n"
" OLD FORMAT (removed):\n"
" {\n"
' "name": "example",\n'
' "base_url": "https://..."\n'
" }\n\n"
" NEW FORMAT (required):\n"
" {\n"
' "name": "example",\n'
' "description": "...",\n'
' "sources": [\n'
" {\n"
' "type": "documentation",\n'
' "base_url": "https://..."\n'
" }\n"
" ]\n"
" }\n\n"
" 📖 See: https://skillseekersweb.com/docs/config-format\n"
)
return self._validate_unified()
def _validate_unified(self) -> bool:
"""Validate unified config format."""
@@ -239,117 +253,21 @@ class ConfigValidator:
f"Source {index} (local): Invalid ai_mode '{ai_mode}'. Must be one of {self.VALID_AI_MODES}"
)
def _validate_legacy(self) -> bool:
"""
Validate legacy config format (backward compatibility).
Legacy configs are the old format used by doc_scraper, github_scraper, pdf_scraper.
"""
logger.info("Detected legacy config format (backward compatible)")
# Detect which legacy type based on fields
if "base_url" in self.config:
logger.info("Legacy type: documentation")
elif "repo" in self.config:
logger.info("Legacy type: github")
elif "pdf" in self.config or "path" in self.config:
logger.info("Legacy type: pdf")
else:
raise ValueError("Cannot detect legacy config type (missing base_url, repo, or pdf)")
return True
def convert_legacy_to_unified(self) -> dict[str, Any]:
"""
Convert legacy config to unified format.
Returns:
Unified config dict
"""
if self.is_unified:
logger.info("Config already in unified format")
return self.config
logger.info("Converting legacy config to unified format...")
# Detect legacy type and convert
if "base_url" in self.config:
return self._convert_legacy_documentation()
elif "repo" in self.config:
return self._convert_legacy_github()
elif "pdf" in self.config or "path" in self.config:
return self._convert_legacy_pdf()
else:
raise ValueError("Cannot convert: unknown legacy format")
def _convert_legacy_documentation(self) -> dict[str, Any]:
"""Convert legacy documentation config to unified."""
unified = {
"name": self.config.get("name", "unnamed"),
"description": self.config.get("description", "Documentation skill"),
"merge_mode": "rule-based",
"sources": [
{
"type": "documentation",
**{k: v for k, v in self.config.items() if k not in ["name", "description"]},
}
],
}
return unified
def _convert_legacy_github(self) -> dict[str, Any]:
"""Convert legacy GitHub config to unified."""
unified = {
"name": self.config.get("name", "unnamed"),
"description": self.config.get("description", "GitHub repository skill"),
"merge_mode": "rule-based",
"sources": [
{
"type": "github",
**{k: v for k, v in self.config.items() if k not in ["name", "description"]},
}
],
}
return unified
def _convert_legacy_pdf(self) -> dict[str, Any]:
"""Convert legacy PDF config to unified."""
unified = {
"name": self.config.get("name", "unnamed"),
"description": self.config.get("description", "PDF document skill"),
"merge_mode": "rule-based",
"sources": [
{
"type": "pdf",
**{k: v for k, v in self.config.items() if k not in ["name", "description"]},
}
],
}
return unified
def get_sources_by_type(self, source_type: str) -> list[dict[str, Any]]:
"""
Get all sources of a specific type.
Args:
source_type: 'documentation', 'github', or 'pdf'
source_type: 'documentation', 'github', 'pdf', or 'local'
Returns:
List of sources matching the type
"""
if not self.is_unified:
# For legacy, convert and get sources
unified = self.convert_legacy_to_unified()
sources = unified["sources"]
else:
sources = self.config["sources"]
sources = self.config["sources"]
return [s for s in sources if s.get("type") == source_type]
def has_multiple_sources(self) -> bool:
"""Check if config has multiple sources (requires merging)."""
if not self.is_unified:
return False
return len(self.config["sources"]) > 1
def needs_api_merge(self) -> bool:
@@ -406,18 +324,16 @@ if __name__ == "__main__":
validator = validate_config(config_file)
print("\n✅ Config valid!")
print(f" Format: {'Unified' if validator.is_unified else 'Legacy'}")
print(f" Name: {validator.config.get('name')}")
if validator.is_unified:
sources = validator.config["sources"]
print(f" Sources: {len(sources)}")
for i, source in enumerate(sources):
print(f" {i + 1}. {source['type']}")
sources = validator.config["sources"]
print(f" Sources: {len(sources)}")
for i, source in enumerate(sources):
print(f" {i + 1}. {source['type']}")
if validator.needs_api_merge():
merge_mode = validator.config.get("merge_mode", "rule-based")
print(f" ⚠️ API merge required (mode: {merge_mode})")
if validator.needs_api_merge():
merge_mode = validator.config.get("merge_mode", "rule-based")
print(f" ⚠️ API merge required (mode: {merge_mode})")
except ValueError as e:
print(f"\n❌ Config invalid: {e}")