code-search-cli 0.1.0__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.
@@ -0,0 +1,6 @@
1
+ """Code Search: task-aware retrieval with explainable evidence bundles."""
2
+
3
+ from .engine import RetrievalEngine
4
+ from .models import Evidence, RetrievalResult
5
+
6
+ __all__ = ["Evidence", "RetrievalEngine", "RetrievalResult"]
@@ -0,0 +1,3 @@
1
+ from .cli import main
2
+
3
+ raise SystemExit(main())
code_retrieval/cli.py ADDED
@@ -0,0 +1,144 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import json
5
+ import sqlite3
6
+ import subprocess
7
+ import sys
8
+
9
+ from .engine import RetrievalEngine
10
+ from .index_store import PersistentIndex
11
+ from .installer import install_codex_skill, uninstall_codex_skill
12
+ from .models import Evidence
13
+ from .repository import RepositoryReader
14
+
15
+
16
+ def build_parser() -> argparse.ArgumentParser:
17
+ parser = argparse.ArgumentParser(prog="code-search", description="Task-aware local code retrieval")
18
+ subparsers = parser.add_subparsers(dest="command", required=True)
19
+
20
+ retrieve = subparsers.add_parser("retrieve", help="build an evidence bundle for a task")
21
+ _repository_args(retrieve)
22
+ retrieve.add_argument("--task", required=True, help="engineering task or failure description")
23
+ retrieve.add_argument("--max-evidence", type=int, default=16)
24
+ retrieve.add_argument("--format", choices=("markdown", "json"), default="markdown")
25
+
26
+ expand = subparsers.add_parser("expand", help="expand a known source anchor")
27
+ _repository_args(expand)
28
+ expand.add_argument("--path", required=True, help="repository-relative source path")
29
+ anchor = expand.add_mutually_exclusive_group()
30
+ anchor.add_argument("--line", type=int)
31
+ anchor.add_argument("--symbol")
32
+ expand.add_argument("--relation", choices=("enclosing", "siblings", "references"), default="enclosing")
33
+ expand.add_argument("--format", choices=("markdown", "json"), default="markdown")
34
+
35
+ index = subparsers.add_parser("index", help="build or incrementally refresh the persistent index")
36
+ _repository_args(index)
37
+ index.add_argument("--format", choices=("text", "json"), default="text")
38
+
39
+ status = subparsers.add_parser("status", help="show persistent index freshness")
40
+ _repository_args(status)
41
+ status.add_argument("--format", choices=("text", "json"), default="text")
42
+
43
+ clean = subparsers.add_parser("clean", help="remove the persistent index for a repository")
44
+ _repository_args(clean)
45
+
46
+ install = subparsers.add_parser("install", help="install the bundled Codex skill")
47
+ install.add_argument("--force", action="store_true", help="replace a locally modified installed skill")
48
+
49
+ uninstall = subparsers.add_parser("uninstall", help="remove the bundled Codex skill")
50
+ uninstall.add_argument("--force", action="store_true", help="remove a locally modified installed skill")
51
+ return parser
52
+
53
+
54
+ def main(argv: list[str] | None = None) -> int:
55
+ args = build_parser().parse_args(argv)
56
+ try:
57
+ if args.command in {"install", "uninstall"}:
58
+ operation = install_codex_skill if args.command == "install" else uninstall_codex_skill
59
+ path, action = operation(force=args.force)
60
+ print(f"Codex skill {action}: {path}")
61
+ if args.command == "install":
62
+ print("Restart Codex to load the skill.")
63
+ return 0
64
+
65
+ if args.command in {"index", "status", "clean"}:
66
+ store = PersistentIndex(RepositoryReader(args.repo, args.ref))
67
+ if args.command == "clean":
68
+ removed = store.clean()
69
+ print(f"Index {'removed' if removed else 'not found'}: {store.path}")
70
+ return 0
71
+ report = store.refresh() if args.command == "index" else store.status()
72
+ data = report.to_dict()
73
+ if args.format == "json":
74
+ print(json.dumps(data, ensure_ascii=False, indent=2))
75
+ else:
76
+ print(_index_text(args.command, data))
77
+ return 0
78
+
79
+ engine = RetrievalEngine(args.repo, args.ref)
80
+ if args.command == "retrieve":
81
+ result = engine.retrieve(args.task, args.max_evidence)
82
+ output = json.dumps(result.to_dict(), ensure_ascii=False, indent=2) if args.format == "json" else result.to_markdown()
83
+ else:
84
+ evidence = engine.expand(
85
+ args.path,
86
+ line=args.line,
87
+ symbol=args.symbol,
88
+ relation=args.relation,
89
+ )
90
+ output = _evidence_json(evidence) if args.format == "json" else _evidence_markdown(evidence)
91
+ print(output)
92
+ return 0
93
+ except (OSError, ValueError, sqlite3.Error, subprocess.SubprocessError) as error:
94
+ print(f"error: {error}", file=sys.stderr)
95
+ return 2
96
+
97
+
98
+ def _repository_args(parser: argparse.ArgumentParser) -> None:
99
+ parser.add_argument("--repo", required=True, help="local repository path")
100
+ parser.add_argument("--ref", help="optional Git commit, tag, or branch; does not modify the working tree")
101
+
102
+
103
+ def _evidence_json(evidence: list[Evidence]) -> str:
104
+ from dataclasses import asdict
105
+
106
+ return json.dumps([asdict(item) for item in evidence], ensure_ascii=False, indent=2)
107
+
108
+
109
+ def _evidence_markdown(evidence: list[Evidence]) -> str:
110
+ lines = ["# Expanded evidence", ""]
111
+ for item in evidence:
112
+ lines.extend(
113
+ [
114
+ f"## `{item.path}:{item.start_line}-{item.end_line}`",
115
+ "",
116
+ "; ".join(item.reasons),
117
+ "",
118
+ "```",
119
+ item.content.rstrip(),
120
+ "```",
121
+ "",
122
+ ]
123
+ )
124
+ return "\n".join(lines)
125
+
126
+
127
+ def _index_text(command: str, data: dict[str, object]) -> str:
128
+ if command == "status":
129
+ state = "fresh" if data["fresh"] else ("stale" if data["exists"] else "not built")
130
+ return (
131
+ f"Index {state}: {data['index_path']}\n"
132
+ f"Source files: {data['source_files']}; pending +{data['added']} "
133
+ f"~{data['updated']} -{data['removed']}"
134
+ )
135
+ return (
136
+ f"Index refreshed: {data['index_path']}\n"
137
+ f"Source files: {data['source_files']}; +{data['added']} "
138
+ f"~{data['updated']} -{data['removed']}; unchanged {data['unchanged']}; "
139
+ f"{data['elapsed_ms']} ms"
140
+ )
141
+
142
+
143
+ if __name__ == "__main__":
144
+ raise SystemExit(main())