rlmgrep 0.1.3__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 CHANGED
@@ -1,2 +1,2 @@
1
1
  __all__ = ["__version__"]
2
- __version__ = "0.1.3"
2
+ __version__ = "0.1.4"
rlmgrep/cli.py CHANGED
@@ -72,11 +72,12 @@ def _parse_args(argv: list[str]) -> argparse.Namespace:
72
72
  parser.add_argument("pattern", nargs="?", help="Query string (interpreted by RLM)")
73
73
  parser.add_argument("paths", nargs="*", help="Files or directories")
74
74
 
75
- parser.add_argument("-n", dest="line_numbers", action="store_true", help="Show line numbers")
76
- parser.add_argument("-H", dest="with_filename", action="store_true", help="Always show filenames")
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)")
77
78
  parser.add_argument("-r", dest="recursive", action="store_true", help="Recursive (directories are searched recursively by default)")
78
79
  parser.add_argument("--no-recursive", dest="recursive", action="store_false", help="Do not recurse directories")
79
- parser.set_defaults(recursive=True)
80
+ parser.set_defaults(recursive=True, line_numbers=True)
80
81
 
81
82
  parser.add_argument("-C", dest="context", type=int, default=0, help="Context lines before/after")
82
83
  parser.add_argument("-A", dest="after", type=int, default=None, help="Context lines after")
@@ -513,13 +514,16 @@ def main(argv: list[str] | None = None) -> int:
513
514
  before = args.before if args.before is not None else args.context
514
515
  after = args.after if args.after is not None else args.context
515
516
 
517
+ use_color = sys.stdout.isatty() and not os.getenv("NO_COLOR")
518
+
516
519
  output_lines = render_matches(
517
520
  files=files,
518
521
  matches=verified,
519
522
  show_line_numbers=args.line_numbers,
520
- show_filename=args.with_filename,
521
523
  before=before,
522
524
  after=after,
525
+ use_color=use_color,
526
+ heading=True,
523
527
  )
524
528
 
525
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
- return f"{line_no}{delim}{text}"
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
- for path in sorted(matches.keys()):
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
- Same as RLMGrepSignature, but also return a narrative answer to the query.
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
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
- - `-H` always show filenames
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 grep-like prefixes:
113
- - `path:line:text` for match lines when both `-H` and `-n` are enabled.
114
- - `path-line-text` for context lines (note the `-` separator).
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,,
@@ -1,14 +0,0 @@
1
- rlmgrep/__init__.py,sha256=cVZBCfo6mJZvGFsStEhk2sSrk77IfDImcTVxgYIhNmY,48
2
- rlmgrep/__main__.py,sha256=MHKZ_ae3fSLGTLUUMOx15fWdeOnJSHhq-zslRP5F5Lc,79
3
- rlmgrep/cli.py,sha256=wR9zJAzkp8jl42zMHL19r4oCxGKfN6K72-JzmQlUS74,18768
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.3.dist-info/METADATA,sha256=RuGjNIucLiFErCBf4KnH4An7lhgUE5vLIT3WwtmCBEY,6615
11
- rlmgrep-0.1.3.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
12
- rlmgrep-0.1.3.dist-info/entry_points.txt,sha256=UV6QkEbkwBO1JJ53mm84_n35tVyOczPvOQ14ga7vrCI,45
13
- rlmgrep-0.1.3.dist-info/top_level.txt,sha256=gTujSRsO58c80eN7aRH2cfe51FHxx8LJ1w1Y2YlHti0,8
14
- rlmgrep-0.1.3.dist-info/RECORD,,