codigest 0.3.9__tar.gz → 0.4.0__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.
- {codigest-0.3.9 → codigest-0.4.0}/.github/workflows/publish.yml +2 -2
- codigest-0.4.0/.python-version +1 -0
- codigest-0.4.0/CITATION.cff +24 -0
- codigest-0.4.0/LICENSE +21 -0
- {codigest-0.3.9 → codigest-0.4.0}/PKG-INFO +72 -9
- {codigest-0.3.9 → codigest-0.4.0}/README.md +67 -5
- {codigest-0.3.9 → codigest-0.4.0}/pyproject.toml +4 -4
- codigest-0.4.0/src/codigest/commands/cap.py +78 -0
- {codigest-0.3.9 → codigest-0.4.0}/src/codigest/commands/clean.py +6 -6
- {codigest-0.3.9 → codigest-0.4.0}/src/codigest/commands/diff.py +10 -8
- {codigest-0.3.9 → codigest-0.4.0}/src/codigest/commands/digest.py +15 -13
- {codigest-0.3.9 → codigest-0.4.0}/src/codigest/commands/init.py +13 -12
- codigest-0.4.0/src/codigest/commands/mcp.py +175 -0
- {codigest-0.3.9 → codigest-0.4.0}/src/codigest/commands/scan.py +30 -29
- {codigest-0.3.9 → codigest-0.4.0}/src/codigest/commands/semdiff.py +24 -29
- {codigest-0.3.9 → codigest-0.4.0}/src/codigest/commands/status.py +18 -20
- codigest-0.4.0/src/codigest/commands/term.py +234 -0
- {codigest-0.3.9 → codigest-0.4.0}/src/codigest/commands/tree.py +22 -19
- {codigest-0.3.9 → codigest-0.4.0}/src/codigest/core/cache.py +34 -53
- {codigest-0.3.9 → codigest-0.4.0}/src/codigest/core/common.py +36 -29
- {codigest-0.3.9 → codigest-0.4.0}/src/codigest/core/git_ops.py +1 -1
- {codigest-0.3.9 → codigest-0.4.0}/src/codigest/core/output.py +6 -6
- {codigest-0.3.9 → codigest-0.4.0}/src/codigest/core/processor.py +2 -2
- {codigest-0.3.9 → codigest-0.4.0}/src/codigest/core/prompts.py +18 -37
- codigest-0.4.0/src/codigest/core/redact.py +88 -0
- {codigest-0.3.9 → codigest-0.4.0}/src/codigest/core/resolver.py +40 -59
- codigest-0.4.0/src/codigest/core/runner.py +136 -0
- {codigest-0.3.9 → codigest-0.4.0}/src/codigest/core/scanner.py +13 -25
- {codigest-0.3.9 → codigest-0.4.0}/src/codigest/core/semdiff.py +78 -76
- {codigest-0.3.9 → codigest-0.4.0}/src/codigest/core/shadow.py +21 -20
- codigest-0.4.0/src/codigest/core/shellhist.py +120 -0
- {codigest-0.3.9 → codigest-0.4.0}/src/codigest/core/tags.py +2 -3
- codigest-0.4.0/src/codigest/core/termstore.py +272 -0
- {codigest-0.3.9 → codigest-0.4.0}/src/codigest/main.py +22 -16
- {codigest-0.3.9 → codigest-0.4.0}/src/codigest/options.py +6 -6
- codigest-0.4.0/src/codigest/shell/hook.ps1 +116 -0
- codigest-0.4.0/tests/test_termstore.py +41 -0
- {codigest-0.3.9 → codigest-0.4.0}/uv.lock +2 -2
- codigest-0.3.9/.python-version +0 -1
- {codigest-0.3.9 → codigest-0.4.0}/.gitignore +0 -0
- {codigest-0.3.9 → codigest-0.4.0}/src/codigest/__init__.py +0 -0
- {codigest-0.3.9 → codigest-0.4.0}/src/codigest/__main__.py +0 -0
- {codigest-0.3.9 → codigest-0.4.0}/src/codigest/config.py +0 -0
- {codigest-0.3.9 → codigest-0.4.0}/src/codigest/core/structure.py +0 -0
- {codigest-0.3.9 → codigest-0.4.0}/src/codigest/core/tokenizer.py +0 -0
|
@@ -25,7 +25,7 @@ jobs:
|
|
|
25
25
|
|
|
26
26
|
# Python 3.14 설정 (uv 이용)
|
|
27
27
|
- name: Set up Python
|
|
28
|
-
run: uv python install 3.
|
|
28
|
+
run: uv python install 3.15
|
|
29
29
|
|
|
30
30
|
# 의존성 설치 및 빌드 (uv build 하나로 해결)
|
|
31
31
|
- name: Build package
|
|
@@ -48,7 +48,7 @@ jobs:
|
|
|
48
48
|
uses: astral-sh/setup-uv@v5
|
|
49
49
|
|
|
50
50
|
- name: Set up Python
|
|
51
|
-
run: uv python install 3.
|
|
51
|
+
run: uv python install 3.15
|
|
52
52
|
|
|
53
53
|
- name: Install Python and Sphinx dependencies
|
|
54
54
|
run: |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.15
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use this software, please cite it as below."
|
|
3
|
+
type: software
|
|
4
|
+
title: "Codigest: codebase and terminal context manager for LLM-assisted development"
|
|
5
|
+
abstract: >-
|
|
6
|
+
Command-line tool that extracts project snapshots, change-tracked diffs,
|
|
7
|
+
structural (semantic) diffs and captured terminal output as LLM-ready XML,
|
|
8
|
+
and serves them to AI agents over MCP.
|
|
9
|
+
authors:
|
|
10
|
+
- family-names: Yong
|
|
11
|
+
given-names: Isaac
|
|
12
|
+
email: isaacyong@naver.com
|
|
13
|
+
affiliation: Sogang University
|
|
14
|
+
repository-code: "https://github.com/NaNIsaac/codigest"
|
|
15
|
+
url: "https://pypi.org/project/codigest/"
|
|
16
|
+
license: MIT
|
|
17
|
+
version: 0.4.0
|
|
18
|
+
date-released: "2026-10-15"
|
|
19
|
+
keywords:
|
|
20
|
+
- LLM
|
|
21
|
+
- developer tools
|
|
22
|
+
- code context
|
|
23
|
+
- MCP
|
|
24
|
+
- CLI
|
codigest-0.4.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 Isaac Yong
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: codigest
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Human-Centered Codebase Context Manager for LLMs
|
|
5
5
|
Project-URL: Homepage, https://github.com/NaNIsaac/codigest
|
|
6
6
|
Project-URL: Repository, https://github.com/NaNIsaac/codigest
|
|
7
7
|
Project-URL: Issues, https://github.com/NaNIsaac/codigest/issues
|
|
8
|
-
Author-email:
|
|
8
|
+
Author-email: Isaac Yong <isaacyong@naver.com>
|
|
9
9
|
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
10
11
|
Classifier: Intended Audience :: Developers
|
|
11
12
|
Classifier: License :: OSI Approved :: MIT License
|
|
12
13
|
Classifier: Operating System :: OS Independent
|
|
13
14
|
Classifier: Programming Language :: Python :: 3
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
15
16
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
17
|
Classifier: Topic :: Utilities
|
|
17
|
-
Requires-Python: >=3.
|
|
18
|
+
Requires-Python: >=3.15
|
|
18
19
|
Requires-Dist: loguru
|
|
19
20
|
Requires-Dist: pathspec
|
|
20
21
|
Requires-Dist: pyperclip
|
|
@@ -31,12 +32,24 @@ Unlike simple copy-paste tools, Codigest employs a **Context Anchor** system (Sh
|
|
|
31
32
|
|
|
32
33
|
> **Note:** You can use the short alias **`cdg`** instead of `codigest` for faster typing.
|
|
33
34
|
|
|
35
|
+
## Quickstart
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
uv tool install codigest
|
|
39
|
+
|
|
40
|
+
cdg scan # whole project as LLM-ready XML (copied to clipboard)
|
|
41
|
+
cdg init && cdg diff # set an anchor, later get only what changed
|
|
42
|
+
cdg cap -- pytest -q # run a command and keep its output
|
|
43
|
+
cdg term -f # hand the last failed command + output to your LLM
|
|
44
|
+
claude mcp add codigest -- cdg mcp # let AI agents call all of the above
|
|
45
|
+
```
|
|
46
|
+
|
|
34
47
|
## Core Philosophy
|
|
35
48
|
|
|
36
49
|
* **Read-Only & Safe**: Codigest never modifies your source code. It only reads, analyzes, and formats context.
|
|
37
50
|
* **Context-Aware**: Instead of dumping raw text, it structures code into XML snapshots designed for LLM comprehension.
|
|
38
51
|
* **Session-Based Tracking**: It maintains an internal anchor to track "work-in-progress" changes independently of your Git commits.
|
|
39
|
-
* **Environment Isolated**: Runs in its own isolated Python 3.
|
|
52
|
+
* **Environment Isolated**: Runs in its own isolated Python 3.15 environment via `uv`, ensuring it never conflicts with your project's dependencies.
|
|
40
53
|
|
|
41
54
|
## Installation
|
|
42
55
|
|
|
@@ -52,13 +65,12 @@ If you don't have `uv` installed, use **Winget** (Windows) or curl (macOS/Linux)
|
|
|
52
65
|
winget install --id astral-sh.uv
|
|
53
66
|
|
|
54
67
|
# macOS/Linux
|
|
55
|
-
curl -LsSf
|
|
56
|
-
|
|
68
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
57
69
|
```
|
|
58
70
|
|
|
59
71
|
### Step 2: Install `codigest`
|
|
60
72
|
|
|
61
|
-
Install Codigest globally. `uv` will automatically manage the required Python 3.
|
|
73
|
+
Install Codigest globally. `uv` will automatically manage the required Python 3.15 environment for you.
|
|
62
74
|
|
|
63
75
|
```bash
|
|
64
76
|
uv tool install codigest
|
|
@@ -192,6 +204,57 @@ cdg clean
|
|
|
192
204
|
cdg clean -y
|
|
193
205
|
```
|
|
194
206
|
|
|
207
|
+
### 9. Terminal Capture & Recall (`cap`, `term`)
|
|
208
|
+
|
|
209
|
+
Runs a command transparently (output still streams live) and records its command line, combined stdout/stderr, exit code and cwd in a machine-global store. `term` then recalls recent entries as a `<terminal_history>` block for your LLM. Works in any directory, no `cdg init` needed.
|
|
210
|
+
|
|
211
|
+
* **Use Case:** "This test failed. Here's exactly what I ran and what it printed."
|
|
212
|
+
* **Safety:** Known secret shapes (API keys, tokens, `password=...`) are redacted before anything touches disk. Output of credential commands (`env`, `printenv`, `gh auth token`, ...) is never stored.
|
|
213
|
+
* **Fallback:** Commands not run through `cap` are read from the native shell history (PowerShell, bash, zsh, fish), without output.
|
|
214
|
+
|
|
215
|
+
**Automatic capture (PowerShell):** `cdg term install` hooks your PowerShell profile so *every* command is captured without the `cap` prefix. Commands run in your current session (`cd`, variables and functions behave normally). Editors, pagers, REPLs and `ssh` are skipped automatically.
|
|
216
|
+
|
|
217
|
+
* Skip one command: start the line with a space.
|
|
218
|
+
* Skip more programs: `$env:CODIGEST_CAP_SKIP = '^\s*(myrepl|mytui)\b'`
|
|
219
|
+
* Disable for a session: `$env:CODIGEST_NOCAP = 1`. Remove: `cdg term uninstall`.
|
|
220
|
+
* Trade-off: captured programs write to a pipe, not a TTY, so some lose colors or progress bars.
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
cdg term install # PowerShell: capture everything automatically
|
|
224
|
+
|
|
225
|
+
# Run and capture (prefix with -- so flags reach the command)
|
|
226
|
+
cdg cap -- pytest -q
|
|
227
|
+
cdg cap "npm run build | tail -20"
|
|
228
|
+
|
|
229
|
+
# Recall
|
|
230
|
+
cdg term # last command
|
|
231
|
+
cdg term -n 3 # last 3 commands
|
|
232
|
+
cdg term -i 2 # only the command from 2 runs ago
|
|
233
|
+
cdg term -f # last failed command
|
|
234
|
+
|
|
235
|
+
cdg term path # where captures are stored (override with CODIGEST_HOME)
|
|
236
|
+
cdg term clear # delete all captures
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### 10. For AI Agents (`mcp`)
|
|
240
|
+
|
|
241
|
+
`cdg mcp` serves Codigest as an [MCP](https://modelcontextprotocol.io) server over stdio, so coding agents can pull context themselves instead of asking you to paste it.
|
|
242
|
+
|
|
243
|
+
| Tool | Returns |
|
|
244
|
+
| --- | --- |
|
|
245
|
+
| `terminal_history` | Recent commands with output and exit code (`count`, `failed`) |
|
|
246
|
+
| `scan` | Project snapshot (`targets`, `resolve`, `message`) |
|
|
247
|
+
| `diff` / `semdiff` | Text / structural changes since the anchor |
|
|
248
|
+
| `digest` | Signatures and docstrings only |
|
|
249
|
+
| `tree` | Directory tree |
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
# Claude Code
|
|
253
|
+
claude mcp add --scope user codigest -- cdg mcp
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Other clients (Cursor, VS Code, Claude Desktop): add a stdio server with command `cdg` and args `["mcp"]`.
|
|
257
|
+
|
|
195
258
|
---
|
|
196
259
|
|
|
197
260
|
## Configuration
|
|
@@ -231,4 +294,4 @@ Codigest maintains a hidden, lightweight Git repository inside `.codigest/anchor
|
|
|
231
294
|
|
|
232
295
|
## License
|
|
233
296
|
|
|
234
|
-
MIT
|
|
297
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -6,12 +6,24 @@ Unlike simple copy-paste tools, Codigest employs a **Context Anchor** system (Sh
|
|
|
6
6
|
|
|
7
7
|
> **Note:** You can use the short alias **`cdg`** instead of `codigest` for faster typing.
|
|
8
8
|
|
|
9
|
+
## Quickstart
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
uv tool install codigest
|
|
13
|
+
|
|
14
|
+
cdg scan # whole project as LLM-ready XML (copied to clipboard)
|
|
15
|
+
cdg init && cdg diff # set an anchor, later get only what changed
|
|
16
|
+
cdg cap -- pytest -q # run a command and keep its output
|
|
17
|
+
cdg term -f # hand the last failed command + output to your LLM
|
|
18
|
+
claude mcp add codigest -- cdg mcp # let AI agents call all of the above
|
|
19
|
+
```
|
|
20
|
+
|
|
9
21
|
## Core Philosophy
|
|
10
22
|
|
|
11
23
|
* **Read-Only & Safe**: Codigest never modifies your source code. It only reads, analyzes, and formats context.
|
|
12
24
|
* **Context-Aware**: Instead of dumping raw text, it structures code into XML snapshots designed for LLM comprehension.
|
|
13
25
|
* **Session-Based Tracking**: It maintains an internal anchor to track "work-in-progress" changes independently of your Git commits.
|
|
14
|
-
* **Environment Isolated**: Runs in its own isolated Python 3.
|
|
26
|
+
* **Environment Isolated**: Runs in its own isolated Python 3.15 environment via `uv`, ensuring it never conflicts with your project's dependencies.
|
|
15
27
|
|
|
16
28
|
## Installation
|
|
17
29
|
|
|
@@ -27,13 +39,12 @@ If you don't have `uv` installed, use **Winget** (Windows) or curl (macOS/Linux)
|
|
|
27
39
|
winget install --id astral-sh.uv
|
|
28
40
|
|
|
29
41
|
# macOS/Linux
|
|
30
|
-
curl -LsSf
|
|
31
|
-
|
|
42
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
32
43
|
```
|
|
33
44
|
|
|
34
45
|
### Step 2: Install `codigest`
|
|
35
46
|
|
|
36
|
-
Install Codigest globally. `uv` will automatically manage the required Python 3.
|
|
47
|
+
Install Codigest globally. `uv` will automatically manage the required Python 3.15 environment for you.
|
|
37
48
|
|
|
38
49
|
```bash
|
|
39
50
|
uv tool install codigest
|
|
@@ -167,6 +178,57 @@ cdg clean
|
|
|
167
178
|
cdg clean -y
|
|
168
179
|
```
|
|
169
180
|
|
|
181
|
+
### 9. Terminal Capture & Recall (`cap`, `term`)
|
|
182
|
+
|
|
183
|
+
Runs a command transparently (output still streams live) and records its command line, combined stdout/stderr, exit code and cwd in a machine-global store. `term` then recalls recent entries as a `<terminal_history>` block for your LLM. Works in any directory, no `cdg init` needed.
|
|
184
|
+
|
|
185
|
+
* **Use Case:** "This test failed. Here's exactly what I ran and what it printed."
|
|
186
|
+
* **Safety:** Known secret shapes (API keys, tokens, `password=...`) are redacted before anything touches disk. Output of credential commands (`env`, `printenv`, `gh auth token`, ...) is never stored.
|
|
187
|
+
* **Fallback:** Commands not run through `cap` are read from the native shell history (PowerShell, bash, zsh, fish), without output.
|
|
188
|
+
|
|
189
|
+
**Automatic capture (PowerShell):** `cdg term install` hooks your PowerShell profile so *every* command is captured without the `cap` prefix. Commands run in your current session (`cd`, variables and functions behave normally). Editors, pagers, REPLs and `ssh` are skipped automatically.
|
|
190
|
+
|
|
191
|
+
* Skip one command: start the line with a space.
|
|
192
|
+
* Skip more programs: `$env:CODIGEST_CAP_SKIP = '^\s*(myrepl|mytui)\b'`
|
|
193
|
+
* Disable for a session: `$env:CODIGEST_NOCAP = 1`. Remove: `cdg term uninstall`.
|
|
194
|
+
* Trade-off: captured programs write to a pipe, not a TTY, so some lose colors or progress bars.
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
cdg term install # PowerShell: capture everything automatically
|
|
198
|
+
|
|
199
|
+
# Run and capture (prefix with -- so flags reach the command)
|
|
200
|
+
cdg cap -- pytest -q
|
|
201
|
+
cdg cap "npm run build | tail -20"
|
|
202
|
+
|
|
203
|
+
# Recall
|
|
204
|
+
cdg term # last command
|
|
205
|
+
cdg term -n 3 # last 3 commands
|
|
206
|
+
cdg term -i 2 # only the command from 2 runs ago
|
|
207
|
+
cdg term -f # last failed command
|
|
208
|
+
|
|
209
|
+
cdg term path # where captures are stored (override with CODIGEST_HOME)
|
|
210
|
+
cdg term clear # delete all captures
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### 10. For AI Agents (`mcp`)
|
|
214
|
+
|
|
215
|
+
`cdg mcp` serves Codigest as an [MCP](https://modelcontextprotocol.io) server over stdio, so coding agents can pull context themselves instead of asking you to paste it.
|
|
216
|
+
|
|
217
|
+
| Tool | Returns |
|
|
218
|
+
| --- | --- |
|
|
219
|
+
| `terminal_history` | Recent commands with output and exit code (`count`, `failed`) |
|
|
220
|
+
| `scan` | Project snapshot (`targets`, `resolve`, `message`) |
|
|
221
|
+
| `diff` / `semdiff` | Text / structural changes since the anchor |
|
|
222
|
+
| `digest` | Signatures and docstrings only |
|
|
223
|
+
| `tree` | Directory tree |
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
# Claude Code
|
|
227
|
+
claude mcp add --scope user codigest -- cdg mcp
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Other clients (Cursor, VS Code, Claude Desktop): add a stdio server with command `cdg` and args `["mcp"]`.
|
|
231
|
+
|
|
170
232
|
---
|
|
171
233
|
|
|
172
234
|
## Configuration
|
|
@@ -206,4 +268,4 @@ Codigest maintains a hidden, lightweight Git repository inside `.codigest/anchor
|
|
|
206
268
|
|
|
207
269
|
## License
|
|
208
270
|
|
|
209
|
-
MIT
|
|
271
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "codigest"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.4.0"
|
|
4
4
|
description = "Human-Centered Codebase Context Manager for LLMs"
|
|
5
|
-
authors = [{name = "
|
|
5
|
+
authors = [{ name = "Isaac Yong", email = "isaacyong@naver.com" }]
|
|
6
6
|
readme = "README.md"
|
|
7
|
-
requires-python = ">=3.
|
|
7
|
+
requires-python = ">=3.15"
|
|
8
8
|
license = { text = "MIT" }
|
|
9
9
|
dependencies = [
|
|
10
10
|
"typer",
|
|
@@ -17,7 +17,7 @@ dependencies = [
|
|
|
17
17
|
|
|
18
18
|
classifiers = [
|
|
19
19
|
"Programming Language :: Python :: 3",
|
|
20
|
-
"Programming Language :: Python :: 3.
|
|
20
|
+
"Programming Language :: Python :: 3.15",
|
|
21
21
|
"License :: OSI Approved :: MIT License",
|
|
22
22
|
"Operating System :: OS Independent",
|
|
23
23
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""
|
|
2
|
+
`cdg cap` -- run a command transparently and record it for later recall.
|
|
3
|
+
|
|
4
|
+
Registered on the root app as a single command (not a Typer group), so the
|
|
5
|
+
first word after `cap` is treated as the wrapped program rather than a
|
|
6
|
+
subcommand. It declares no options of its own: everything after `cap` belongs
|
|
7
|
+
to the wrapped command, so flags like `-n` are never stolen from it.
|
|
8
|
+
"""
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Annotated
|
|
11
|
+
|
|
12
|
+
import typer
|
|
13
|
+
from rich.console import Console
|
|
14
|
+
|
|
15
|
+
lazy from ..core import runner, termstore, shellhist
|
|
16
|
+
|
|
17
|
+
err_console = Console(stderr=True)
|
|
18
|
+
|
|
19
|
+
CONTEXT_SETTINGS = {
|
|
20
|
+
"allow_extra_args": True,
|
|
21
|
+
"ignore_unknown_options": True,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
CommandArgument = Annotated[
|
|
25
|
+
list[str] | None,
|
|
26
|
+
typer.Argument(
|
|
27
|
+
metavar="COMMAND...",
|
|
28
|
+
help="Command to run. Prefix with `--` so its flags are passed through.",
|
|
29
|
+
),
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def handle(ctx: typer.Context, command: CommandArgument = None) -> None:
|
|
34
|
+
"""
|
|
35
|
+
[Capture] Run a command, show its output as usual, and record it globally.
|
|
36
|
+
|
|
37
|
+
Examples:
|
|
38
|
+
|
|
39
|
+
cdg cap -- pytest -q
|
|
40
|
+
|
|
41
|
+
cdg cap "npm run build | tail -20"
|
|
42
|
+
"""
|
|
43
|
+
args = [*(command or []), *ctx.args]
|
|
44
|
+
|
|
45
|
+
if not args:
|
|
46
|
+
err_console.print(
|
|
47
|
+
"[yellow]Usage:[/yellow] cdg cap -- <command>\n"
|
|
48
|
+
"[dim]Example: cdg cap -- pytest -q[/dim]"
|
|
49
|
+
)
|
|
50
|
+
raise typer.Exit(code=2)
|
|
51
|
+
|
|
52
|
+
command_text = runner.join_command(args)
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
exit_code, output, duration_ms = runner.run(args)
|
|
56
|
+
except FileNotFoundError as e:
|
|
57
|
+
err_console.print(f"[red][Error][/red] Command not found: {args[0]}")
|
|
58
|
+
raise typer.Exit(code=127) from e
|
|
59
|
+
except RuntimeError as e:
|
|
60
|
+
err_console.print(f"[red][Error][/red] {e}")
|
|
61
|
+
raise typer.Exit(code=1) from e
|
|
62
|
+
|
|
63
|
+
entry = termstore.Entry(
|
|
64
|
+
command=command_text,
|
|
65
|
+
output=output,
|
|
66
|
+
exit_code=exit_code,
|
|
67
|
+
cwd=str(Path.cwd()),
|
|
68
|
+
shell=shellhist.detect_shell(),
|
|
69
|
+
started_at=termstore.now_iso(),
|
|
70
|
+
duration_ms=duration_ms,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
termstore.append_entry(entry)
|
|
75
|
+
except OSError as e:
|
|
76
|
+
err_console.print(f"[dim]⚠️ Capture not stored: {e}[/dim]")
|
|
77
|
+
|
|
78
|
+
raise typer.Exit(code=exit_code)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import typer
|
|
2
|
-
import shutil
|
|
3
1
|
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
4
|
from rich.console import Console
|
|
5
5
|
|
|
6
|
-
from ..core import common
|
|
6
|
+
lazy from ..core import common
|
|
7
7
|
|
|
8
8
|
app = typer.Typer()
|
|
9
9
|
console = Console()
|
|
@@ -19,7 +19,7 @@ def handle(
|
|
|
19
19
|
resolve_path=True
|
|
20
20
|
),
|
|
21
21
|
yes: bool = typer.Option(False, "-y", "--yes", "-f", "--force", help="Skip confirmation prompt"),
|
|
22
|
-
):
|
|
22
|
+
) -> None:
|
|
23
23
|
"""
|
|
24
24
|
[Cleanup] Removes the .codigest environment and clears all context anchors and artifacts.
|
|
25
25
|
"""
|
|
@@ -38,8 +38,8 @@ def handle(
|
|
|
38
38
|
raise typer.Exit()
|
|
39
39
|
|
|
40
40
|
try:
|
|
41
|
-
|
|
41
|
+
common.remove_tree(codigest_dir)
|
|
42
42
|
console.print(f"[bold green]✔ Successfully removed .codigest environment from {root_path}[/bold green]")
|
|
43
43
|
except Exception as e:
|
|
44
44
|
console.print(f"[bold red]Error removing .codigest:[/bold red] {e}")
|
|
45
|
-
raise typer.Exit(1)
|
|
45
|
+
raise typer.Exit(1) from e
|
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
import typer
|
|
2
1
|
from pathlib import Path
|
|
3
|
-
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
4
|
|
|
5
5
|
from ..options import CopyOption, StdoutOption, ResolveOption, MessageOption
|
|
6
|
-
|
|
7
|
-
from
|
|
6
|
+
|
|
7
|
+
lazy from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
8
|
+
lazy from ..core import prompts, shadow, tags, common
|
|
9
|
+
lazy from ..core.output import OutputHandler
|
|
8
10
|
|
|
9
11
|
app = typer.Typer()
|
|
10
12
|
|
|
11
13
|
@app.callback(invoke_without_command=True)
|
|
12
14
|
def handle(
|
|
13
15
|
target: Path = typer.Argument(
|
|
14
|
-
Path.cwd(),
|
|
16
|
+
Path.cwd(),
|
|
15
17
|
help="Target directory to check diff",
|
|
16
18
|
exists=True,
|
|
17
19
|
file_okay=False,
|
|
@@ -23,7 +25,7 @@ def handle(
|
|
|
23
25
|
stdout: StdoutOption = False,
|
|
24
26
|
message: MessageOption = "",
|
|
25
27
|
resolve: ResolveOption = False,
|
|
26
|
-
):
|
|
28
|
+
) -> None:
|
|
27
29
|
"""
|
|
28
30
|
[Context Update] Shows changes since the last 'codigest scan'.
|
|
29
31
|
Useful for updating LLM context without re-uploading everything.
|
|
@@ -34,7 +36,7 @@ def handle(
|
|
|
34
36
|
# [1] Context Setup
|
|
35
37
|
ctx = common.get_context(target)
|
|
36
38
|
root_path = ctx.root_path
|
|
37
|
-
|
|
39
|
+
|
|
38
40
|
anchor = shadow.ContextAnchor(root_path)
|
|
39
41
|
|
|
40
42
|
# Check Baseline
|
|
@@ -84,7 +86,7 @@ def handle(
|
|
|
84
86
|
|
|
85
87
|
# [4] Output
|
|
86
88
|
log_console.print(f"[bold green]Changes Detected![/bold green] ({len(formatted_diff)} chars)")
|
|
87
|
-
|
|
89
|
+
|
|
88
90
|
def _save():
|
|
89
91
|
if save:
|
|
90
92
|
out_path = root_path / ".codigest" / "changes.diff"
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import typer
|
|
2
1
|
from pathlib import Path
|
|
3
|
-
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
4
|
|
|
5
5
|
from ..options import CopyOption, StdoutOption, ResolveOption, MessageOption
|
|
6
|
-
|
|
7
|
-
from
|
|
6
|
+
|
|
7
|
+
lazy from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
8
|
+
lazy from ..core import structure, prompts, semdiff, tags, tokenizer, common, cache
|
|
9
|
+
lazy from ..core.output import OutputHandler
|
|
8
10
|
|
|
9
11
|
app = typer.Typer()
|
|
10
12
|
|
|
@@ -16,7 +18,7 @@ def handle(
|
|
|
16
18
|
stdout: StdoutOption = False,
|
|
17
19
|
message: MessageOption = "",
|
|
18
20
|
resolve: ResolveOption = False,
|
|
19
|
-
):
|
|
21
|
+
) -> None:
|
|
20
22
|
"""
|
|
21
23
|
[Architectural View] Summarizes the codebase structure (Classes/Functions only).
|
|
22
24
|
"""
|
|
@@ -26,7 +28,7 @@ def handle(
|
|
|
26
28
|
# [1] Context Setup
|
|
27
29
|
ctx = common.get_context(target)
|
|
28
30
|
root_path = ctx.root_path
|
|
29
|
-
|
|
31
|
+
|
|
30
32
|
prompt_engine = prompts.get_engine(root_path)
|
|
31
33
|
|
|
32
34
|
with Progress(
|
|
@@ -35,12 +37,12 @@ def handle(
|
|
|
35
37
|
transient=True,
|
|
36
38
|
console=log_console
|
|
37
39
|
) as progress:
|
|
38
|
-
|
|
40
|
+
|
|
39
41
|
task = progress.add_task("digest", total=None)
|
|
40
|
-
|
|
42
|
+
|
|
41
43
|
# [2] File Discovery via Context
|
|
42
44
|
files = ctx.get_target_files(ignore_config=False, resolve_deps=resolve)
|
|
43
|
-
|
|
45
|
+
|
|
44
46
|
tree_str = structure.generate_ascii_tree(files, root_path)
|
|
45
47
|
|
|
46
48
|
content_cache = cache.ContentCache(root_path)
|
|
@@ -72,7 +74,7 @@ def handle(
|
|
|
72
74
|
content_cache.save()
|
|
73
75
|
|
|
74
76
|
summary_blob = "\n".join(summary_blocks)
|
|
75
|
-
|
|
77
|
+
|
|
76
78
|
try:
|
|
77
79
|
digest_content = prompt_engine.render(
|
|
78
80
|
"digest",
|
|
@@ -83,13 +85,13 @@ def handle(
|
|
|
83
85
|
)
|
|
84
86
|
except Exception as e:
|
|
85
87
|
log_console.print(f"[red]❌ Rendering Failed:[/red] {e}")
|
|
86
|
-
raise typer.Exit(1)
|
|
87
|
-
|
|
88
|
+
raise typer.Exit(1) from e
|
|
89
|
+
|
|
88
90
|
progress.update(task, completed=100)
|
|
89
91
|
|
|
90
92
|
token_count = tokenizer.estimate_tokens(digest_content)
|
|
91
93
|
log_console.print(f"[bold green]Digest Generated![/bold green] ([bold cyan]~{token_count:,} Tokens[/bold cyan])")
|
|
92
|
-
|
|
94
|
+
|
|
93
95
|
def _save():
|
|
94
96
|
if save:
|
|
95
97
|
out_path = root_path / ".codigest" / "digest.txt"
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import typer
|
|
2
1
|
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import typer
|
|
3
4
|
from rich.console import Console
|
|
4
|
-
from rich.panel import Panel
|
|
5
5
|
|
|
6
|
-
from
|
|
6
|
+
lazy from rich.panel import Panel
|
|
7
|
+
lazy from ..core import scanner, shadow
|
|
7
8
|
|
|
8
9
|
app = typer.Typer()
|
|
9
10
|
console = Console()
|
|
@@ -13,7 +14,7 @@ DEFAULT_CONFIG = """# Codigest Configuration File
|
|
|
13
14
|
description = "Auto-generated context configuration"
|
|
14
15
|
|
|
15
16
|
[filter]
|
|
16
|
-
max_file_size_kb = 100
|
|
17
|
+
max_file_size_kb = 100
|
|
17
18
|
extensions = [
|
|
18
19
|
".py", ".pyi",
|
|
19
20
|
".ts", ".tsx", ".js", ".jsx",
|
|
@@ -37,7 +38,7 @@ structure = "toon"
|
|
|
37
38
|
def handle(
|
|
38
39
|
# target 인자 추가 (기본값: 현재 위치)
|
|
39
40
|
target: Path = typer.Argument(
|
|
40
|
-
Path.cwd(),
|
|
41
|
+
Path.cwd(),
|
|
41
42
|
help="Target directory to initialize",
|
|
42
43
|
exists=True,
|
|
43
44
|
file_okay=False,
|
|
@@ -45,12 +46,12 @@ def handle(
|
|
|
45
46
|
resolve_path=True
|
|
46
47
|
),
|
|
47
48
|
force: bool = typer.Option(False, "--force", "-f", help="Overwrite existing config"),
|
|
48
|
-
):
|
|
49
|
+
) -> None:
|
|
49
50
|
"""
|
|
50
51
|
Initialize the .codigest environment and capture the initial state.
|
|
51
52
|
"""
|
|
52
53
|
root_path = target # Typer가 이미 resolve_path=True로 절대경로 변환해줌
|
|
53
|
-
|
|
54
|
+
|
|
54
55
|
config_dir = root_path / ".codigest"
|
|
55
56
|
config_file = config_dir / "config.toml"
|
|
56
57
|
gitignore_file = root_path / ".gitignore"
|
|
@@ -61,7 +62,7 @@ def handle(
|
|
|
61
62
|
if not config_dir.exists():
|
|
62
63
|
config_dir.mkdir()
|
|
63
64
|
console.print(" [green]✔[/green] Created [bold].codigest/[/bold] directory")
|
|
64
|
-
|
|
65
|
+
|
|
65
66
|
if not config_file.exists() or force:
|
|
66
67
|
config_file.write_text(DEFAULT_CONFIG, encoding="utf-8")
|
|
67
68
|
console.print(" [green]✔[/green] Created [bold]config.toml[/bold]")
|
|
@@ -75,22 +76,22 @@ def handle(
|
|
|
75
76
|
console.print("\n[dim]Creating initial context anchor...[/dim]")
|
|
76
77
|
try:
|
|
77
78
|
# Default scan settings for initialization
|
|
78
|
-
files = scanner.scan_project(root_path)
|
|
79
|
+
files = scanner.scan_project(root_path)
|
|
79
80
|
anchor = shadow.ContextAnchor(root_path)
|
|
80
81
|
anchor.update(files)
|
|
81
82
|
console.print(" [green]✔[/green] Baseline snapshot captured.")
|
|
82
83
|
except Exception as e:
|
|
83
84
|
console.print(f" [red]⚠️ Failed to create initial anchor: {e}[/red]")
|
|
84
85
|
|
|
85
|
-
console.print("\n[bold green]✨ Ready to digest![/bold green]")
|
|
86
|
+
console.print("\n[bold green]✨ Ready to digest![/bold green]")
|
|
86
87
|
console.print("Try running: [cyan]codigest diff[/cyan] to see changes from now on.")
|
|
87
88
|
|
|
88
|
-
def _update_gitignore(path: Path):
|
|
89
|
+
def _update_gitignore(path: Path) -> None:
|
|
89
90
|
entry = ".codigest/"
|
|
90
91
|
if not path.exists():
|
|
91
92
|
path.write_text(f"# Git Ignore\n{entry}\n", encoding="utf-8")
|
|
92
93
|
return
|
|
93
|
-
|
|
94
|
+
|
|
94
95
|
if entry not in path.read_text(encoding="utf-8"):
|
|
95
96
|
with path.open("a", encoding="utf-8") as f:
|
|
96
97
|
f.write(f"\n{entry}\n")
|