rlmgrep 0.1.2__py3-none-any.whl → 0.1.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rlmgrep/__init__.py +1 -1
- rlmgrep/cli.py +10 -4
- rlmgrep/render.py +33 -15
- rlmgrep/rlm.py +19 -1
- {rlmgrep-0.1.2.dist-info → rlmgrep-0.1.4.dist-info}/METADATA +7 -7
- rlmgrep-0.1.4.dist-info/RECORD +14 -0
- rlmgrep-0.1.2.dist-info/RECORD +0 -14
- {rlmgrep-0.1.2.dist-info → rlmgrep-0.1.4.dist-info}/WHEEL +0 -0
- {rlmgrep-0.1.2.dist-info → rlmgrep-0.1.4.dist-info}/entry_points.txt +0 -0
- {rlmgrep-0.1.2.dist-info → rlmgrep-0.1.4.dist-info}/top_level.txt +0 -0
rlmgrep/__init__.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
__all__ = ["__version__"]
|
|
2
|
-
__version__ = "0.1.
|
|
2
|
+
__version__ = "0.1.4"
|
rlmgrep/cli.py
CHANGED
|
@@ -6,6 +6,7 @@ import sys
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
|
|
8
8
|
import dspy
|
|
9
|
+
from . import __version__
|
|
9
10
|
from .config import ensure_default_config, load_config
|
|
10
11
|
from .file_map import build_file_map
|
|
11
12
|
from .ingest import FileRecord, collect_candidates, load_files, resolve_type_exts
|
|
@@ -67,14 +68,16 @@ def _parse_args(argv: list[str]) -> argparse.Namespace:
|
|
|
67
68
|
prog="rlmgrep",
|
|
68
69
|
description="Grep-shaped CLI search powered by DSPy RLM.",
|
|
69
70
|
)
|
|
71
|
+
parser.add_argument("--version", action="version", version=f"rlmgrep {__version__}")
|
|
70
72
|
parser.add_argument("pattern", nargs="?", help="Query string (interpreted by RLM)")
|
|
71
73
|
parser.add_argument("paths", nargs="*", help="Files or directories")
|
|
72
74
|
|
|
73
|
-
parser.add_argument("-n", dest="line_numbers", action="store_true", help="Show line numbers")
|
|
74
|
-
parser.add_argument("-
|
|
75
|
+
parser.add_argument("-n", dest="line_numbers", action="store_true", help="Show line numbers (default)")
|
|
76
|
+
parser.add_argument("--no-line-number", dest="line_numbers", action="store_false", help="Hide line numbers")
|
|
77
|
+
parser.add_argument("-H", dest="with_filename", action="store_true", help="Always show filenames (default)")
|
|
75
78
|
parser.add_argument("-r", dest="recursive", action="store_true", help="Recursive (directories are searched recursively by default)")
|
|
76
79
|
parser.add_argument("--no-recursive", dest="recursive", action="store_false", help="Do not recurse directories")
|
|
77
|
-
parser.set_defaults(recursive=True)
|
|
80
|
+
parser.set_defaults(recursive=True, line_numbers=True)
|
|
78
81
|
|
|
79
82
|
parser.add_argument("-C", dest="context", type=int, default=0, help="Context lines before/after")
|
|
80
83
|
parser.add_argument("-A", dest="after", type=int, default=None, help="Context lines after")
|
|
@@ -511,13 +514,16 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
511
514
|
before = args.before if args.before is not None else args.context
|
|
512
515
|
after = args.after if args.after is not None else args.context
|
|
513
516
|
|
|
517
|
+
use_color = sys.stdout.isatty() and not os.getenv("NO_COLOR")
|
|
518
|
+
|
|
514
519
|
output_lines = render_matches(
|
|
515
520
|
files=files,
|
|
516
521
|
matches=verified,
|
|
517
522
|
show_line_numbers=args.line_numbers,
|
|
518
|
-
show_filename=args.with_filename,
|
|
519
523
|
before=before,
|
|
520
524
|
after=after,
|
|
525
|
+
use_color=use_color,
|
|
526
|
+
heading=True,
|
|
521
527
|
)
|
|
522
528
|
|
|
523
529
|
if args.answer:
|
rlmgrep/render.py
CHANGED
|
@@ -2,22 +2,36 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from .ingest import FileRecord
|
|
4
4
|
|
|
5
|
+
COLOR_RESET = "\x1b[0m"
|
|
6
|
+
COLOR_PATH = "\x1b[35m"
|
|
7
|
+
COLOR_LINE_NO = "\x1b[32m"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _colorize(text: str, color: str, use_color: bool) -> str:
|
|
11
|
+
if not use_color:
|
|
12
|
+
return text
|
|
13
|
+
return f"{color}{text}{COLOR_RESET}"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _format_heading(path: str, use_color: bool) -> str:
|
|
17
|
+
if not path.startswith((".", "/")):
|
|
18
|
+
path = f"./{path}"
|
|
19
|
+
return _colorize(path, COLOR_PATH, use_color)
|
|
20
|
+
|
|
5
21
|
|
|
6
22
|
def _format_line(
|
|
7
|
-
path: str,
|
|
8
23
|
line_no: int,
|
|
9
24
|
text: str,
|
|
10
25
|
is_match: bool,
|
|
11
|
-
show_filename: bool,
|
|
12
26
|
show_line_numbers: bool,
|
|
27
|
+
use_color: bool,
|
|
28
|
+
heading: bool,
|
|
13
29
|
) -> str:
|
|
14
30
|
delim = ":" if is_match else "-"
|
|
15
|
-
if show_filename and show_line_numbers:
|
|
16
|
-
return f"{path}{delim}{line_no}{delim}{text}"
|
|
17
|
-
if show_filename:
|
|
18
|
-
return f"{path}{delim}{text}"
|
|
19
31
|
if show_line_numbers:
|
|
20
|
-
|
|
32
|
+
prefix = _colorize(str(line_no), COLOR_LINE_NO, use_color)
|
|
33
|
+
sep = "\t" if heading else ""
|
|
34
|
+
return f"{prefix}{delim}{sep}{text}"
|
|
21
35
|
return text
|
|
22
36
|
|
|
23
37
|
|
|
@@ -39,18 +53,22 @@ def render_matches(
|
|
|
39
53
|
files: dict[str, FileRecord],
|
|
40
54
|
matches: dict[str, list[int]],
|
|
41
55
|
show_line_numbers: bool,
|
|
42
|
-
show_filename: bool,
|
|
43
56
|
before: int,
|
|
44
57
|
after: int,
|
|
58
|
+
use_color: bool = False,
|
|
59
|
+
heading: bool = True,
|
|
45
60
|
) -> list[str]:
|
|
46
61
|
output: list[str] = []
|
|
47
|
-
multiple_files = len(files) > 1
|
|
48
|
-
show_filename = show_filename or multiple_files
|
|
49
62
|
|
|
50
|
-
|
|
63
|
+
paths = sorted(matches.keys())
|
|
64
|
+
for idx, path in enumerate(paths):
|
|
51
65
|
record = files.get(path)
|
|
52
66
|
if record is None:
|
|
53
67
|
continue
|
|
68
|
+
if heading:
|
|
69
|
+
if idx > 0:
|
|
70
|
+
output.append("")
|
|
71
|
+
output.append(_format_heading(path, use_color))
|
|
54
72
|
lines = record.lines
|
|
55
73
|
page_map = record.page_map
|
|
56
74
|
n_lines = len(lines)
|
|
@@ -65,12 +83,12 @@ def render_matches(
|
|
|
65
83
|
text = f"{text}\tpage={page_map[line_no - 1]}"
|
|
66
84
|
output.append(
|
|
67
85
|
_format_line(
|
|
68
|
-
path,
|
|
69
86
|
line_no,
|
|
70
87
|
text,
|
|
71
88
|
True,
|
|
72
|
-
show_filename,
|
|
73
89
|
show_line_numbers,
|
|
90
|
+
use_color,
|
|
91
|
+
heading,
|
|
74
92
|
)
|
|
75
93
|
)
|
|
76
94
|
continue
|
|
@@ -90,12 +108,12 @@ def render_matches(
|
|
|
90
108
|
is_match = line_no in match_set
|
|
91
109
|
output.append(
|
|
92
110
|
_format_line(
|
|
93
|
-
path,
|
|
94
111
|
line_no,
|
|
95
112
|
text,
|
|
96
113
|
is_match,
|
|
97
|
-
show_filename,
|
|
98
114
|
show_line_numbers,
|
|
115
|
+
use_color,
|
|
116
|
+
heading,
|
|
99
117
|
)
|
|
100
118
|
)
|
|
101
119
|
if idx < len(merged) - 1:
|
rlmgrep/rlm.py
CHANGED
|
@@ -24,6 +24,10 @@ class RLMGrepSignature(dspy.Signature):
|
|
|
24
24
|
find relevant lines. Return all relevant matches you can find, avoid duplicates,
|
|
25
25
|
and only use exact paths from the directory keys.
|
|
26
26
|
Always read the ASCII file map first to orient yourself to the available paths.
|
|
27
|
+
Do not wrap code in backticks; only raw Python.
|
|
28
|
+
Do not import pandas or numpy; use built-ins only.
|
|
29
|
+
|
|
30
|
+
Files like "photo.jpg.md" or "audio.mp3.md" are LLM descriptions/transcriptions of images/audio that were originally in the directory but have been converted to md to make them searchable by you.
|
|
27
31
|
"""
|
|
28
32
|
|
|
29
33
|
directory: dict = dspy.InputField(
|
|
@@ -52,7 +56,21 @@ class RLMGrepSignature(dspy.Signature):
|
|
|
52
56
|
|
|
53
57
|
class RLMGrepAnswerSignature(dspy.Signature):
|
|
54
58
|
"""
|
|
55
|
-
|
|
59
|
+
You are the search engine for rlmgrep, a grep-shaped CLI for coding agents.
|
|
60
|
+
Inputs include a directory mapping of files (path -> full text), an ASCII file
|
|
61
|
+
map, and a user query string. Your output must be grep-printable matches as
|
|
62
|
+
(path, line) pairs that point to real lines in the provided texts.
|
|
63
|
+
The query may be natural language or a short pattern; interpret it freely to
|
|
64
|
+
find relevant lines. Return all relevant matches you can find, avoid duplicates,
|
|
65
|
+
and only use exact paths from the directory keys.
|
|
66
|
+
Always read the ASCII file map first to orient yourself to the available paths.
|
|
67
|
+
Do not wrap code in backticks; only raw Python.
|
|
68
|
+
Do not import pandas or numpy; use built-ins only.
|
|
69
|
+
|
|
70
|
+
In this mode you are also responsible for generating a narrative answer to the query based on the provided files.
|
|
71
|
+
|
|
72
|
+
Files like "photo.jpg.md" or "audio.mp3.md" are LLM descriptions/transcriptions of images/audio that were originally in the directory but have been converted to md to make them searchable by you.
|
|
73
|
+
|
|
56
74
|
"""
|
|
57
75
|
|
|
58
76
|
directory: dict = dspy.InputField(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rlmgrep
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: Grep-shaped CLI search powered by DSPy RLM
|
|
5
5
|
Author: rlmgrep
|
|
6
6
|
License: MIT
|
|
@@ -61,8 +61,9 @@ rlmgrep [options] "query" [paths...]
|
|
|
61
61
|
|
|
62
62
|
Common options:
|
|
63
63
|
|
|
64
|
-
- `-n` show line numbers
|
|
65
|
-
-
|
|
64
|
+
- `-n` show line numbers (default)
|
|
65
|
+
- `--no-line-number` hide line numbers
|
|
66
|
+
- `-H` always show filenames (default)
|
|
66
67
|
- `-C N` context lines before/after (grep-style)
|
|
67
68
|
- `-A N` context lines after
|
|
68
69
|
- `-B N` context lines before
|
|
@@ -109,10 +110,9 @@ rg -l "token" . | rlmgrep --stdin-files --answer "what does this token control?"
|
|
|
109
110
|
## Output contract (stable for agents)
|
|
110
111
|
|
|
111
112
|
- Matches are written to stdout; warnings go to stderr.
|
|
112
|
-
- Output uses
|
|
113
|
-
-
|
|
114
|
-
- `
|
|
115
|
-
- If `-H` or `-n` are omitted, their parts are omitted.
|
|
113
|
+
- Output uses rg-style headings by default:
|
|
114
|
+
- A file header line like `./path/to/file`
|
|
115
|
+
- Then `line:\ttext` for matches, `line-\ttext` for context lines
|
|
116
116
|
- Line numbers are 1-based.
|
|
117
117
|
- When context ranges are disjoint, a `--` line separates groups.
|
|
118
118
|
- Exit codes:
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
rlmgrep/__init__.py,sha256=V2k5h8cZ-5Dt0onxqKP5wn7Rn2cmXmsSVcJMQZ9X1sY,48
|
|
2
|
+
rlmgrep/__main__.py,sha256=MHKZ_ae3fSLGTLUUMOx15fWdeOnJSHhq-zslRP5F5Lc,79
|
|
3
|
+
rlmgrep/cli.py,sha256=4qwR5cdQV0H40XZhLXdAO9tzlubeQCgA2sepWrXzKWI,18996
|
|
4
|
+
rlmgrep/config.py,sha256=A6VLuuXSgQ1vM207CP0G92Mg3et93dGSmkkLQ0IOfwk,2388
|
|
5
|
+
rlmgrep/file_map.py,sha256=x2Ri1wzK8_87GUorsAV01K_nYLZcv30yIquDeTCcdEw,876
|
|
6
|
+
rlmgrep/ingest.py,sha256=uCz2el9B-RIT9umFo-gFEdAsmWPP1IJOArFFQY0D_1A,9127
|
|
7
|
+
rlmgrep/interpreter.py,sha256=s_nMRxLlAU9C0JmUzUBW5NbVbuH67doVWF54K54STlA,2478
|
|
8
|
+
rlmgrep/render.py,sha256=mxU7ByarJ6FU8ERt7biPVIOmpZQdKRrdyoZqlngploQ,3558
|
|
9
|
+
rlmgrep/rlm.py,sha256=i3rCTp8OABByF60Un5gO7265gaW4spwU0OFKIz4surg,5750
|
|
10
|
+
rlmgrep-0.1.4.dist-info/METADATA,sha256=s8iJbqKgLZSiUv7q4dkun4f40uDTQOYom3qjE1OMaks,6600
|
|
11
|
+
rlmgrep-0.1.4.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
12
|
+
rlmgrep-0.1.4.dist-info/entry_points.txt,sha256=UV6QkEbkwBO1JJ53mm84_n35tVyOczPvOQ14ga7vrCI,45
|
|
13
|
+
rlmgrep-0.1.4.dist-info/top_level.txt,sha256=gTujSRsO58c80eN7aRH2cfe51FHxx8LJ1w1Y2YlHti0,8
|
|
14
|
+
rlmgrep-0.1.4.dist-info/RECORD,,
|
rlmgrep-0.1.2.dist-info/RECORD
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
rlmgrep/__init__.py,sha256=tXbRXsO0NE_UV1kIHiZTTQQH0fj0U2KoxxNusu_gzrM,48
|
|
2
|
-
rlmgrep/__main__.py,sha256=MHKZ_ae3fSLGTLUUMOx15fWdeOnJSHhq-zslRP5F5Lc,79
|
|
3
|
-
rlmgrep/cli.py,sha256=-R8wosqWIibuWVYNFNQ2NpTh-uRpeQms3OqqBX9vuwk,18653
|
|
4
|
-
rlmgrep/config.py,sha256=A6VLuuXSgQ1vM207CP0G92Mg3et93dGSmkkLQ0IOfwk,2388
|
|
5
|
-
rlmgrep/file_map.py,sha256=x2Ri1wzK8_87GUorsAV01K_nYLZcv30yIquDeTCcdEw,876
|
|
6
|
-
rlmgrep/ingest.py,sha256=uCz2el9B-RIT9umFo-gFEdAsmWPP1IJOArFFQY0D_1A,9127
|
|
7
|
-
rlmgrep/interpreter.py,sha256=s_nMRxLlAU9C0JmUzUBW5NbVbuH67doVWF54K54STlA,2478
|
|
8
|
-
rlmgrep/render.py,sha256=w6KOfont2M7pQz_EEngTFMY5xJEE11N_ko8P9x5FdH8,3097
|
|
9
|
-
rlmgrep/rlm.py,sha256=LZfkyWxjvtf8dwo5JxetKvvpBYeGKhajwHEVpCb2eo4,4474
|
|
10
|
-
rlmgrep-0.1.2.dist-info/METADATA,sha256=9_EA5Gy94JFgSLy9nFuoR1B0auVlhNhIyEBTtUyBTaY,6615
|
|
11
|
-
rlmgrep-0.1.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
12
|
-
rlmgrep-0.1.2.dist-info/entry_points.txt,sha256=UV6QkEbkwBO1JJ53mm84_n35tVyOczPvOQ14ga7vrCI,45
|
|
13
|
-
rlmgrep-0.1.2.dist-info/top_level.txt,sha256=gTujSRsO58c80eN7aRH2cfe51FHxx8LJ1w1Y2YlHti0,8
|
|
14
|
-
rlmgrep-0.1.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|