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.
Files changed (29) hide show
  1. codigest-0.3.3/PKG-INFO +199 -0
  2. codigest-0.3.3/README.md +174 -0
  3. {codigest-0.3.2 → codigest-0.3.3}/pyproject.toml +3 -2
  4. {codigest-0.3.2 → codigest-0.3.3}/src/codigest/commands/diff.py +3 -3
  5. {codigest-0.3.2 → codigest-0.3.3}/src/codigest/commands/digest.py +3 -3
  6. {codigest-0.3.2 → codigest-0.3.3}/src/codigest/commands/scan.py +96 -41
  7. {codigest-0.3.2 → codigest-0.3.3}/src/codigest/commands/semdiff.py +4 -4
  8. {codigest-0.3.2 → codigest-0.3.3}/src/codigest/commands/tree.py +20 -22
  9. codigest-0.3.3/src/codigest/core/resolver.py +107 -0
  10. {codigest-0.3.2 → codigest-0.3.3}/src/codigest/core/semdiff.py +1 -1
  11. {codigest-0.3.2 → codigest-0.3.3}/src/codigest/core/shadow.py +39 -24
  12. {codigest-0.3.2 → codigest-0.3.3}/src/codigest/main.py +1 -1
  13. {codigest-0.3.2 → codigest-0.3.3}/uv.lock +1 -1
  14. codigest-0.3.2/PKG-INFO +0 -176
  15. codigest-0.3.2/README.md +0 -151
  16. {codigest-0.3.2 → codigest-0.3.3}/.github/workflows/publish.yml +0 -0
  17. {codigest-0.3.2 → codigest-0.3.3}/.gitignore +0 -0
  18. {codigest-0.3.2 → codigest-0.3.3}/.python-version +0 -0
  19. {codigest-0.3.2 → codigest-0.3.3}/src/codigest/__init__.py +0 -0
  20. {codigest-0.3.2 → codigest-0.3.3}/src/codigest/__main__.py +0 -0
  21. {codigest-0.3.2 → codigest-0.3.3}/src/codigest/commands/init.py +0 -0
  22. {codigest-0.3.2 → codigest-0.3.3}/src/codigest/config.py +0 -0
  23. {codigest-0.3.2 → codigest-0.3.3}/src/codigest/core/git_ops.py +0 -0
  24. {codigest-0.3.2 → codigest-0.3.3}/src/codigest/core/processor.py +0 -0
  25. {codigest-0.3.2 → codigest-0.3.3}/src/codigest/core/prompts.py +0 -0
  26. {codigest-0.3.2 → codigest-0.3.3}/src/codigest/core/scanner.py +0 -0
  27. {codigest-0.3.2 → codigest-0.3.3}/src/codigest/core/structure.py +0 -0
  28. {codigest-0.3.2 → codigest-0.3.3}/src/codigest/core/tags.py +0 -0
  29. {codigest-0.3.2 → codigest-0.3.3}/src/codigest/core/tokenizer.py +0 -0
@@ -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
@@ -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.2"
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]⚠️ No scan history found.[/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]📋 Copied to clipboard[/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]💾 Saved to {out_path}[/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]❌ Rendering Failed:[/red] {e}")
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]📋 Copied to clipboard[/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]💾 Saved to {out_path}[/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
- from ..core import scanner, structure, tags, prompts, processor, shadow, tokenizer
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
- # 검증: targets가 root_path 밖에 있으면 경고 (옵션)
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
- console.print(f"[yellow]⚠️ Warning: {p.name} is outside project root {root_path.name}[/yellow]")
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
- console.print(f"[yellow]⚠️ .codigest directory missing in {root_path.name}. Running init...[/yellow]")
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
- console.print(f"[red]❌ Error: Cannot create .codigest at {root_path}[/red]")
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
- TextColumn("[bold blue]Scanning Project...[/bold blue]"),
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
- # Pre-scan Diff Report
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
- # Structure Generation
173
+ # Tree Generation
119
174
  tree_str = structure.generate_ascii_tree(files, root_path)
120
175
 
121
- # Content Processing
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
- # Final Assembly
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
- console.print(f"[red]❌ Template Rendering Failed:[/red] {e}")
200
+ # [수정] 이모티콘 제거
201
+ console.print(f"[red][Error] Template Rendering Failed:[/red] {e}")
147
202
  raise typer.Exit(1)
148
203
 
149
- # Update Anchor
204
+ # Final Actions
150
205
  try:
151
206
  anchor.update(files)
152
207
  except Exception as e:
153
- console.print(f"[yellow]⚠️ Warning: Failed to update context anchor: {e}[/yellow]")
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
- size_kb = len(snapshot_content) / 1024
160
- token_count = tokenizer.estimate_tokens(snapshot_content)
161
-
162
- # Report
163
- console.print("[bold green]✔ Snapshot Saved![/bold green]")
164
- console.print(f" 📄 Path: [underline]{output_path}[/underline]")
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
- console.print(f" [dim]ℹ️ Changes before this scan saved to: {pre_diff_path.name}[/dim]")
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
- console.print(f"[bold red]❌ Save Failed:[/bold red] {e}")
228
+ # [수정] 이모티콘 제거
229
+ console.print(f"[bold red][Error] Save Failed:[/bold red] {e}")
175
230
  raise typer.Exit(1)