codigest 0.3.7__tar.gz → 0.3.9__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 (33) hide show
  1. {codigest-0.3.7 → codigest-0.3.9}/PKG-INFO +5 -5
  2. {codigest-0.3.7 → codigest-0.3.9}/pyproject.toml +4 -4
  3. {codigest-0.3.7 → codigest-0.3.9}/src/codigest/commands/diff.py +15 -23
  4. {codigest-0.3.7 → codigest-0.3.9}/src/codigest/commands/digest.py +16 -24
  5. {codigest-0.3.7 → codigest-0.3.9}/src/codigest/commands/scan.py +13 -12
  6. {codigest-0.3.7 → codigest-0.3.9}/src/codigest/commands/semdiff.py +16 -24
  7. {codigest-0.3.7 → codigest-0.3.9}/src/codigest/commands/tree.py +20 -11
  8. codigest-0.3.9/src/codigest/core/output.py +50 -0
  9. codigest-0.3.9/src/codigest/options.py +31 -0
  10. {codigest-0.3.7 → codigest-0.3.9}/uv.lock +1 -1
  11. {codigest-0.3.7 → codigest-0.3.9}/.github/workflows/publish.yml +0 -0
  12. {codigest-0.3.7 → codigest-0.3.9}/.gitignore +0 -0
  13. {codigest-0.3.7 → codigest-0.3.9}/.python-version +0 -0
  14. {codigest-0.3.7 → codigest-0.3.9}/README.md +0 -0
  15. {codigest-0.3.7 → codigest-0.3.9}/src/codigest/__init__.py +0 -0
  16. {codigest-0.3.7 → codigest-0.3.9}/src/codigest/__main__.py +0 -0
  17. {codigest-0.3.7 → codigest-0.3.9}/src/codigest/commands/clean.py +0 -0
  18. {codigest-0.3.7 → codigest-0.3.9}/src/codigest/commands/init.py +0 -0
  19. {codigest-0.3.7 → codigest-0.3.9}/src/codigest/commands/status.py +0 -0
  20. {codigest-0.3.7 → codigest-0.3.9}/src/codigest/config.py +0 -0
  21. {codigest-0.3.7 → codigest-0.3.9}/src/codigest/core/cache.py +0 -0
  22. {codigest-0.3.7 → codigest-0.3.9}/src/codigest/core/common.py +0 -0
  23. {codigest-0.3.7 → codigest-0.3.9}/src/codigest/core/git_ops.py +0 -0
  24. {codigest-0.3.7 → codigest-0.3.9}/src/codigest/core/processor.py +0 -0
  25. {codigest-0.3.7 → codigest-0.3.9}/src/codigest/core/prompts.py +0 -0
  26. {codigest-0.3.7 → codigest-0.3.9}/src/codigest/core/resolver.py +0 -0
  27. {codigest-0.3.7 → codigest-0.3.9}/src/codigest/core/scanner.py +0 -0
  28. {codigest-0.3.7 → codigest-0.3.9}/src/codigest/core/semdiff.py +0 -0
  29. {codigest-0.3.7 → codigest-0.3.9}/src/codigest/core/shadow.py +0 -0
  30. {codigest-0.3.7 → codigest-0.3.9}/src/codigest/core/structure.py +0 -0
  31. {codigest-0.3.7 → codigest-0.3.9}/src/codigest/core/tags.py +0 -0
  32. {codigest-0.3.7 → codigest-0.3.9}/src/codigest/core/tokenizer.py +0 -0
  33. {codigest-0.3.7 → codigest-0.3.9}/src/codigest/main.py +0 -0
@@ -1,10 +1,10 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: codigest
3
- Version: 0.3.7
3
+ Version: 0.3.9
4
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
5
+ Project-URL: Homepage, https://github.com/NaNIsaac/codigest
6
+ Project-URL: Repository, https://github.com/NaNIsaac/codigest
7
+ Project-URL: Issues, https://github.com/NaNIsaac/codigest/issues
8
8
  Author-email: Codigest Team <sngd024@google.com>
9
9
  License: MIT
10
10
  Classifier: Intended Audience :: Developers
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "codigest"
3
- version = "0.3.7"
3
+ version = "0.3.9"
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"
@@ -26,9 +26,9 @@ classifiers = [
26
26
  ]
27
27
 
28
28
  [project.urls]
29
- Homepage = "https://github.com/SJB777/codigest"
30
- Repository = "https://github.com/SJB777/codigest"
31
- Issues = "https://github.com/SJB777/codigest/issues"
29
+ Homepage = "https://github.com/NaNIsaac/codigest"
30
+ Repository = "https://github.com/NaNIsaac/codigest"
31
+ Issues = "https://github.com/NaNIsaac/codigest/issues"
32
32
 
33
33
  [project.scripts]
34
34
  codigest = "codigest.main:app"
@@ -1,14 +1,12 @@
1
1
  import typer
2
- import pyperclip
3
2
  from pathlib import Path
4
- from rich.console import Console
5
3
  from rich.progress import Progress, SpinnerColumn, TextColumn
6
4
 
5
+ from ..options import CopyOption, StdoutOption, ResolveOption, MessageOption
7
6
  from ..core import prompts, shadow, tags, common
7
+ from ..core.output import OutputHandler
8
8
 
9
9
  app = typer.Typer()
10
- console = Console()
11
- err_console = Console(stderr=True)
12
10
 
13
11
  @app.callback(invoke_without_command=True)
14
12
  def handle(
@@ -20,18 +18,18 @@ def handle(
20
18
  dir_okay=True,
21
19
  resolve_path=True
22
20
  ),
23
- copy: bool = typer.Option(True, help="Auto-copy to clipboard"),
21
+ copy: CopyOption = True,
24
22
  save: bool = typer.Option(True, help="Save to .codigest/changes.diff"),
25
- stdout: bool = typer.Option(False, "-s", "--stdout", help="Print output to terminal (stdout) instead of file"),
26
- message: str = typer.Option("", "--message", "-m", help="Add specific instruction context"),
27
- # [추가] resolve 옵션
28
- resolve: bool = typer.Option(False, "-r", "--resolve", help="Recursively resolve imports"),
23
+ stdout: StdoutOption = False,
24
+ message: MessageOption = "",
25
+ resolve: ResolveOption = False,
29
26
  ):
30
27
  """
31
28
  [Context Update] Shows changes since the last 'codigest scan'.
32
29
  Useful for updating LLM context without re-uploading everything.
33
30
  """
34
- log_console = err_console if stdout else console
31
+ handler = OutputHandler(stdout=stdout, copy=copy)
32
+ log_console = handler.log_console
35
33
 
36
34
  # [1] Context Setup
37
35
  ctx = common.get_context(target)
@@ -87,17 +85,11 @@ def handle(
87
85
  # [4] Output
88
86
  log_console.print(f"[bold green]Changes Detected![/bold green] ({len(formatted_diff)} chars)")
89
87
 
90
- if copy:
91
- try:
92
- pyperclip.copy(formatted_diff)
93
- log_console.print("[dim]Clipboard copied[/dim]")
94
- except Exception:
95
- log_console.print("[dim]⚠️ Clipboard unavailable (skipped copy)[/dim]")
88
+ def _save():
89
+ if save:
90
+ out_path = root_path / ".codigest" / "changes.diff"
91
+ out_path.parent.mkdir(exist_ok=True)
92
+ out_path.write_text(formatted_diff, encoding="utf-8")
93
+ log_console.print(f"[dim]Saved to {out_path}[/dim]")
96
94
 
97
- if stdout:
98
- print(formatted_diff)
99
- elif save:
100
- out_path = root_path / ".codigest" / "changes.diff"
101
- out_path.parent.mkdir(exist_ok=True)
102
- out_path.write_text(formatted_diff, encoding="utf-8")
103
- log_console.print(f"[dim]Saved to {out_path}[/dim]")
95
+ handler.handle_output(formatted_diff, save_action=_save, item_name="diff")
@@ -1,29 +1,27 @@
1
1
  import typer
2
- import pyperclip
3
2
  from pathlib import Path
4
- from rich.console import Console
5
3
  from rich.progress import Progress, SpinnerColumn, TextColumn
6
4
 
5
+ from ..options import CopyOption, StdoutOption, ResolveOption, MessageOption
7
6
  from ..core import structure, prompts, semdiff, tags, tokenizer, common, cache
7
+ from ..core.output import OutputHandler
8
8
 
9
9
  app = typer.Typer()
10
- console = Console()
11
- err_console = Console(stderr=True)
12
10
 
13
11
  @app.callback(invoke_without_command=True)
14
12
  def handle(
15
13
  target: Path = typer.Argument(Path.cwd(), help="Target directory"),
16
- copy: bool = typer.Option(True, help="Auto-copy to clipboard"),
14
+ copy: CopyOption = True,
17
15
  save: bool = typer.Option(True, help="Save to .codigest/digest.txt"),
18
- stdout: bool = typer.Option(False, "-s", "--stdout", help="Print output to terminal (stdout) instead of file"),
19
- message: str = typer.Option("", "--message", "-m", help="Add specific instruction"),
20
- # [추가]
21
- resolve: bool = typer.Option(False, "-r", "--resolve", help="Recursively resolve imports"),
16
+ stdout: StdoutOption = False,
17
+ message: MessageOption = "",
18
+ resolve: ResolveOption = False,
22
19
  ):
23
20
  """
24
21
  [Architectural View] Summarizes the codebase structure (Classes/Functions only).
25
22
  """
26
- log_console = err_console if stdout else console
23
+ handler = OutputHandler(stdout=stdout, copy=copy)
24
+ log_console = handler.log_console
27
25
 
28
26
  # [1] Context Setup
29
27
  ctx = common.get_context(target)
@@ -92,17 +90,11 @@ def handle(
92
90
  token_count = tokenizer.estimate_tokens(digest_content)
93
91
  log_console.print(f"[bold green]Digest Generated![/bold green] ([bold cyan]~{token_count:,} Tokens[/bold cyan])")
94
92
 
95
- if copy:
96
- try:
97
- pyperclip.copy(digest_content)
98
- log_console.print("[dim]📋 Copied to clipboard[/dim]")
99
- except Exception:
100
- log_console.print("[dim]⚠️ Clipboard unavailable (skipped copy)[/dim]")
101
-
102
- if stdout:
103
- print(digest_content)
104
- elif save:
105
- out_path = root_path / ".codigest" / "digest.txt"
106
- out_path.parent.mkdir(exist_ok=True)
107
- out_path.write_text(digest_content, encoding="utf-8")
108
- log_console.print(f"[dim]💾 Saved to {out_path}[/dim]")
93
+ def _save():
94
+ if save:
95
+ out_path = root_path / ".codigest" / "digest.txt"
96
+ out_path.parent.mkdir(exist_ok=True)
97
+ out_path.write_text(digest_content, encoding="utf-8")
98
+ log_console.print(f"[dim]💾 Saved to {out_path}[/dim]")
99
+
100
+ handler.handle_output(digest_content, save_action=_save, item_name="digest")
@@ -1,16 +1,15 @@
1
1
  import sys
2
2
  import typer
3
3
  from pathlib import Path
4
- from rich.console import Console
5
4
  from rich.progress import Progress, SpinnerColumn, TextColumn
6
5
  from rich.panel import Panel
7
6
  from rich.filesize import decimal
8
7
 
8
+ from ..options import CopyOption, StdoutOption, ResolveOption, MessageOption, AllOption
9
9
  from ..core import structure, tags, prompts, processor, shadow, tokenizer, common, cache
10
+ from ..core.output import OutputHandler
10
11
 
11
12
  app = typer.Typer()
12
- console = Console()
13
- err_console = Console(stderr=True)
14
13
 
15
14
  @app.callback(invoke_without_command=True)
16
15
  def handle(
@@ -21,18 +20,20 @@ def handle(
21
20
  resolve_path=True
22
21
  ),
23
22
  output: str = typer.Option("snapshot.txt", "-o", "--output", help="Output filename inside .codigest/"),
24
- stdout: bool = typer.Option(False, "-s", "--stdout", help="Print output to terminal (stdout) instead of file"),
25
- all: bool = typer.Option(False, "--all", "-a", help="Ignore config filters"),
26
- message: str = typer.Option("", "--message", "-m", help="Add specific instruction"),
23
+ copy: CopyOption = True,
24
+ stdout: StdoutOption = False,
25
+ all: AllOption = False,
26
+ message: MessageOption = "",
27
27
  line_numbers: bool = typer.Option(False, "--lines", "-l", help="Add line numbers to code blocks"),
28
28
  yes: bool = typer.Option(False, "-y", "--yes", help="Skip confirmation prompt"),
29
- resolve: bool = typer.Option(False, "-r", "--resolve", help="Recursively resolve imports for local files"),
29
+ resolve: ResolveOption = False,
30
30
  ):
31
31
  """
32
32
  Scans the codebase.
33
33
  If TARGETS provided, only scans those paths within the project.
34
34
  """
35
- log_console = err_console if stdout else console
35
+ handler = OutputHandler(stdout=stdout, copy=copy)
36
+ log_console = handler.log_console
36
37
 
37
38
  # [1] Context Setup (Centralized)
38
39
  ctx = common.get_context(targets)
@@ -168,9 +169,7 @@ def handle(
168
169
  except Exception as e:
169
170
  log_console.print(f"[yellow][Warning] Failed to update context anchor: {e}[/yellow]")
170
171
 
171
- if stdout:
172
- print(snapshot_content)
173
- else:
172
+ def _save():
174
173
  try:
175
174
  output_path.write_text(snapshot_content, encoding="utf-8")
176
175
  final_token_count = tokenizer.estimate_tokens(snapshot_content)
@@ -186,4 +185,6 @@ def handle(
186
185
 
187
186
  except Exception as e:
188
187
  log_console.print(f"[bold red][Error] Save Failed:[/bold red] {e}")
189
- raise typer.Exit(1)
188
+ raise typer.Exit(1)
189
+
190
+ handler.handle_output(snapshot_content, save_action=_save)
@@ -1,30 +1,28 @@
1
1
  import typer
2
- import pyperclip
3
2
  from pathlib import Path
4
- from rich.console import Console
5
3
  from rich.progress import Progress, SpinnerColumn, TextColumn
6
4
 
5
+ from ..options import CopyOption, StdoutOption, ResolveOption, MessageOption
7
6
  from ..core import prompts, shadow, semdiff, tags, tokenizer, common
7
+ from ..core.output import OutputHandler
8
8
 
9
9
  app = typer.Typer()
10
- console = Console()
11
- err_console = Console(stderr=True)
12
10
 
13
11
  @app.callback(invoke_without_command=True)
14
12
  def handle(
15
13
  target: Path = typer.Argument(Path.cwd(), help="Target directory"),
16
- copy: bool = typer.Option(True, help="Auto-copy to clipboard"),
14
+ copy: CopyOption = True,
17
15
  save: bool = typer.Option(True, help="Save to .codigest/semdiff.txt"),
18
- stdout: bool = typer.Option(False, "-s", "--stdout", help="Print output to terminal (stdout) instead of file"),
19
- message: str = typer.Option("", "--message", "-m", help="Add specific instruction"),
20
- # [추가] resolve 옵션
21
- resolve: bool = typer.Option(False, "-r", "--resolve", help="Recursively resolve imports"),
16
+ stdout: StdoutOption = False,
17
+ message: MessageOption = "",
18
+ resolve: ResolveOption = False,
22
19
  ):
23
20
  """
24
21
  [Advanced] Generates a Semantic Diff (AST-based) report.
25
22
  Shows ADDED/REMOVED/MODIFIED functions & classes instead of raw text lines.
26
23
  """
27
- log_console = err_console if stdout else console
24
+ handler = OutputHandler(stdout=stdout, copy=copy)
25
+ log_console = handler.log_console
28
26
 
29
27
  # [1] Context Setup
30
28
  ctx = common.get_context(target)
@@ -134,17 +132,11 @@ def handle(
134
132
  token_count = tokenizer.estimate_tokens(final_output)
135
133
  log_console.print(f"[bold green]SemDiff Generated![/bold green] ([bold cyan]~{token_count:,} Tokens[/bold cyan])")
136
134
 
137
- if copy:
138
- try:
139
- pyperclip.copy(final_output)
140
- log_console.print("[dim]Clipboard copied[/dim]")
141
- except Exception:
142
- log_console.print("[dim]⚠️ Clipboard unavailable (skipped copy)[/dim]")
143
-
144
- if stdout:
145
- print(final_output)
146
- elif save:
147
- out_path = root_path / ".codigest" / "semdiff.txt"
148
- out_path.parent.mkdir(exist_ok=True)
149
- out_path.write_text(final_output, encoding="utf-8")
150
- log_console.print(f"[dim]Saved to {out_path}[/dim]")
135
+ def _save():
136
+ if save:
137
+ out_path = root_path / ".codigest" / "semdiff.txt"
138
+ out_path.parent.mkdir(exist_ok=True)
139
+ out_path.write_text(final_output, encoding="utf-8")
140
+ log_console.print(f"[dim]Saved to {out_path}[/dim]")
141
+
142
+ handler.handle_output(final_output, save_action=_save, item_name="semdiff")
@@ -1,14 +1,14 @@
1
1
  import typer
2
2
  from pathlib import Path
3
- from rich.console import Console
4
3
  from rich.tree import Tree
5
4
  from rich.text import Text
6
5
  from rich.filesize import decimal
7
6
 
8
- from ..core import common
7
+ from ..options import CopyOption, StdoutOption, ResolveOption, AllOption
8
+ from ..core import common, structure
9
+ from ..core.output import OutputHandler
9
10
 
10
11
  app = typer.Typer()
11
- console = Console()
12
12
 
13
13
  def _build_rich_tree(root_path: Path, files: list[Path]) -> Tree:
14
14
  # (기존의 이모티콘 없는 버전 로직 유지)
@@ -16,7 +16,6 @@ def _build_rich_tree(root_path: Path, files: list[Path]) -> Tree:
16
16
  dir_nodes = {root_path: tree}
17
17
 
18
18
  for path in files:
19
- # (중략: 기존 트리 생성 로직)
20
19
  # External path 처리 (루트 밖의 파일)
21
20
  try:
22
21
  relative = path.relative_to(root_path)
@@ -54,12 +53,17 @@ def _build_rich_tree(root_path: Path, files: list[Path]) -> Tree:
54
53
  @app.callback(invoke_without_command=True)
55
54
  def handle(
56
55
  target: Path = typer.Argument(Path.cwd(), help="Target directory"),
57
- all: bool = typer.Option(False, "--all", "-a", help="Ignore config filters"),
58
- resolve: bool = typer.Option(False, "-r", "--resolve", help="Recursively resolve imports"),
56
+ copy: CopyOption = False,
57
+ stdout: StdoutOption = False,
58
+ all: AllOption = False,
59
+ resolve: ResolveOption = False,
59
60
  ):
60
61
  """
61
62
  [Visual] Print the project directory tree.
62
63
  """
64
+ handler = OutputHandler(stdout=stdout, copy=copy)
65
+ log_console = handler.log_console
66
+
63
67
  # 1. Context 생성 (루트 찾기용)
64
68
  ctx = common.get_context(target)
65
69
  root_path = ctx.root_path
@@ -69,14 +73,19 @@ def handle(
69
73
  # [수정] targets=[target]을 명시적으로 전달해야 해당 폴더만 스캔함
70
74
  files = ctx.get_target_files(targets=[target], ignore_config=all, resolve_deps=resolve)
71
75
  except Exception as e:
72
- console.print(f"[red][Error] Scan failed:[/red] {e}")
76
+ log_console.print(f"[red][Error] Scan failed:[/red] {e}")
73
77
  raise typer.Exit(code=1)
74
78
 
75
79
  if not files:
76
- console.print("[yellow][Warning] No matching files found.[/yellow]")
80
+ log_console.print("[yellow][Warning] No matching files found.[/yellow]")
77
81
  return
78
82
 
79
83
  # 3. Visualize
80
- tree_viz = _build_rich_tree(root_path, files)
81
- console.print(tree_viz)
82
- console.print(f"\n[dim]Found {len(files)} files.[/dim]")
84
+ ascii_tree = structure.generate_ascii_tree(files, root_path)
85
+
86
+ def _print_tree_viz():
87
+ tree_viz = _build_rich_tree(root_path, files)
88
+ log_console.print(tree_viz)
89
+ log_console.print(f"\n[dim]Found {len(files)} files.[/dim]")
90
+
91
+ handler.handle_output(ascii_tree, save_action=_print_tree_viz, item_name="tree")
@@ -0,0 +1,50 @@
1
+ """
2
+ Unified Output Handler for CLI commands.
3
+ Manages stderr log console, clipboard copying, and file vs stdout output.
4
+ """
5
+ from dataclasses import dataclass
6
+ from typing import Callable, Optional
7
+ import pyperclip
8
+ from rich.console import Console
9
+
10
+ console = Console()
11
+ err_console = Console(stderr=True)
12
+
13
+ @dataclass
14
+ class OutputHandler:
15
+ stdout: bool = False
16
+ copy: bool = True
17
+
18
+ @property
19
+ def log_console(self) -> Console:
20
+ """
21
+ Returns err_console (stderr) when output is directed to stdout,
22
+ ensuring progress/logs do not pollute stdout pipelines.
23
+ """
24
+ return err_console if self.stdout else console
25
+
26
+ def copy_to_clipboard(self, content: str, item_name: str = "output"):
27
+ """Copies content to clipboard safely."""
28
+ if not self.copy:
29
+ return
30
+ try:
31
+ pyperclip.copy(content)
32
+ self.log_console.print(f"[dim]📋 Copied {item_name} to clipboard[/dim]")
33
+ except Exception:
34
+ self.log_console.print("[dim]⚠️ Clipboard unavailable (skipped copy)[/dim]")
35
+
36
+ def handle_output(
37
+ self,
38
+ content: str,
39
+ save_action: Optional[Callable[[], None]] = None,
40
+ item_name: str = "output"
41
+ ):
42
+ """
43
+ Executes clipboard copy, stdout print, or file save action cleanly.
44
+ """
45
+ self.copy_to_clipboard(content, item_name=item_name)
46
+
47
+ if self.stdout:
48
+ print(content)
49
+ elif save_action:
50
+ save_action()
@@ -0,0 +1,31 @@
1
+ """
2
+ Centralized CLI Options & Common Annotated Types.
3
+ """
4
+ from typing import Annotated
5
+ import typer
6
+
7
+ # Common CLI Option Types using typing.Annotated
8
+ CopyOption = Annotated[
9
+ bool,
10
+ typer.Option("-c", "--copy/--no-copy", help="Auto-copy output to clipboard")
11
+ ]
12
+
13
+ StdoutOption = Annotated[
14
+ bool,
15
+ typer.Option("-s", "--stdout", help="Print output to terminal (stdout) instead of file")
16
+ ]
17
+
18
+ ResolveOption = Annotated[
19
+ bool,
20
+ typer.Option("-r", "--resolve", help="Recursively resolve imports")
21
+ ]
22
+
23
+ MessageOption = Annotated[
24
+ str,
25
+ typer.Option("-m", "--message", help="Add specific instruction context")
26
+ ]
27
+
28
+ AllOption = Annotated[
29
+ bool,
30
+ typer.Option("-a", "--all", help="Ignore config filters")
31
+ ]
@@ -16,7 +16,7 @@ wheels = [
16
16
 
17
17
  [[package]]
18
18
  name = "codigest"
19
- version = "0.3.6"
19
+ version = "0.3.8"
20
20
  source = { editable = "." }
21
21
  dependencies = [
22
22
  { name = "loguru" },
File without changes
File without changes
File without changes
File without changes