cgcardona / muse public
code_query.py python
91 lines 3.1 KB
e6786943 feat: upgrade to Python 3.14, drop from __future__ import annotations Gabriel Cardona <cgcardona@gmail.com> 1d ago
1 """``muse code-query`` — predicate query over code commit history.
2
3 Search the commit graph for code changes matching a structured predicate::
4
5 muse code-query "symbol == 'my_function' and change == 'added'"
6 muse code-query "language == 'Python' and author == 'agent-x'"
7 muse code-query "agent_id == 'claude' and sem_ver_bump == 'major'"
8 muse code-query "file == 'src/core.py'"
9 muse code-query "change == 'removed' and kind == 'class'"
10 muse code-query "model_id contains 'claude'"
11
12 Fields
13 ------
14
15 ``symbol`` Qualified symbol name (e.g. ``"MyClass.method"``).
16 ``file`` Workspace-relative file path.
17 ``language`` Language name (``"Python"``, ``"TypeScript"``…).
18 ``kind`` Symbol kind (``"function"``, ``"class"``, ``"method"``…).
19 ``change`` ``"added"``, ``"removed"``, or ``"modified"``.
20 ``author`` Commit author string.
21 ``agent_id`` Agent identity from commit provenance.
22 ``model_id`` Model ID from commit provenance.
23 ``toolchain_id`` Toolchain string from commit provenance.
24 ``sem_ver_bump`` ``"none"``, ``"patch"``, ``"minor"``, or ``"major"``.
25 ``branch`` Branch name.
26
27 Operators: ``==``, ``!=``, ``contains``, ``startswith``
28
29 Usage::
30
31 muse code-query QUERY
32 muse code-query QUERY --branch dev --max 100
33 muse code-query QUERY --json
34 """
35
36 import json
37 import logging
38 import pathlib
39 import sys
40
41 import typer
42
43 from muse.core.query_engine import format_matches, walk_history
44 from muse.core.repo import require_repo
45 from muse.plugins.code._code_query import build_evaluator
46
47 logger = logging.getLogger(__name__)
48
49 app = typer.Typer()
50
51
52 def _current_branch(root: pathlib.Path) -> str:
53 head_ref = (root / ".muse" / "HEAD").read_text().strip()
54 return head_ref.removeprefix("refs/heads/").strip()
55
56
57 @app.callback(invoke_without_command=True)
58 def code_query(
59 ctx: typer.Context,
60 query: str = typer.Argument(..., help="Query expression (see muse code-query --help)."),
61 branch: str | None = typer.Option(None, "--branch", help="Branch to search (default: HEAD branch)."),
62 max_commits: int = typer.Option(200, "--max", help="Maximum commits to inspect."),
63 output_json: bool = typer.Option(False, "--json", help="Emit JSON array of matches."),
64 ) -> None:
65 """Query the code commit history using a structured predicate.
66
67 Walks up to *max* commits from HEAD on the specified branch and returns
68 all commits (and symbol-level changes) matching the predicate.
69
70 Examples::
71
72 muse code-query "symbol == 'parse_query' and change == 'added'"
73 muse code-query "agent_id contains 'claude' and sem_ver_bump == 'major'"
74 muse code-query "file == 'muse/core/store.py'"
75 """
76 root = require_repo()
77
78 try:
79 evaluator = build_evaluator(query)
80 except ValueError as exc:
81 typer.echo(f"❌ Query parse error: {exc}", err=True)
82 raise typer.Exit(code=1) from exc
83
84 resolved_branch = branch or _current_branch(root)
85 matches = walk_history(root, resolved_branch, evaluator, max_commits=max_commits)
86
87 if output_json:
88 typer.echo(json.dumps(list(matches)))
89 return
90
91 typer.echo(format_matches(matches))