codebase-brief 0.1.1__tar.gz → 0.1.3__tar.gz
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.
- {codebase_brief-0.1.1 → codebase_brief-0.1.3}/CHANGELOG.md +11 -0
- {codebase_brief-0.1.1 → codebase_brief-0.1.3}/PKG-INFO +12 -6
- {codebase_brief-0.1.1 → codebase_brief-0.1.3}/README.md +11 -5
- {codebase_brief-0.1.1 → codebase_brief-0.1.3}/codebase_brief.egg-info/PKG-INFO +12 -6
- {codebase_brief-0.1.1 → codebase_brief-0.1.3}/context_inject/cli.py +153 -5
- {codebase_brief-0.1.1 → codebase_brief-0.1.3}/pyproject.toml +1 -1
- {codebase_brief-0.1.1 → codebase_brief-0.1.3}/tests/test_cli.py +37 -2
- {codebase_brief-0.1.1 → codebase_brief-0.1.3}/CONTRIBUTING.md +0 -0
- {codebase_brief-0.1.1 → codebase_brief-0.1.3}/LICENSE +0 -0
- {codebase_brief-0.1.1 → codebase_brief-0.1.3}/MANIFEST.in +0 -0
- {codebase_brief-0.1.1 → codebase_brief-0.1.3}/codebase_brief.egg-info/SOURCES.txt +0 -0
- {codebase_brief-0.1.1 → codebase_brief-0.1.3}/codebase_brief.egg-info/dependency_links.txt +0 -0
- {codebase_brief-0.1.1 → codebase_brief-0.1.3}/codebase_brief.egg-info/entry_points.txt +0 -0
- {codebase_brief-0.1.1 → codebase_brief-0.1.3}/codebase_brief.egg-info/top_level.txt +0 -0
- {codebase_brief-0.1.1 → codebase_brief-0.1.3}/context_inject/__init__.py +0 -0
- {codebase_brief-0.1.1 → codebase_brief-0.1.3}/context_inject/__main__.py +0 -0
- {codebase_brief-0.1.1 → codebase_brief-0.1.3}/setup.cfg +0 -0
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.3
|
|
4
|
+
|
|
5
|
+
- Find task-matching snippets deeper inside selected source files and show their line ranges instead of always starting at the file's beginning.
|
|
6
|
+
- Keep excerpt reads bounded and apply the existing secret redaction before including source snippets.
|
|
7
|
+
|
|
8
|
+
## 0.1.2
|
|
9
|
+
|
|
10
|
+
- Add `brief --demo`, which generates a real example brief from temporary sample files without reading or modifying the user's project.
|
|
11
|
+
- Clarify how to try the demo and install the CLI when pipx is not already available.
|
|
12
|
+
- Show the menu's actual `0` exit key correctly in the README.
|
|
13
|
+
|
|
3
14
|
## 0.1.1
|
|
4
15
|
|
|
5
16
|
- Clarify the product's best-fit use case as browser-based coding chats that cannot inspect a local working tree.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codebase-brief
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Local-first CLI that builds task-focused context briefs from code repositories
|
|
5
5
|
Author: Mohamed Maggy
|
|
6
6
|
License-Expression: MIT
|
|
@@ -53,7 +53,7 @@ The first image is a terminal-style rendering of the app's actual numbered menu.
|
|
|
53
53
|
|
|
54
54
|
You need Python 3.10 or newer. Git is optional, but required for recent-history and base-diff details.
|
|
55
55
|
|
|
56
|
-
The current PyPI release is `0.1.
|
|
56
|
+
The current PyPI release is `0.1.3` and includes the review-feedback summary, importer-aware `--base` selection, and a sample demo. Install it with `pipx`:
|
|
57
57
|
|
|
58
58
|
```powershell
|
|
59
59
|
pipx install codebase-brief
|
|
@@ -62,6 +62,10 @@ brief "C:\work\my-app" --task "Trace the login flow" --copy
|
|
|
62
62
|
|
|
63
63
|
The brief is copied to your clipboard for pasting into your assistant. Replace the example path and task with yours. To use the numbered menu, run `brief` with no arguments.
|
|
64
64
|
|
|
65
|
+
Want to see a real example before choosing your project? Run `brief --demo`. It uses a tiny temporary example project and does not read or change your files.
|
|
66
|
+
|
|
67
|
+
If you do not have `pipx` installed, skip the command above and use the virtual-environment steps below instead; they work with the project's minimum Python version. To install `pipx` first, follow its [official installation guide](https://pipx.pypa.io/latest/how-to/install-pipx.html). The current pipx installer requires Python 3.11 or newer, while Codebase Brief itself supports Python 3.10 and newer.
|
|
68
|
+
|
|
65
69
|
Or use a virtual environment:
|
|
66
70
|
|
|
67
71
|
```powershell
|
|
@@ -79,13 +83,15 @@ If the `brief` command is not available, use `python -m context_inject` while th
|
|
|
79
83
|
|
|
80
84
|
When the menu opens, it uses the folder you launched it from as the starting project. Type a number and press Enter:
|
|
81
85
|
|
|
82
|
-
1
|
|
83
|
-
2
|
|
84
|
-
3
|
|
85
|
-
|
|
86
|
+
- **1 — Build a context brief:** Describe the coding task, optionally enter a Git base ref, then accept or change the approximate size limit.
|
|
87
|
+
- **2 — Choose a different project:** Point the tool at another local repository or source folder.
|
|
88
|
+
- **3 — Help and examples:** See the workflow and an example command.
|
|
89
|
+
- **0 — Exit:** Close the menu.
|
|
86
90
|
|
|
87
91
|
In plain terms, Codebase Brief makes a small guide to a project so an AI coding assistant can get oriented before helping with a task. It does not write or fix code. It reads project information locally and prepares a guide for you to review and share.
|
|
88
92
|
|
|
93
|
+
For selected source files, it looks for code near words in your task and labels each excerpt with its source line range. This is a bounded, best-effort search: if it finds no clear match, it falls back to a general excerpt. Check the cited lines in your editor before relying on them.
|
|
94
|
+
|
|
89
95
|
Before generating the brief, the tool shows suggested files and why they were selected. Press Enter to keep the checked files, type file numbers to toggle selections, or use `add path/to/file.py` to include another safe text file from the repository. Type `none` for an overview without source excerpts. After the brief is shown, saved, or copied, Codebase Brief prints a copyable summary of files kept, removed, added from suggestions, and manually added. To help improve file selection, paste that summary into the [tester feedback form](https://github.com/mohamed1111111m/codebase-brief/issues/new?template=tester-feedback.yml) along with your task and whether the brief helped. Do not include private source code or secrets. The menu's save-here choice writes `context.md` in the folder where you launched the menu; if that is the target repository, the file will be created there. Choose the show or copy option to avoid writing a file into that folder.
|
|
90
96
|
|
|
91
97
|
For your first try, choose a project you know and a task you understand. Check whether the suggested files are the ones you would have shown a coding assistant yourself.
|
|
@@ -28,7 +28,7 @@ The first image is a terminal-style rendering of the app's actual numbered menu.
|
|
|
28
28
|
|
|
29
29
|
You need Python 3.10 or newer. Git is optional, but required for recent-history and base-diff details.
|
|
30
30
|
|
|
31
|
-
The current PyPI release is `0.1.
|
|
31
|
+
The current PyPI release is `0.1.3` and includes the review-feedback summary, importer-aware `--base` selection, and a sample demo. Install it with `pipx`:
|
|
32
32
|
|
|
33
33
|
```powershell
|
|
34
34
|
pipx install codebase-brief
|
|
@@ -37,6 +37,10 @@ brief "C:\work\my-app" --task "Trace the login flow" --copy
|
|
|
37
37
|
|
|
38
38
|
The brief is copied to your clipboard for pasting into your assistant. Replace the example path and task with yours. To use the numbered menu, run `brief` with no arguments.
|
|
39
39
|
|
|
40
|
+
Want to see a real example before choosing your project? Run `brief --demo`. It uses a tiny temporary example project and does not read or change your files.
|
|
41
|
+
|
|
42
|
+
If you do not have `pipx` installed, skip the command above and use the virtual-environment steps below instead; they work with the project's minimum Python version. To install `pipx` first, follow its [official installation guide](https://pipx.pypa.io/latest/how-to/install-pipx.html). The current pipx installer requires Python 3.11 or newer, while Codebase Brief itself supports Python 3.10 and newer.
|
|
43
|
+
|
|
40
44
|
Or use a virtual environment:
|
|
41
45
|
|
|
42
46
|
```powershell
|
|
@@ -54,13 +58,15 @@ If the `brief` command is not available, use `python -m context_inject` while th
|
|
|
54
58
|
|
|
55
59
|
When the menu opens, it uses the folder you launched it from as the starting project. Type a number and press Enter:
|
|
56
60
|
|
|
57
|
-
1
|
|
58
|
-
2
|
|
59
|
-
3
|
|
60
|
-
|
|
61
|
+
- **1 — Build a context brief:** Describe the coding task, optionally enter a Git base ref, then accept or change the approximate size limit.
|
|
62
|
+
- **2 — Choose a different project:** Point the tool at another local repository or source folder.
|
|
63
|
+
- **3 — Help and examples:** See the workflow and an example command.
|
|
64
|
+
- **0 — Exit:** Close the menu.
|
|
61
65
|
|
|
62
66
|
In plain terms, Codebase Brief makes a small guide to a project so an AI coding assistant can get oriented before helping with a task. It does not write or fix code. It reads project information locally and prepares a guide for you to review and share.
|
|
63
67
|
|
|
68
|
+
For selected source files, it looks for code near words in your task and labels each excerpt with its source line range. This is a bounded, best-effort search: if it finds no clear match, it falls back to a general excerpt. Check the cited lines in your editor before relying on them.
|
|
69
|
+
|
|
64
70
|
Before generating the brief, the tool shows suggested files and why they were selected. Press Enter to keep the checked files, type file numbers to toggle selections, or use `add path/to/file.py` to include another safe text file from the repository. Type `none` for an overview without source excerpts. After the brief is shown, saved, or copied, Codebase Brief prints a copyable summary of files kept, removed, added from suggestions, and manually added. To help improve file selection, paste that summary into the [tester feedback form](https://github.com/mohamed1111111m/codebase-brief/issues/new?template=tester-feedback.yml) along with your task and whether the brief helped. Do not include private source code or secrets. The menu's save-here choice writes `context.md` in the folder where you launched the menu; if that is the target repository, the file will be created there. Choose the show or copy option to avoid writing a file into that folder.
|
|
65
71
|
|
|
66
72
|
For your first try, choose a project you know and a task you understand. Check whether the suggested files are the ones you would have shown a coding assistant yourself.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codebase-brief
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Local-first CLI that builds task-focused context briefs from code repositories
|
|
5
5
|
Author: Mohamed Maggy
|
|
6
6
|
License-Expression: MIT
|
|
@@ -53,7 +53,7 @@ The first image is a terminal-style rendering of the app's actual numbered menu.
|
|
|
53
53
|
|
|
54
54
|
You need Python 3.10 or newer. Git is optional, but required for recent-history and base-diff details.
|
|
55
55
|
|
|
56
|
-
The current PyPI release is `0.1.
|
|
56
|
+
The current PyPI release is `0.1.3` and includes the review-feedback summary, importer-aware `--base` selection, and a sample demo. Install it with `pipx`:
|
|
57
57
|
|
|
58
58
|
```powershell
|
|
59
59
|
pipx install codebase-brief
|
|
@@ -62,6 +62,10 @@ brief "C:\work\my-app" --task "Trace the login flow" --copy
|
|
|
62
62
|
|
|
63
63
|
The brief is copied to your clipboard for pasting into your assistant. Replace the example path and task with yours. To use the numbered menu, run `brief` with no arguments.
|
|
64
64
|
|
|
65
|
+
Want to see a real example before choosing your project? Run `brief --demo`. It uses a tiny temporary example project and does not read or change your files.
|
|
66
|
+
|
|
67
|
+
If you do not have `pipx` installed, skip the command above and use the virtual-environment steps below instead; they work with the project's minimum Python version. To install `pipx` first, follow its [official installation guide](https://pipx.pypa.io/latest/how-to/install-pipx.html). The current pipx installer requires Python 3.11 or newer, while Codebase Brief itself supports Python 3.10 and newer.
|
|
68
|
+
|
|
65
69
|
Or use a virtual environment:
|
|
66
70
|
|
|
67
71
|
```powershell
|
|
@@ -79,13 +83,15 @@ If the `brief` command is not available, use `python -m context_inject` while th
|
|
|
79
83
|
|
|
80
84
|
When the menu opens, it uses the folder you launched it from as the starting project. Type a number and press Enter:
|
|
81
85
|
|
|
82
|
-
1
|
|
83
|
-
2
|
|
84
|
-
3
|
|
85
|
-
|
|
86
|
+
- **1 — Build a context brief:** Describe the coding task, optionally enter a Git base ref, then accept or change the approximate size limit.
|
|
87
|
+
- **2 — Choose a different project:** Point the tool at another local repository or source folder.
|
|
88
|
+
- **3 — Help and examples:** See the workflow and an example command.
|
|
89
|
+
- **0 — Exit:** Close the menu.
|
|
86
90
|
|
|
87
91
|
In plain terms, Codebase Brief makes a small guide to a project so an AI coding assistant can get oriented before helping with a task. It does not write or fix code. It reads project information locally and prepares a guide for you to review and share.
|
|
88
92
|
|
|
93
|
+
For selected source files, it looks for code near words in your task and labels each excerpt with its source line range. This is a bounded, best-effort search: if it finds no clear match, it falls back to a general excerpt. Check the cited lines in your editor before relying on them.
|
|
94
|
+
|
|
89
95
|
Before generating the brief, the tool shows suggested files and why they were selected. Press Enter to keep the checked files, type file numbers to toggle selections, or use `add path/to/file.py` to include another safe text file from the repository. Type `none` for an overview without source excerpts. After the brief is shown, saved, or copied, Codebase Brief prints a copyable summary of files kept, removed, added from suggestions, and manually added. To help improve file selection, paste that summary into the [tester feedback form](https://github.com/mohamed1111111m/codebase-brief/issues/new?template=tester-feedback.yml) along with your task and whether the brief helped. Do not include private source code or secrets. The menu's save-here choice writes `context.md` in the folder where you launched the menu; if that is the target repository, the file will be created there. Choose the show or copy option to avoid writing a file into that folder.
|
|
90
96
|
|
|
91
97
|
For your first try, choose a project you know and a task you understand. Check whether the suggested files are the ones you would have shown a coding assistant yourself.
|
|
@@ -11,6 +11,7 @@ import re
|
|
|
11
11
|
import shutil
|
|
12
12
|
import subprocess
|
|
13
13
|
import sys
|
|
14
|
+
import tempfile
|
|
14
15
|
from pathlib import Path
|
|
15
16
|
|
|
16
17
|
CHARS_PER_TOKEN = 4 # rough estimate; real token counts vary by model and text
|
|
@@ -23,7 +24,7 @@ MAX_KEY_FILES = 12
|
|
|
23
24
|
MAX_SELECTION_CHOICES = 30
|
|
24
25
|
MAX_DISCOVERED_FILES = 20_000
|
|
25
26
|
IMPORT_SUFFIXES = {".py", ".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs", ".mts", ".cts"}
|
|
26
|
-
VERSION = "0.1.
|
|
27
|
+
VERSION = "0.1.3"
|
|
27
28
|
MANIFESTS = (
|
|
28
29
|
"package.json", "pyproject.toml", "requirements.txt", "Cargo.toml",
|
|
29
30
|
"go.mod", "Gemfile", "composer.json", "pom.xml",
|
|
@@ -202,6 +203,103 @@ def task_words(task: str) -> set[str]:
|
|
|
202
203
|
return {word.lower() for word in re.findall(r"[A-Za-z][A-Za-z0-9_-]{2,}", task)}
|
|
203
204
|
|
|
204
205
|
|
|
206
|
+
_TASK_STOP_WORDS = {
|
|
207
|
+
"about", "after", "also", "and", "are", "does", "from", "give", "help",
|
|
208
|
+
"how", "into", "its", "make", "show", "that", "the", "this", "what", "with",
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def read_task_excerpt(
|
|
213
|
+
root: Path, relative: str, task: str, limit: int = 1050,
|
|
214
|
+
max_bytes: int = 96_000,
|
|
215
|
+
) -> str | None:
|
|
216
|
+
"""Find compact, task-related source windows, including later definitions.
|
|
217
|
+
|
|
218
|
+
File ranking alone is not enough: returning only the first lines of a large
|
|
219
|
+
file can omit the function that made it relevant. Reads remain bounded and
|
|
220
|
+
pass through the same path and secret safeguards as ordinary excerpts.
|
|
221
|
+
"""
|
|
222
|
+
path = safe_path(root, relative)
|
|
223
|
+
if path is None:
|
|
224
|
+
return None
|
|
225
|
+
try:
|
|
226
|
+
with path.open("rb") as source:
|
|
227
|
+
raw = source.read(max_bytes + 1)
|
|
228
|
+
except OSError:
|
|
229
|
+
return None
|
|
230
|
+
if b"\0" in raw[:4096]:
|
|
231
|
+
return None
|
|
232
|
+
|
|
233
|
+
truncated = len(raw) > max_bytes
|
|
234
|
+
text = raw[:max_bytes].decode("utf-8", errors="replace")
|
|
235
|
+
all_lines = text.splitlines()
|
|
236
|
+
if not all_lines:
|
|
237
|
+
return None
|
|
238
|
+
lines = [line[:500] for line in all_lines[:1500]]
|
|
239
|
+
truncated = truncated or len(all_lines) > len(lines) or any(len(line) > 500 for line in all_lines[:1500])
|
|
240
|
+
terms = {term for term in task_words(task) if term not in _TASK_STOP_WORDS}
|
|
241
|
+
# Split snake_case and hyphenated identifiers so "demo" can match
|
|
242
|
+
# generate_demo_brief without requiring an exact function-name query.
|
|
243
|
+
tokenized_lines = [
|
|
244
|
+
{part.lower() for part in re.findall(r"[A-Za-z][A-Za-z0-9]*", line)}
|
|
245
|
+
for line in lines
|
|
246
|
+
]
|
|
247
|
+
frequencies = {
|
|
248
|
+
term: sum(term in line_terms for line_terms in tokenized_lines)
|
|
249
|
+
for term in terms
|
|
250
|
+
}
|
|
251
|
+
scores: list[float] = []
|
|
252
|
+
for line_terms in tokenized_lines:
|
|
253
|
+
scores.append(sum(
|
|
254
|
+
1 + math.log((len(lines) + 1) / (frequencies[term] + 1))
|
|
255
|
+
for term in terms if term in line_terms
|
|
256
|
+
))
|
|
257
|
+
for index, line in enumerate(lines):
|
|
258
|
+
if scores[index] and re.match(r"\s*(?:async\s+)?(?:def|class)\s+", line):
|
|
259
|
+
# In source files, a matching function/class declaration is a
|
|
260
|
+
# stronger anchor than a help string that merely mentions the task.
|
|
261
|
+
scores[index] += 8
|
|
262
|
+
|
|
263
|
+
if not any(scores):
|
|
264
|
+
return compact(redact("\n".join(lines)), limit)
|
|
265
|
+
|
|
266
|
+
# Make short windows around the strongest matching lines. Merge overlaps,
|
|
267
|
+
# then take the best windows until the excerpt budget is used.
|
|
268
|
+
candidates = sorted(range(len(lines)), key=lambda index: (-scores[index], index))
|
|
269
|
+
windows: list[tuple[int, int]] = []
|
|
270
|
+
for index in candidates:
|
|
271
|
+
if scores[index] == 0:
|
|
272
|
+
break
|
|
273
|
+
start, end = max(0, index - 4), min(len(lines), index + 11)
|
|
274
|
+
if any(start <= old_end and end >= old_start for old_start, old_end in windows):
|
|
275
|
+
continue
|
|
276
|
+
windows.append((start, end))
|
|
277
|
+
selected: list[tuple[int, int, str]] = []
|
|
278
|
+
used = 0
|
|
279
|
+
for start, end in windows:
|
|
280
|
+
body = "\n".join(lines[start:end]).strip()
|
|
281
|
+
snippet = f"Lines {start + 1}–{end}:\n{body}"
|
|
282
|
+
if not body:
|
|
283
|
+
continue
|
|
284
|
+
remaining = limit - used
|
|
285
|
+
if remaining <= 0:
|
|
286
|
+
break
|
|
287
|
+
if len(snippet) > remaining:
|
|
288
|
+
break
|
|
289
|
+
selected.append((start, end, snippet))
|
|
290
|
+
used += len(snippet) + 2
|
|
291
|
+
if not selected:
|
|
292
|
+
return compact("\n".join(lines), limit)
|
|
293
|
+
|
|
294
|
+
selected.sort(key=lambda item: item[0])
|
|
295
|
+
result = "\n…\n".join(snippet for _start, _end, snippet in selected)
|
|
296
|
+
if truncated:
|
|
297
|
+
result += "\nMore of this file was left out to keep the brief short."
|
|
298
|
+
elif len(selected) < len(windows):
|
|
299
|
+
result += "\nOther matching sections were left out to keep the brief short."
|
|
300
|
+
return redact(result)
|
|
301
|
+
|
|
302
|
+
|
|
205
303
|
def discover_files(root: Path) -> list[str]:
|
|
206
304
|
found: list[str] = []
|
|
207
305
|
visited_dirs = 0
|
|
@@ -815,12 +913,14 @@ def gather(
|
|
|
815
913
|
if label and label != "additional project file":
|
|
816
914
|
key.append(f"{relative} is already included above; it was also {label}.")
|
|
817
915
|
continue
|
|
818
|
-
excerpt =
|
|
916
|
+
excerpt = read_task_excerpt(root, relative, task)
|
|
917
|
+
if excerpt is None:
|
|
918
|
+
fallback = read_excerpt(root, relative)
|
|
919
|
+
excerpt = fallback[0] if fallback else None
|
|
819
920
|
if excerpt:
|
|
820
|
-
content
|
|
821
|
-
content = compact(content, 1050)
|
|
921
|
+
content = compact(excerpt, 1050)
|
|
822
922
|
label = reasons.get(relative, "selected by project signals")
|
|
823
|
-
shortened_note = "" if "Some lines were left out" in content
|
|
923
|
+
shortened_note = "" if "Some lines were left out" in content or "More of this file was left out" in content else ""
|
|
824
924
|
key.append(f"{relative} — {label}\n{excerpt_for_brief(relative, content)}" + shortened_note)
|
|
825
925
|
if not key:
|
|
826
926
|
if selected_files is not None and not key_files:
|
|
@@ -943,6 +1043,46 @@ def terminal_supports_color() -> bool:
|
|
|
943
1043
|
return False
|
|
944
1044
|
|
|
945
1045
|
|
|
1046
|
+
DEMO_TASK = "Trace how a sign-in request is checked"
|
|
1047
|
+
DEMO_FILES = {
|
|
1048
|
+
"README.md": (
|
|
1049
|
+
"# Sample Shop\n\n"
|
|
1050
|
+
"A tiny example storefront. Its sign-in route checks a password through a small auth helper.\n"
|
|
1051
|
+
),
|
|
1052
|
+
"pyproject.toml": (
|
|
1053
|
+
"[project]\nname = 'sample-shop'\nversion = '0.1.0'\n"
|
|
1054
|
+
"requires-python = '>=3.10'\ndependencies = []\n"
|
|
1055
|
+
),
|
|
1056
|
+
"src/sample_shop/auth.py": (
|
|
1057
|
+
"def check_password(saved_password: str, supplied_password: str) -> bool:\n"
|
|
1058
|
+
" return saved_password == supplied_password\n"
|
|
1059
|
+
),
|
|
1060
|
+
"src/sample_shop/routes.py": (
|
|
1061
|
+
"from .auth import check_password\n\n"
|
|
1062
|
+
"def sign_in(saved_password: str, supplied_password: str) -> str:\n"
|
|
1063
|
+
" if check_password(saved_password, supplied_password):\n"
|
|
1064
|
+
" return 'Welcome back'\n"
|
|
1065
|
+
" return 'Password did not match'\n"
|
|
1066
|
+
),
|
|
1067
|
+
"tests/test_auth.py": (
|
|
1068
|
+
"from sample_shop.auth import check_password\n\n"
|
|
1069
|
+
"def test_matching_password_is_accepted():\n"
|
|
1070
|
+
" assert check_password('correct horse', 'correct horse')\n"
|
|
1071
|
+
),
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
|
|
1075
|
+
def generate_demo_brief() -> str:
|
|
1076
|
+
"""Generate a real sample brief without reading or changing the user's project."""
|
|
1077
|
+
with tempfile.TemporaryDirectory(prefix="codebase-brief-demo-") as temporary:
|
|
1078
|
+
root = Path(temporary)
|
|
1079
|
+
for relative, content in DEMO_FILES.items():
|
|
1080
|
+
path = root / relative
|
|
1081
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
1082
|
+
path.write_text(content, encoding="utf-8")
|
|
1083
|
+
return gather(root, DEMO_TASK, budget_tokens=1_200)
|
|
1084
|
+
|
|
1085
|
+
|
|
946
1086
|
def interactive() -> int:
|
|
947
1087
|
"""Run the guided, numbered terminal interface."""
|
|
948
1088
|
repo = Path.cwd().resolve()
|
|
@@ -1036,6 +1176,7 @@ def interactive() -> int:
|
|
|
1036
1176
|
print("\nA base ref only helps when your current Git commit differs from that ref. Matching covers static imports, literal dynamic imports, and simple TS/JS aliases; runtime-built names may be missed.")
|
|
1037
1177
|
print(f"The default guide size is about {DEFAULT_BUDGET_TOKENS:,} tokens ({CHARS_PER_TOKEN} characters per token, roughly).")
|
|
1038
1178
|
print("Save creates a Markdown file; copy puts the guide on your clipboard only when you choose it.")
|
|
1179
|
+
print("Run 'brief --demo' to see a sample brief without using your project.")
|
|
1039
1180
|
print('\nExample: codebase-brief "C:\\work\\my-app" --task "Find login handling" --base main --budget 2400 --copy')
|
|
1040
1181
|
input("\nPress Enter to return to the menu...")
|
|
1041
1182
|
continue
|
|
@@ -1135,7 +1276,14 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
1135
1276
|
parser.add_argument("--output", help="Write Markdown to this path (default: stdout)")
|
|
1136
1277
|
parser.add_argument("--copy", action="store_true", help="Copy the generated Markdown to your system clipboard")
|
|
1137
1278
|
parser.add_argument("-i", "--interactive", action="store_true", help="Open the numbered interactive menu")
|
|
1279
|
+
parser.add_argument("--demo", action="store_true", help="Print a real sample brief without reading your project")
|
|
1138
1280
|
args = parser.parse_args(argv)
|
|
1281
|
+
if args.demo:
|
|
1282
|
+
if args.interactive or args.repo_path or args.task or args.base or args.output or args.copy or args.budget != DEFAULT_BUDGET_TOKENS:
|
|
1283
|
+
parser.error("--demo cannot be combined with repo_path, --task, --base, --budget, --output, --copy, or --interactive")
|
|
1284
|
+
print("Preview from a built-in sample project. Your project folder was not read or changed.\n")
|
|
1285
|
+
sys.stdout.write(generate_demo_brief())
|
|
1286
|
+
return 0
|
|
1139
1287
|
if args.interactive:
|
|
1140
1288
|
if args.repo_path or args.task or args.base or args.output or args.copy or args.budget != DEFAULT_BUDGET_TOKENS:
|
|
1141
1289
|
parser.error("--interactive cannot be combined with repo_path, --task, --base, --budget, --output, or --copy")
|
|
@@ -6,7 +6,7 @@ import unittest
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from unittest.mock import patch
|
|
8
8
|
|
|
9
|
-
from context_inject.cli import DEFAULT_BUDGET_TOKENS, MAX_CHARS, MAX_FILE_BYTES, MAX_KEY_FILES, choose_files, compact, copy_to_clipboard, gather, interactive, local_imports, main, read_excerpt, redact, review_feedback, safe_path, suggest_file_details, suggest_files
|
|
9
|
+
from context_inject.cli import DEFAULT_BUDGET_TOKENS, MAX_CHARS, MAX_FILE_BYTES, MAX_KEY_FILES, choose_files, compact, copy_to_clipboard, gather, interactive, local_imports, main, read_excerpt, read_task_excerpt, redact, review_feedback, safe_path, suggest_file_details, suggest_files
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class ContextInjectTests(unittest.TestCase):
|
|
@@ -75,6 +75,21 @@ class ContextInjectTests(unittest.TestCase):
|
|
|
75
75
|
self.assertLess(output.index("Files that may help"), output.index("Technology and commands"))
|
|
76
76
|
self.assertLessEqual(len(output), 1000 * 4)
|
|
77
77
|
|
|
78
|
+
def test_task_excerpt_finds_relevant_function_beyond_file_prefix(self):
|
|
79
|
+
filler = "\n".join(f"def unrelated_{index}(): return {index}" for index in range(180))
|
|
80
|
+
source = filler + "\n\ndef build_demo_brief():\n return gather(sample_files, task)\n"
|
|
81
|
+
self.put("src/app.py", source)
|
|
82
|
+
|
|
83
|
+
excerpt = read_task_excerpt(
|
|
84
|
+
self.root, "src/app.py",
|
|
85
|
+
"Explain how the built-in demo creates a sample context brief",
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
self.assertIsNotNone(excerpt)
|
|
89
|
+
self.assertIn("def build_demo_brief", excerpt)
|
|
90
|
+
self.assertIn("return gather(sample_files, task)", excerpt)
|
|
91
|
+
self.assertNotIn("def unrelated_0", excerpt)
|
|
92
|
+
|
|
78
93
|
def test_excerpt_shortening_stops_on_a_word_boundary(self):
|
|
79
94
|
excerpt = compact("This is a long sentence that should end on a complete word. " * 3, 90)
|
|
80
95
|
self.assertTrue(excerpt.startswith("This is a long sentence that should\nSome lines were left out"))
|
|
@@ -222,6 +237,26 @@ class ContextInjectTests(unittest.TestCase):
|
|
|
222
237
|
self.assertIn("First use:", stdout.getvalue())
|
|
223
238
|
self.assertIn("--budget", stdout.getvalue())
|
|
224
239
|
self.assertIn("--copy", stdout.getvalue())
|
|
240
|
+
self.assertIn("--demo", stdout.getvalue())
|
|
241
|
+
|
|
242
|
+
def test_demo_generates_real_brief_without_reading_or_changing_current_folder(self):
|
|
243
|
+
output = io.StringIO()
|
|
244
|
+
before = set(self.root.rglob("*"))
|
|
245
|
+
with patch("pathlib.Path.cwd", return_value=self.root), contextlib.redirect_stdout(output):
|
|
246
|
+
self.assertEqual(main(["--demo"]), 0)
|
|
247
|
+
brief = output.getvalue()
|
|
248
|
+
self.assertIn("Trace how a sign-in request is checked", brief)
|
|
249
|
+
self.assertIn("src/sample_shop/auth.py", brief)
|
|
250
|
+
self.assertIn("src/sample_shop/routes.py", brief)
|
|
251
|
+
self.assertIn("check_password", brief)
|
|
252
|
+
self.assertEqual(set(self.root.rglob("*")), before)
|
|
253
|
+
|
|
254
|
+
def test_demo_rejects_combined_project_arguments(self):
|
|
255
|
+
stderr = io.StringIO()
|
|
256
|
+
with contextlib.redirect_stderr(stderr), self.assertRaises(SystemExit) as error:
|
|
257
|
+
main([str(self.root), "--demo"])
|
|
258
|
+
self.assertEqual(error.exception.code, 2)
|
|
259
|
+
self.assertIn("--demo cannot be combined", stderr.getvalue())
|
|
225
260
|
|
|
226
261
|
def test_cli_copy_generates_a_brief_and_calls_clipboard(self):
|
|
227
262
|
self.put("README.md", "# Clipboard fixture")
|
|
@@ -466,7 +501,7 @@ class ContextInjectTests(unittest.TestCase):
|
|
|
466
501
|
plain = __import__("re").sub(r"\x1b\[[0-9;]*m", "", output.getvalue())
|
|
467
502
|
self.assertIn("CODEBASE", plain)
|
|
468
503
|
self.assertIn("BRIEF", plain)
|
|
469
|
-
self.assertIn("v0.1.
|
|
504
|
+
self.assertIn("v0.1.3", plain)
|
|
470
505
|
self.assertIn("Meet Briefy - your repo guide", plain)
|
|
471
506
|
self.assertIn("WHAT THIS DOES", plain)
|
|
472
507
|
self.assertIn("Makes a short guide to your project for an AI coding assistant", plain)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|