feat(helper): add interactive Q&A helper command
Add `markitect helper <QUESTION>` CLI command that answers questions about markitect using its own documentation as LLM context. Uses OpenRouter with openrouter/aurora-alpha by default; model is configurable via --model flag or MARKITECT_HELPER_MODEL env var. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
101
markitect/helper/cli.py
Normal file
101
markitect/helper/cli.py
Normal file
@@ -0,0 +1,101 @@
|
||||
"""
|
||||
CLI command for the markitect helper.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import click
|
||||
|
||||
from markitect.helper.knowledge import collect_knowledge
|
||||
|
||||
DEFAULT_PROVIDER = "openrouter"
|
||||
DEFAULT_MODEL = "openrouter/aurora-alpha"
|
||||
MODEL_ENV_VAR = "MARKITECT_HELPER_MODEL"
|
||||
|
||||
SYSTEM_PROMPT_TEMPLATE = (
|
||||
"You are a MarkiTect expert assistant. Answer the user's question "
|
||||
"based on the following MarkiTect documentation. Be concise and "
|
||||
"accurate. If the documentation does not cover the question, say so.\n\n"
|
||||
"{knowledge}"
|
||||
)
|
||||
|
||||
|
||||
@click.command("helper")
|
||||
@click.argument("question", nargs=-1, required=True)
|
||||
@click.option(
|
||||
"--provider", "-p",
|
||||
default=DEFAULT_PROVIDER,
|
||||
type=click.Choice(["openrouter", "claude-code", "gemini", "openai"]),
|
||||
show_default=True,
|
||||
help="LLM provider to use.",
|
||||
)
|
||||
@click.option(
|
||||
"--model", "-m",
|
||||
default=None,
|
||||
help=(
|
||||
f"Model name. Overrides {MODEL_ENV_VAR} env var and the default "
|
||||
f"({DEFAULT_MODEL})."
|
||||
),
|
||||
)
|
||||
def helper_command(question, provider, model):
|
||||
"""Ask a question about MarkiTect and get an answer from the docs.
|
||||
|
||||
\b
|
||||
Examples:
|
||||
markitect helper "What is markitect?"
|
||||
markitect helper How do schemas work
|
||||
markitect helper -m anthropic/claude-sonnet-4 "Explain templates"
|
||||
"""
|
||||
from markitect.llm import create_adapter
|
||||
from markitect.llm.exceptions import LLMConfigurationError, LLMError
|
||||
from markitect.prompts.execution.models import RunConfig
|
||||
|
||||
# Join multi-word question into a single string.
|
||||
question_text = " ".join(question)
|
||||
if not question_text.strip():
|
||||
click.echo("Error: empty question.", err=True)
|
||||
sys.exit(1)
|
||||
|
||||
# Resolve model: --model flag > env var > default.
|
||||
resolved_model = model or os.environ.get(MODEL_ENV_VAR) or DEFAULT_MODEL
|
||||
|
||||
# Build knowledge context.
|
||||
click.echo("Loading markitect knowledge base...", err=True)
|
||||
knowledge = collect_knowledge()
|
||||
if not knowledge:
|
||||
click.echo("Warning: no documentation files found.", err=True)
|
||||
|
||||
system_prompt = SYSTEM_PROMPT_TEMPLATE.format(knowledge=knowledge)
|
||||
|
||||
# Create adapter.
|
||||
try:
|
||||
adapter = create_adapter(
|
||||
provider=provider,
|
||||
model=resolved_model,
|
||||
system_prompt=system_prompt,
|
||||
)
|
||||
except LLMConfigurationError as exc:
|
||||
click.echo(f"Configuration error: {exc}", err=True)
|
||||
if "api" in str(exc).lower() or "key" in str(exc).lower():
|
||||
click.echo(
|
||||
"Hint: set OPENROUTER_API_KEY (or the relevant provider key) "
|
||||
"in your environment.",
|
||||
err=True,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
# Execute the question.
|
||||
click.echo(f"Asking {provider} ({resolved_model})...", err=True)
|
||||
try:
|
||||
config = RunConfig(
|
||||
model_name=resolved_model,
|
||||
max_tokens=4000,
|
||||
temperature=0.3,
|
||||
)
|
||||
response = adapter.execute_prompt(question_text, config)
|
||||
except LLMError as exc:
|
||||
click.echo(f"LLM error: {exc}", err=True)
|
||||
sys.exit(1)
|
||||
|
||||
click.echo(response.content)
|
||||
Reference in New Issue
Block a user