pbi-cli/src/pbi_cli/core/config.py
MinaSaad1 51a23668a7 feat: add REPL mode, test suite, CI/CD, and Claude Skills (Sprints 6-8)
Sprint 6 - REPL Mode + Polish:
- Error hierarchy (PbiCliError, McpToolError, etc.) for clean REPL error handling
- Interactive REPL with prompt-toolkit (persistent MCP connection, command completion, history)
- REPL-aware run_tool() and connection commands that reuse shared client
- README.md and CHANGELOG.md

Sprint 7 - Tests + CI/CD:
- 120 tests across unit, command, and e2e test files (79% coverage)
- MockPbiMcpClient with canned responses for test isolation
- GitHub Actions CI (lint + typecheck + test matrix: 3 OS x 3 Python)
- GitHub Actions release workflow for PyPI trusted publishing

Sprint 8 - Claude Skills + Installer:
- 5 bundled SKILL.md files (modeling, dax, deployment, security, docs)
- `pbi skills install/list/uninstall` command for Claude Code discovery
- Skills packaged with wheel via setuptools package-data
2026-03-26 13:54:24 +02:00

60 lines
1.8 KiB
Python

"""Configuration management for pbi-cli.
Manages ~/.pbi-cli/config.json for binary paths, versions, and preferences.
"""
from __future__ import annotations
import json
from dataclasses import asdict, dataclass, field
from pathlib import Path
PBI_CLI_HOME = Path.home() / ".pbi-cli"
CONFIG_FILE = PBI_CLI_HOME / "config.json"
@dataclass(frozen=True)
class PbiConfig:
"""Immutable configuration object."""
binary_version: str = ""
binary_path: str = ""
default_connection: str = ""
binary_args: list[str] = field(default_factory=lambda: ["--start", "--skipconfirmation"])
def with_updates(self, **kwargs: object) -> PbiConfig:
"""Return a new config with the specified fields updated."""
current = asdict(self)
current.update(kwargs)
return PbiConfig(**current)
def ensure_home_dir() -> Path:
"""Create ~/.pbi-cli/ if it does not exist. Returns the path."""
PBI_CLI_HOME.mkdir(parents=True, exist_ok=True)
return PBI_CLI_HOME
def load_config() -> PbiConfig:
"""Load config from disk. Returns defaults if file does not exist."""
if not CONFIG_FILE.exists():
return PbiConfig()
try:
raw = json.loads(CONFIG_FILE.read_text(encoding="utf-8"))
return PbiConfig(
binary_version=raw.get("binary_version", ""),
binary_path=raw.get("binary_path", ""),
default_connection=raw.get("default_connection", ""),
binary_args=raw.get("binary_args", ["--start", "--skipconfirmation"]),
)
except (json.JSONDecodeError, KeyError):
return PbiConfig()
def save_config(config: PbiConfig) -> None:
"""Write config to disk."""
ensure_home_dir()
CONFIG_FILE.write_text(
json.dumps(asdict(config), indent=2, ensure_ascii=False) + "\n",
encoding="utf-8",
)