ai-lsc/scripts/backfill_layer_flags.py

89 lines
2.9 KiB
Python
Executable File

"""H-15: backfill missing boolean flag keys in registry layer files.
Every registry entry must declare all 7 flag keys per the ToolFlags
schema. Layer files that pre-date the schema expansion only declare
the first three (has_cli / has_gui / has_web). This script appends the
missing four (is_ollama / is_passive / is_mcp /
is_skills_collection) defaulting to ``False`` to every flags block in
every layer file under ``registry/layers/``.
"""
from __future__ import annotations
import re
import sys
from pathlib import Path
LAYER_DIR = Path("/home/z/my-project/workspace/ai-lsc/src/ai_lsc/registry/layers")
REQUIRED_KEYS = [
"has_cli",
"has_gui",
"has_web",
"is_ollama",
"is_passive",
"is_mcp",
"is_skills_collection",
]
# Matches a `"flags": { ... }` block, capturing the inner body.
_FLAGS_RE = re.compile(
r'("flags":\s*\{)([^}]*)(\})',
re.DOTALL,
)
def backfill(text: str) -> tuple[str, int]:
"""Return (new_text, number_of_blocks_updated)."""
updated = 0
def repl(m: re.Match) -> str:
nonlocal updated
head, body, tail = m.group(1), m.group(2), m.group(3)
present = set(re.findall(r'"([a-z_]+)"\s*:', body))
missing = [k for k in REQUIRED_KEYS if k not in present]
if not missing:
return m.group(0)
indent_match = re.search(r'\n([ \t]+)"', body)
indent = indent_match.group(1) if indent_match else " "
# Build the new body from scratch. Strip trailing whitespace
# and any trailing comma from the existing body so we can append
# new lines cleanly; if the body is empty (was `{}`), start fresh.
body_stripped = body.rstrip()
body_stripped = re.sub(r',\s*$', '', body_stripped)
new_lines = []
if body_stripped:
new_lines.append(body_stripped + ",")
for i, k in enumerate(missing):
suffix = "," if i < len(missing) - 1 else ""
new_lines.append(f'{indent}"{k}": False{suffix}')
updated += 1
close_indent = indent[:-4] if len(indent) >= 4 else ""
return f"{head}" + "\n".join(new_lines) + f"\n{close_indent}{tail}"
new_text = _FLAGS_RE.sub(repl, text)
return new_text, updated
def main() -> int:
if not LAYER_DIR.is_dir():
print(f"layer dir not found: {LAYER_DIR}", file=sys.stderr)
return 2
total_blocks = 0
total_files = 0
for path in sorted(LAYER_DIR.glob("*.py")):
original = path.read_text(encoding="utf-8")
new_text, blocks = backfill(original)
if blocks:
path.write_text(new_text, encoding="utf-8")
print(f" {path.name}: updated {blocks} flags block(s)")
total_blocks += blocks
total_files += 1
else:
print(f" {path.name}: no changes needed")
print(f"\nUpdated {total_blocks} flags block(s) across {total_files} file(s).")
return 0
if __name__ == "__main__":
sys.exit(main())