codigest 0.3.2__tar.gz → 0.3.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.
- codigest-0.3.3/PKG-INFO +199 -0
- codigest-0.3.3/README.md +174 -0
- {codigest-0.3.2 → codigest-0.3.3}/pyproject.toml +3 -2
- {codigest-0.3.2 → codigest-0.3.3}/src/codigest/commands/diff.py +3 -3
- {codigest-0.3.2 → codigest-0.3.3}/src/codigest/commands/digest.py +3 -3
- {codigest-0.3.2 → codigest-0.3.3}/src/codigest/commands/scan.py +96 -41
- {codigest-0.3.2 → codigest-0.3.3}/src/codigest/commands/semdiff.py +4 -4
- {codigest-0.3.2 → codigest-0.3.3}/src/codigest/commands/tree.py +20 -22
- codigest-0.3.3/src/codigest/core/resolver.py +107 -0
- {codigest-0.3.2 → codigest-0.3.3}/src/codigest/core/semdiff.py +1 -1
- {codigest-0.3.2 → codigest-0.3.3}/src/codigest/core/shadow.py +39 -24
- {codigest-0.3.2 → codigest-0.3.3}/src/codigest/main.py +1 -1
- {codigest-0.3.2 → codigest-0.3.3}/uv.lock +1 -1
- codigest-0.3.2/PKG-INFO +0 -176
- codigest-0.3.2/README.md +0 -151
- {codigest-0.3.2 → codigest-0.3.3}/.github/workflows/publish.yml +0 -0
- {codigest-0.3.2 → codigest-0.3.3}/.gitignore +0 -0
- {codigest-0.3.2 → codigest-0.3.3}/.python-version +0 -0
- {codigest-0.3.2 → codigest-0.3.3}/src/codigest/__init__.py +0 -0
- {codigest-0.3.2 → codigest-0.3.3}/src/codigest/__main__.py +0 -0
- {codigest-0.3.2 → codigest-0.3.3}/src/codigest/commands/init.py +0 -0
- {codigest-0.3.2 → codigest-0.3.3}/src/codigest/config.py +0 -0
- {codigest-0.3.2 → codigest-0.3.3}/src/codigest/core/git_ops.py +0 -0
- {codigest-0.3.2 → codigest-0.3.3}/src/codigest/core/processor.py +0 -0
- {codigest-0.3.2 → codigest-0.3.3}/src/codigest/core/prompts.py +0 -0
- {codigest-0.3.2 → codigest-0.3.3}/src/codigest/core/scanner.py +0 -0
- {codigest-0.3.2 → codigest-0.3.3}/src/codigest/core/structure.py +0 -0
- {codigest-0.3.2 → codigest-0.3.3}/src/codigest/core/tags.py +0 -0
- {codigest-0.3.2 → codigest-0.3.3}/src/codigest/core/tokenizer.py +0 -0
codigest-0.3.3/PKG-INFO
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: codigest
|
|
3
|
+
Version: 0.3.3
|
|
4
|
+
Summary: Human-Centered Codebase Context Manager for LLMs
|
|
5
|
+
Project-URL: Homepage, https://github.com/SJB777/codigest
|
|
6
|
+
Project-URL: Repository, https://github.com/SJB777/codigest
|
|
7
|
+
Project-URL: Issues, https://github.com/SJB777/codigest/issues
|
|
8
|
+
Author-email: Codigest Team <sngd024@google.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
+
Classifier: Topic :: Utilities
|
|
17
|
+
Requires-Python: >=3.14
|
|
18
|
+
Requires-Dist: loguru
|
|
19
|
+
Requires-Dist: pathspec
|
|
20
|
+
Requires-Dist: pyperclip
|
|
21
|
+
Requires-Dist: rich
|
|
22
|
+
Requires-Dist: tomli-w
|
|
23
|
+
Requires-Dist: typer
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# Codigest
|
|
27
|
+
|
|
28
|
+
**Codigest** is a standalone CLI tool designed to extract, structure, and track the context of your codebase for Large Language Models (LLMs).
|
|
29
|
+
|
|
30
|
+
Unlike simple copy-paste tools, Codigest employs a **Context Anchor** system (Shadow Git) to track changes locally without polluting your main version control history. It also features **Semantic Analysis** to detect structural changes in your code.
|
|
31
|
+
|
|
32
|
+
> **Note:** You can use the short alias **`cdg`** instead of `codigest` for faster typing.
|
|
33
|
+
|
|
34
|
+
## Core Philosophy
|
|
35
|
+
|
|
36
|
+
* **Read-Only & Safe**: Codigest never modifies your source code. It only reads, analyzes, and formats context.
|
|
37
|
+
* **Context-Aware**: Instead of dumping raw text, it structures code into XML snapshots designed for LLM comprehension.
|
|
38
|
+
* **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.14 environment via `uv`, ensuring it never conflicts with your project's dependencies.
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
Codigest runs as a global tool. You don't need to add it to your project's `requirements.txt`.
|
|
44
|
+
We strongly recommend using **uv** for the best experience.
|
|
45
|
+
|
|
46
|
+
### Step 1: Install `uv`
|
|
47
|
+
|
|
48
|
+
If you don't have `uv` installed, use **Winget** (Windows) or curl (macOS/Linux).
|
|
49
|
+
|
|
50
|
+
```powershell
|
|
51
|
+
# Windows (via Winget)
|
|
52
|
+
winget install --id astral-sh.uv
|
|
53
|
+
|
|
54
|
+
# macOS/Linux
|
|
55
|
+
curl -LsSf [https://astral.sh/uv/install.sh](https://astral.sh/uv/install.sh) | sh
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Step 2: Install `codigest`
|
|
60
|
+
|
|
61
|
+
Install Codigest globally. `uv` will automatically manage the required Python 3.14 environment for you.
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
uv tool install codigest
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Step 3: Update Path (Important!)
|
|
68
|
+
|
|
69
|
+
To run `codigest` (or `cdg`) from any terminal, ensure your tools directory is in your PATH.
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
uv tool update-shell
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
*> **Note:** Restart your terminal (or VS Code) after this step to apply changes.*
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Workflow & Commands
|
|
80
|
+
|
|
81
|
+
Once installed, use the **`cdg`** command anywhere.
|
|
82
|
+
|
|
83
|
+
### 1. Initialization
|
|
84
|
+
|
|
85
|
+
Sets up the `.codigest` directory and captures the initial baseline anchor.
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
cdg init
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### 2. Full Context Snapshot (`scan`)
|
|
92
|
+
|
|
93
|
+
Scans the codebase and generates a structured XML snapshot. Includes a **Pre-flight Check** to prevent accidental token overflow.
|
|
94
|
+
|
|
95
|
+
* **Output:** `.codigest/snapshot.xml`
|
|
96
|
+
* **Features:**
|
|
97
|
+
* **Smart Confirmation:** Automatically skips confirmation for small contexts, but warns you for large ones (>30k tokens).
|
|
98
|
+
* **Dependency Resolution (`-r`):** Automatically finds and includes local files imported by your target files.
|
|
99
|
+
* **Scope Control:** You can specify folders or files to scan.
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
# Basic scan (Interactive confirmation if large)
|
|
105
|
+
cdg scan
|
|
106
|
+
|
|
107
|
+
# Scan specific folder with dependency resolution (Smart Context)
|
|
108
|
+
cdg scan src/main.py -r
|
|
109
|
+
|
|
110
|
+
# Force execution without confirmation (Good for CI/CD)
|
|
111
|
+
cdg scan -y --message "Automated snapshot"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 3. Incremental Changes (`diff`)
|
|
115
|
+
|
|
116
|
+
Tracks text-based changes between the last `scan` and the current working tree.
|
|
117
|
+
|
|
118
|
+
* **Output:** `.codigest/changes.diff`
|
|
119
|
+
* **Use Case:** "I modified 3 files. Here is exactly what changed since the last snapshot."
|
|
120
|
+
* **Note:** Checks against the internal Shadow Git, enabling tracking without committing to the real Git.
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
cdg diff
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### 4. Semantic Analysis (`semdiff`)
|
|
127
|
+
|
|
128
|
+
Analyzes **structural changes** (AST-based) rather than line-by-line text differences.
|
|
129
|
+
|
|
130
|
+
* **Output:** `.codigest/semdiff.xml`
|
|
131
|
+
* **Use Case:** "I refactored the API. Show me added/removed functions or signature changes."
|
|
132
|
+
* **Benefit:** Reduces token usage by ignoring formatting/comment changes.
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
cdg semdiff
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### 5. Project Tree (`tree`)
|
|
139
|
+
|
|
140
|
+
Visualizes the project structure. Respects `.gitignore`.
|
|
141
|
+
|
|
142
|
+
* **Style:** Professional, cleaner output without emojis for better readability.
|
|
143
|
+
* **Use Case:** Quickly verifying which files are being tracked.
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
cdg tree
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### 6. Architecture Digest (`digest`)
|
|
150
|
+
|
|
151
|
+
Generates a high-level outline of the project structure (Classes, Functions, Methods only).
|
|
152
|
+
|
|
153
|
+
* **Output:** `.codigest/digest.xml`
|
|
154
|
+
* **Use Case:** "Don't read the implementation details. Just understand the class hierarchy."
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
cdg digest
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Configuration
|
|
163
|
+
|
|
164
|
+
You can customize behavior in `.codigest/config.toml`.
|
|
165
|
+
|
|
166
|
+
```toml
|
|
167
|
+
[filter]
|
|
168
|
+
# Target extensions
|
|
169
|
+
extensions = [".py", ".ts", ".rs", ".md", ".json"]
|
|
170
|
+
|
|
171
|
+
# Exclude patterns (Gitignore syntax)
|
|
172
|
+
exclude_patterns = [
|
|
173
|
+
"*.lock",
|
|
174
|
+
"tests/data/",
|
|
175
|
+
"legacy_code/"
|
|
176
|
+
]
|
|
177
|
+
|
|
178
|
+
[output]
|
|
179
|
+
format = "xml"
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Architecture Details
|
|
183
|
+
|
|
184
|
+
**Context Anchor (Shadow Git)**
|
|
185
|
+
Codigest maintains a hidden, lightweight Git repository inside `.codigest/anchor/.shadow_git`.
|
|
186
|
+
|
|
187
|
+
* It is renamed to `.shadow_git` to prevent VS Code and other IDEs from confusing it with your project's actual repository.
|
|
188
|
+
* When you run `scan`, the current state is committed to this anchor.
|
|
189
|
+
* When you run `diff`, the tool compares your working directory against this anchor.
|
|
190
|
+
|
|
191
|
+
**Safety Mechanisms**
|
|
192
|
+
|
|
193
|
+
* **Pre-flight Check:** Calculates estimated token count before processing to prevent context overflow errors.
|
|
194
|
+
* **Structure-Aware Dedent:** Ensures XML tags are perfectly aligned (flush-left) to prevent indentation artifacts in LLM prompts.
|
|
195
|
+
* **XML Injection Protection:** Automatically escapes content within XML tags.
|
|
196
|
+
|
|
197
|
+
## License
|
|
198
|
+
|
|
199
|
+
MIT License
|
codigest-0.3.3/README.md
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Codigest
|
|
2
|
+
|
|
3
|
+
**Codigest** is a standalone CLI tool designed to extract, structure, and track the context of your codebase for Large Language Models (LLMs).
|
|
4
|
+
|
|
5
|
+
Unlike simple copy-paste tools, Codigest employs a **Context Anchor** system (Shadow Git) to track changes locally without polluting your main version control history. It also features **Semantic Analysis** to detect structural changes in your code.
|
|
6
|
+
|
|
7
|
+
> **Note:** You can use the short alias **`cdg`** instead of `codigest` for faster typing.
|
|
8
|
+
|
|
9
|
+
## Core Philosophy
|
|
10
|
+
|
|
11
|
+
* **Read-Only & Safe**: Codigest never modifies your source code. It only reads, analyzes, and formats context.
|
|
12
|
+
* **Context-Aware**: Instead of dumping raw text, it structures code into XML snapshots designed for LLM comprehension.
|
|
13
|
+
* **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.14 environment via `uv`, ensuring it never conflicts with your project's dependencies.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
Codigest runs as a global tool. You don't need to add it to your project's `requirements.txt`.
|
|
19
|
+
We strongly recommend using **uv** for the best experience.
|
|
20
|
+
|
|
21
|
+
### Step 1: Install `uv`
|
|
22
|
+
|
|
23
|
+
If you don't have `uv` installed, use **Winget** (Windows) or curl (macOS/Linux).
|
|
24
|
+
|
|
25
|
+
```powershell
|
|
26
|
+
# Windows (via Winget)
|
|
27
|
+
winget install --id astral-sh.uv
|
|
28
|
+
|
|
29
|
+
# macOS/Linux
|
|
30
|
+
curl -LsSf [https://astral.sh/uv/install.sh](https://astral.sh/uv/install.sh) | sh
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Step 2: Install `codigest`
|
|
35
|
+
|
|
36
|
+
Install Codigest globally. `uv` will automatically manage the required Python 3.14 environment for you.
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
uv tool install codigest
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Step 3: Update Path (Important!)
|
|
43
|
+
|
|
44
|
+
To run `codigest` (or `cdg`) from any terminal, ensure your tools directory is in your PATH.
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
uv tool update-shell
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
*> **Note:** Restart your terminal (or VS Code) after this step to apply changes.*
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Workflow & Commands
|
|
55
|
+
|
|
56
|
+
Once installed, use the **`cdg`** command anywhere.
|
|
57
|
+
|
|
58
|
+
### 1. Initialization
|
|
59
|
+
|
|
60
|
+
Sets up the `.codigest` directory and captures the initial baseline anchor.
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
cdg init
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 2. Full Context Snapshot (`scan`)
|
|
67
|
+
|
|
68
|
+
Scans the codebase and generates a structured XML snapshot. Includes a **Pre-flight Check** to prevent accidental token overflow.
|
|
69
|
+
|
|
70
|
+
* **Output:** `.codigest/snapshot.xml`
|
|
71
|
+
* **Features:**
|
|
72
|
+
* **Smart Confirmation:** Automatically skips confirmation for small contexts, but warns you for large ones (>30k tokens).
|
|
73
|
+
* **Dependency Resolution (`-r`):** Automatically finds and includes local files imported by your target files.
|
|
74
|
+
* **Scope Control:** You can specify folders or files to scan.
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Basic scan (Interactive confirmation if large)
|
|
80
|
+
cdg scan
|
|
81
|
+
|
|
82
|
+
# Scan specific folder with dependency resolution (Smart Context)
|
|
83
|
+
cdg scan src/main.py -r
|
|
84
|
+
|
|
85
|
+
# Force execution without confirmation (Good for CI/CD)
|
|
86
|
+
cdg scan -y --message "Automated snapshot"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 3. Incremental Changes (`diff`)
|
|
90
|
+
|
|
91
|
+
Tracks text-based changes between the last `scan` and the current working tree.
|
|
92
|
+
|
|
93
|
+
* **Output:** `.codigest/changes.diff`
|
|
94
|
+
* **Use Case:** "I modified 3 files. Here is exactly what changed since the last snapshot."
|
|
95
|
+
* **Note:** Checks against the internal Shadow Git, enabling tracking without committing to the real Git.
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
cdg diff
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 4. Semantic Analysis (`semdiff`)
|
|
102
|
+
|
|
103
|
+
Analyzes **structural changes** (AST-based) rather than line-by-line text differences.
|
|
104
|
+
|
|
105
|
+
* **Output:** `.codigest/semdiff.xml`
|
|
106
|
+
* **Use Case:** "I refactored the API. Show me added/removed functions or signature changes."
|
|
107
|
+
* **Benefit:** Reduces token usage by ignoring formatting/comment changes.
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
cdg semdiff
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### 5. Project Tree (`tree`)
|
|
114
|
+
|
|
115
|
+
Visualizes the project structure. Respects `.gitignore`.
|
|
116
|
+
|
|
117
|
+
* **Style:** Professional, cleaner output without emojis for better readability.
|
|
118
|
+
* **Use Case:** Quickly verifying which files are being tracked.
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
cdg tree
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### 6. Architecture Digest (`digest`)
|
|
125
|
+
|
|
126
|
+
Generates a high-level outline of the project structure (Classes, Functions, Methods only).
|
|
127
|
+
|
|
128
|
+
* **Output:** `.codigest/digest.xml`
|
|
129
|
+
* **Use Case:** "Don't read the implementation details. Just understand the class hierarchy."
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
cdg digest
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Configuration
|
|
138
|
+
|
|
139
|
+
You can customize behavior in `.codigest/config.toml`.
|
|
140
|
+
|
|
141
|
+
```toml
|
|
142
|
+
[filter]
|
|
143
|
+
# Target extensions
|
|
144
|
+
extensions = [".py", ".ts", ".rs", ".md", ".json"]
|
|
145
|
+
|
|
146
|
+
# Exclude patterns (Gitignore syntax)
|
|
147
|
+
exclude_patterns = [
|
|
148
|
+
"*.lock",
|
|
149
|
+
"tests/data/",
|
|
150
|
+
"legacy_code/"
|
|
151
|
+
]
|
|
152
|
+
|
|
153
|
+
[output]
|
|
154
|
+
format = "xml"
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Architecture Details
|
|
158
|
+
|
|
159
|
+
**Context Anchor (Shadow Git)**
|
|
160
|
+
Codigest maintains a hidden, lightweight Git repository inside `.codigest/anchor/.shadow_git`.
|
|
161
|
+
|
|
162
|
+
* It is renamed to `.shadow_git` to prevent VS Code and other IDEs from confusing it with your project's actual repository.
|
|
163
|
+
* When you run `scan`, the current state is committed to this anchor.
|
|
164
|
+
* When you run `diff`, the tool compares your working directory against this anchor.
|
|
165
|
+
|
|
166
|
+
**Safety Mechanisms**
|
|
167
|
+
|
|
168
|
+
* **Pre-flight Check:** Calculates estimated token count before processing to prevent context overflow errors.
|
|
169
|
+
* **Structure-Aware Dedent:** Ensures XML tags are perfectly aligned (flush-left) to prevent indentation artifacts in LLM prompts.
|
|
170
|
+
* **XML Injection Protection:** Automatically escapes content within XML tags.
|
|
171
|
+
|
|
172
|
+
## License
|
|
173
|
+
|
|
174
|
+
MIT License
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "codigest"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.3"
|
|
4
4
|
description = "Human-Centered Codebase Context Manager for LLMs"
|
|
5
5
|
authors = [{name = "Codigest Team", email = "sngd024@google.com"}]
|
|
6
6
|
readme = "README.md"
|
|
@@ -32,7 +32,8 @@ Issues = "https://github.com/SJB777/codigest/issues"
|
|
|
32
32
|
|
|
33
33
|
[project.scripts]
|
|
34
34
|
codigest = "codigest.main:app"
|
|
35
|
+
cdg = "codigest.main:app"
|
|
35
36
|
|
|
36
37
|
[build-system]
|
|
37
38
|
requires = ["hatchling"]
|
|
38
|
-
build-backend = "hatchling.build"
|
|
39
|
+
build-backend = "hatchling.build"
|
|
@@ -35,7 +35,7 @@ def handle(
|
|
|
35
35
|
# 1. Check Baseline Existence
|
|
36
36
|
last_update = anchor.get_last_update_time()
|
|
37
37
|
if last_update == "Never":
|
|
38
|
-
console.print("[yellow]
|
|
38
|
+
console.print("[yellow]No scan history found.[/yellow]")
|
|
39
39
|
console.print(" Run [bold cyan]codigest scan[/bold cyan] first to establish a baseline.")
|
|
40
40
|
raise typer.Exit(1)
|
|
41
41
|
|
|
@@ -84,10 +84,10 @@ def handle(
|
|
|
84
84
|
|
|
85
85
|
if copy:
|
|
86
86
|
pyperclip.copy(formatted_diff)
|
|
87
|
-
console.print("[dim]
|
|
87
|
+
console.print("[dim]Copied to clipboard[/dim]")
|
|
88
88
|
|
|
89
89
|
if save:
|
|
90
90
|
out_path = root_path / ".codigest" / "changes.diff"
|
|
91
91
|
out_path.parent.mkdir(exist_ok=True)
|
|
92
92
|
out_path.write_text(formatted_diff, encoding="utf-8")
|
|
93
|
-
console.print(f"[dim]
|
|
93
|
+
console.print(f"[dim]Saved to {out_path}[/dim]")
|
|
@@ -65,7 +65,7 @@ def handle(
|
|
|
65
65
|
instruction=message
|
|
66
66
|
)
|
|
67
67
|
except Exception as e:
|
|
68
|
-
console.print(f"[red]
|
|
68
|
+
console.print(f"[red]Rendering Failed:[/red] {e}")
|
|
69
69
|
raise typer.Exit(1)
|
|
70
70
|
|
|
71
71
|
progress.update(task, completed=100)
|
|
@@ -75,10 +75,10 @@ def handle(
|
|
|
75
75
|
|
|
76
76
|
if copy:
|
|
77
77
|
pyperclip.copy(digest_content)
|
|
78
|
-
console.print("[dim]
|
|
78
|
+
console.print("[dim]Copied to clipboard[/dim]")
|
|
79
79
|
|
|
80
80
|
if save:
|
|
81
81
|
out_path = root_path / ".codigest" / "digest.xml"
|
|
82
82
|
out_path.parent.mkdir(exist_ok=True)
|
|
83
83
|
out_path.write_text(digest_content, encoding="utf-8")
|
|
84
|
-
console.print(f"[dim]
|
|
84
|
+
console.print(f"[dim]Saved to {out_path}[/dim]")
|
|
@@ -3,8 +3,11 @@ import tomllib
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from rich.console import Console
|
|
5
5
|
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
6
|
+
from rich.panel import Panel
|
|
7
|
+
from rich.filesize import decimal
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
# [변경] resolver 추가
|
|
10
|
+
from ..core import scanner, structure, tags, prompts, processor, shadow, tokenizer, resolver
|
|
8
11
|
|
|
9
12
|
app = typer.Typer()
|
|
10
13
|
console = Console()
|
|
@@ -29,10 +32,6 @@ def _load_config_filters(root_path: Path):
|
|
|
29
32
|
return extensions, exclude_patterns
|
|
30
33
|
|
|
31
34
|
def _find_project_root(start_path: Path) -> Path:
|
|
32
|
-
"""
|
|
33
|
-
.codigest 폴더나 .git 폴더가 있는 상위 디렉토리를 찾습니다.
|
|
34
|
-
못 찾으면 start_path를 반환합니다.
|
|
35
|
-
"""
|
|
36
35
|
current = start_path.resolve()
|
|
37
36
|
for parent in [current] + list(current.parents):
|
|
38
37
|
if (parent / ".codigest").exists() or (parent / ".git").exists():
|
|
@@ -41,7 +40,6 @@ def _find_project_root(start_path: Path) -> Path:
|
|
|
41
40
|
|
|
42
41
|
@app.callback(invoke_without_command=True)
|
|
43
42
|
def handle(
|
|
44
|
-
# targets를 리스트로 받음 (여러 폴더 지정 가능)
|
|
45
43
|
targets: list[Path] = typer.Argument(
|
|
46
44
|
None,
|
|
47
45
|
help="Specific files or directories to scan (Scope)",
|
|
@@ -52,37 +50,37 @@ def handle(
|
|
|
52
50
|
all: bool = typer.Option(False, "--all", "-a", help="Ignore config filters"),
|
|
53
51
|
message: str = typer.Option("", "--message", "-m", help="Add specific instruction"),
|
|
54
52
|
line_numbers: bool = typer.Option(False, "--lines", "-l", help="Add line numbers to code blocks"),
|
|
53
|
+
yes: bool = typer.Option(False, "-y", "--yes", help="Skip confirmation prompt"),
|
|
54
|
+
# [추가] 의존성 해결 옵션
|
|
55
|
+
resolve: bool = typer.Option(False, "-r", "--resolve", help="Recursively resolve imports for local files"),
|
|
55
56
|
):
|
|
56
57
|
"""
|
|
57
58
|
Scans the codebase.
|
|
58
59
|
If TARGETS provided, only scans those paths within the project.
|
|
59
60
|
"""
|
|
60
|
-
# 1. 프로젝트 루트 결정 (현재 위치 기준 상위 탐색)
|
|
61
61
|
root_path = _find_project_root(Path.cwd())
|
|
62
|
-
|
|
63
|
-
# 2. Scope 설정
|
|
64
|
-
# 입력된 targets가 있으면 사용, 없으면 [root_path] 전체
|
|
65
62
|
scan_scope = targets if targets else None
|
|
66
63
|
|
|
67
|
-
#
|
|
64
|
+
# Scope validation
|
|
68
65
|
if scan_scope:
|
|
69
66
|
for p in scan_scope:
|
|
70
67
|
if not p.is_relative_to(root_path):
|
|
71
|
-
|
|
68
|
+
# [수정] 이모티콘 제거
|
|
69
|
+
console.print(f"[yellow][Warning] {p.name} is outside project root {root_path.name}[/yellow]")
|
|
72
70
|
|
|
71
|
+
# Init check
|
|
73
72
|
artifact_dir = root_path / ".codigest"
|
|
74
|
-
|
|
75
73
|
if not artifact_dir.exists():
|
|
76
|
-
|
|
74
|
+
# [수정] 이모티콘 제거
|
|
75
|
+
console.print(f"[yellow][Warning] .codigest directory missing in {root_path.name}. Running init...[/yellow]")
|
|
77
76
|
try:
|
|
78
77
|
artifact_dir.mkdir(exist_ok=True)
|
|
79
78
|
except PermissionError:
|
|
80
|
-
|
|
79
|
+
# [수정] 이모티콘 제거
|
|
80
|
+
console.print(f"[red][Error] Cannot create .codigest at {root_path}[/red]")
|
|
81
81
|
raise typer.Exit(1)
|
|
82
82
|
|
|
83
83
|
output_path = artifact_dir / output
|
|
84
|
-
|
|
85
|
-
# Initialize Engines
|
|
86
84
|
prompt_engine = prompts.get_engine(root_path)
|
|
87
85
|
anchor = shadow.ContextAnchor(root_path)
|
|
88
86
|
|
|
@@ -90,22 +88,79 @@ def handle(
|
|
|
90
88
|
if not all:
|
|
91
89
|
extensions, extra_ignores = _load_config_filters(root_path)
|
|
92
90
|
|
|
91
|
+
# ----------------------------------------------------------------
|
|
92
|
+
# 1. Pre-flight Scan (Fast)
|
|
93
|
+
# ----------------------------------------------------------------
|
|
93
94
|
with Progress(
|
|
94
95
|
SpinnerColumn(),
|
|
95
|
-
|
|
96
|
+
# [수정] 이모티콘 제거
|
|
97
|
+
TextColumn("[bold blue]Scanning file structure...[/bold blue]"),
|
|
96
98
|
transient=True,
|
|
97
99
|
console=console
|
|
98
|
-
):
|
|
99
|
-
|
|
100
|
-
# File Discovery
|
|
100
|
+
) as progress:
|
|
101
|
+
task = progress.add_task("scanning", total=None)
|
|
101
102
|
files = scanner.scan_project(
|
|
102
103
|
root_path,
|
|
103
104
|
extensions,
|
|
104
105
|
extra_ignores,
|
|
105
106
|
include_paths=scan_scope
|
|
106
107
|
)
|
|
107
|
-
|
|
108
|
-
#
|
|
108
|
+
|
|
109
|
+
# [추가] 의존성 해결 로직 (Resolver)
|
|
110
|
+
if resolve:
|
|
111
|
+
progress.update(task, description="[bold blue]Resolving dependencies...[/bold blue]")
|
|
112
|
+
initial_count = len(files)
|
|
113
|
+
files = resolver.resolve_dependencies(root_path, files)
|
|
114
|
+
resolved_count = len(files)
|
|
115
|
+
if resolved_count > initial_count:
|
|
116
|
+
console.print(f"[dim]Resolved {resolved_count - initial_count} dependency files.[/dim]")
|
|
117
|
+
|
|
118
|
+
progress.update(task, completed=100)
|
|
119
|
+
|
|
120
|
+
# Calculate Stats
|
|
121
|
+
total_files = len(files)
|
|
122
|
+
total_size = sum(f.stat().st_size for f in files)
|
|
123
|
+
est_tokens = int(total_size / 4)
|
|
124
|
+
|
|
125
|
+
# Display Plan
|
|
126
|
+
# [수정] 이모티콘 제거 및 레이아웃 정리
|
|
127
|
+
console.print(Panel(f"""[bold]Scan Plan[/bold]
|
|
128
|
+
Target: [cyan]{root_path}[/cyan]
|
|
129
|
+
Scope: {total_files} files
|
|
130
|
+
Est. Size: {decimal(total_size)}
|
|
131
|
+
Est. Tokens: ~{est_tokens:,}""", expand=False))
|
|
132
|
+
|
|
133
|
+
# --- Smart Confirmation Logic ---
|
|
134
|
+
TOKEN_THRESHOLD = 30000
|
|
135
|
+
FILE_COUNT_THRESHOLD = 100
|
|
136
|
+
|
|
137
|
+
is_large_context = est_tokens > TOKEN_THRESHOLD or total_files > FILE_COUNT_THRESHOLD
|
|
138
|
+
|
|
139
|
+
if yes:
|
|
140
|
+
pass # Explicit override
|
|
141
|
+
elif is_large_context:
|
|
142
|
+
# [수정] 이모티콘 제거
|
|
143
|
+
console.print(f"[yellow][Warning] Large context detected (> {TOKEN_THRESHOLD:,} tokens or > {FILE_COUNT_THRESHOLD} files).[/yellow]")
|
|
144
|
+
# [수정] 이모티콘 제거
|
|
145
|
+
if not typer.confirm("Proceed with digestion?"):
|
|
146
|
+
console.print("[red]Aborted.[/red]")
|
|
147
|
+
raise typer.Exit()
|
|
148
|
+
else:
|
|
149
|
+
# [수정] 이모티콘 제거
|
|
150
|
+
console.print("[dim]Small context detected. Automatically proceeding...[/dim]")
|
|
151
|
+
|
|
152
|
+
# ----------------------------------------------------------------
|
|
153
|
+
# 2. Execution (Heavy)
|
|
154
|
+
# ----------------------------------------------------------------
|
|
155
|
+
with Progress(
|
|
156
|
+
SpinnerColumn(),
|
|
157
|
+
# [수정] 이모티콘 제거
|
|
158
|
+
TextColumn("[bold blue]Generating Snapshot...[/bold blue]"),
|
|
159
|
+
transient=True,
|
|
160
|
+
console=console
|
|
161
|
+
) as progress:
|
|
162
|
+
|
|
163
|
+
# Diff Report
|
|
109
164
|
if anchor.has_history():
|
|
110
165
|
try:
|
|
111
166
|
diff_content = anchor.get_changes(files)
|
|
@@ -115,17 +170,16 @@ def handle(
|
|
|
115
170
|
except Exception:
|
|
116
171
|
pass
|
|
117
172
|
|
|
118
|
-
#
|
|
173
|
+
# Tree Generation
|
|
119
174
|
tree_str = structure.generate_ascii_tree(files, root_path)
|
|
120
175
|
|
|
121
|
-
# Content
|
|
176
|
+
# Content Reading & Tagging
|
|
122
177
|
file_blocks = []
|
|
123
178
|
for file_path in files:
|
|
124
179
|
rel_path = file_path.relative_to(root_path).as_posix()
|
|
125
180
|
try:
|
|
126
181
|
content = processor.read_file_content(file_path, add_line_numbers=line_numbers)
|
|
127
182
|
|
|
128
|
-
|
|
129
183
|
block = tags.file(rel_path, content)
|
|
130
184
|
file_blocks.append(block)
|
|
131
185
|
except Exception:
|
|
@@ -133,7 +187,7 @@ def handle(
|
|
|
133
187
|
|
|
134
188
|
source_code_blob = "\n\n".join(file_blocks)
|
|
135
189
|
|
|
136
|
-
#
|
|
190
|
+
# Template Rendering
|
|
137
191
|
try:
|
|
138
192
|
snapshot_content = prompt_engine.render(
|
|
139
193
|
"snapshot",
|
|
@@ -143,33 +197,34 @@ def handle(
|
|
|
143
197
|
instruction=message
|
|
144
198
|
)
|
|
145
199
|
except Exception as e:
|
|
146
|
-
|
|
200
|
+
# [수정] 이모티콘 제거
|
|
201
|
+
console.print(f"[red][Error] Template Rendering Failed:[/red] {e}")
|
|
147
202
|
raise typer.Exit(1)
|
|
148
203
|
|
|
149
|
-
#
|
|
204
|
+
# Final Actions
|
|
150
205
|
try:
|
|
151
206
|
anchor.update(files)
|
|
152
207
|
except Exception as e:
|
|
153
|
-
|
|
208
|
+
# [수정] 이모티콘 제거
|
|
209
|
+
console.print(f"[yellow][Warning] Failed to update context anchor: {e}[/yellow]")
|
|
154
210
|
|
|
155
|
-
# Save to Disk
|
|
156
211
|
try:
|
|
157
212
|
output_path.write_text(snapshot_content, encoding="utf-8")
|
|
158
213
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
console.print("[
|
|
164
|
-
console.print(f"
|
|
165
|
-
console.print(f" 📊 Size: {size_kb:.1f} KB | [bold cyan]~{token_count:,} Tokens[/bold cyan]")
|
|
166
|
-
console.print(f" 📂 Files: {len(files)}")
|
|
214
|
+
final_token_count = tokenizer.estimate_tokens(snapshot_content)
|
|
215
|
+
|
|
216
|
+
# [수정] 이모티콘 제거 및 메시지 단순화
|
|
217
|
+
console.print("[bold green]Snapshot Saved![/bold green]")
|
|
218
|
+
console.print(f" Path: [underline]{output_path}[/underline]")
|
|
219
|
+
console.print(f" Final Tokens: [bold cyan]~{final_token_count:,}[/bold cyan]")
|
|
167
220
|
|
|
168
221
|
if anchor.has_history():
|
|
169
222
|
pre_diff_path = artifact_dir / "previous_changes.diff"
|
|
170
223
|
if pre_diff_path.exists() and pre_diff_path.stat().st_size > 0:
|
|
171
|
-
|
|
224
|
+
# [수정] 이모티콘 제거
|
|
225
|
+
console.print(f" [dim]Changes before this scan saved to: {pre_diff_path.name}[/dim]")
|
|
172
226
|
|
|
173
227
|
except Exception as e:
|
|
174
|
-
|
|
228
|
+
# [수정] 이모티콘 제거
|
|
229
|
+
console.print(f"[bold red][Error] Save Failed:[/bold red] {e}")
|
|
175
230
|
raise typer.Exit(1)
|