Tools

File: /home/atari-monk/atari-monk/project/tools/prompt_tool/cli.py

import argparse
import json
import subprocess
from pathlib import Path
from typing import TypeAlias


PromptFileMap: TypeAlias = dict[str, list[Path]]
PromptFileMapRaw: TypeAlias = dict[str, list[str]]
PromptMapRaw: TypeAlias = dict[str, PromptFileMapRaw]
FilesContext: TypeAlias = dict[str, list[str]]


PROMPTS: dict[str, str] = {
    "implementation": "Implementation prompt (code generation)",
    "requirements-implementation": "Implementation with requirements checklist",
    "analysis": "Architecture and design analysis",
}

PROJECTS: dict[str, str] = {
    "tools": "Tools project",
    "bowling-game": "Mini game about bowling",
}

PROMPTS_ROOT = Path("/home/atari-monk/atari-monk/project/prompts/prompts")
PROJECTS_ROOT = Path("/home/atari-monk/atari-monk/project")

CODE_SUFFIXES: set[str] = {
    "py",
    "ts",
    "js",
    "jsx",
    "tsx",
    "java",
    "go",
    "rs",
    "cpp",
    "c",
    "h",
    "hpp",
    "cs",
    "kt",
    "swift",
    "php",
    "rb",
    "sh",
}


def load_markdown(path: Path) -> str:
    return path.read_text(encoding="utf-8")


def convert_prompt_group(group: PromptFileMapRaw) -> PromptFileMap:
    return {key: [Path(value) for value in values] for key, values in group.items()}


def load_paths(path: Path, prompt_key: str) -> PromptFileMap:
    raw: PromptMapRaw = json.loads(path.read_text(encoding="utf-8"))

    if prompt_key not in raw:
        raise ValueError(f"Prompt '{prompt_key}' not found in prompt map")

    return convert_prompt_group(raw[prompt_key])


def is_code_file(path: Path) -> bool:
    return path.suffix.lstrip(".") in CODE_SUFFIXES


def load_file(path: Path, show_path: bool) -> str:
    content = path.read_text(encoding="utf-8")

    if not is_code_file(path):
        return content

    language = path.suffix.lstrip(".")
    parts: list[str] = []

    if show_path:
        parts.append(str(path.resolve()))

    parts.append(f"```{language}\n{content}\n```")

    return "\n".join(parts)


def load_context(paths: PromptFileMap, show_path: bool) -> FilesContext:
    return {key: [load_file(path, show_path) for path in values] for key, values in paths.items()}


def inject(template: str, ctx: FilesContext) -> str:
    result = template

    for key, values in ctx.items():
        result = result.replace(f"[[{key}]]", "\n\n-----\n\n".join(values))

    return result


def copy_to_clipboard(text: str) -> None:
    subprocess.run(
        ["xclip", "-selection", "clipboard"],
        input=text,
        text=True,
        check=True,
    )


def build_prompt(prompt_name: str, project_name: str, show_code_path: bool) -> str:
    template_path = PROMPTS_ROOT / f"{prompt_name}.md"

    map_path = PROJECTS_ROOT / project_name / ".config" / "prompt-map.json"

    template = load_markdown(template_path)
    paths = load_paths(map_path, prompt_name)
    context = load_context(paths, show_code_path)

    return inject(template, context)


def list_items() -> None:
    print("Available prompts:\n")

    for key, value in PROMPTS.items():
        print(f"  {key:<30} {value}")

    print("\nAvailable projects:\n")

    for key, value in PROJECTS.items():
        print(f"  {key:<30} {value}")


def create_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(prog="prompt", description="Prompt builder tool")

    parser.add_argument("command", nargs="?", help="Prompt name or 'list'")
    parser.add_argument("project", nargs="?", help="Project name")

    parser.add_argument(
        "-cp",
        "--code-path",
        action="store_true",
        help="Print absolute path before code blocks",
    )

    parser.add_argument(
        "-p",
        "--print",
        dest="print_result",
        action="store_true",
        help="Print result to console",
    )

    return parser


def main() -> int:
    parser = create_parser()
    args = parser.parse_args()

    if args.command == "list":
        list_items()
        return 0

    if args.command is None or args.project is None:
        parser.print_help()
        return 1

    prompt_name: str = args.command
    project_name: str = args.project

    if prompt_name not in PROMPTS:
        parser.print_help()
        return 1

    if project_name not in PROJECTS:
        parser.print_help()
        return 1

    result = build_prompt(prompt_name, project_name, args.code_path)

    if args.print_result:
        print(result)

    copy_to_clipboard(result)

    return 0


if __name__ == "__main__":
    raise SystemExit(main())