janito 0.7.0__py3-none-any.whl → 0.8.0__py3-none-any.whl
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.
- janito/__main__.py +127 -141
- janito/agents/__init__.py +22 -22
- janito/agents/agent.py +24 -27
- janito/agents/claudeai.py +41 -45
- janito/agents/deepseekai.py +47 -0
- janito/change/applied_blocks.py +34 -0
- janito/change/applier.py +167 -0
- janito/change/edit_blocks.py +148 -0
- janito/change/finder.py +72 -0
- janito/change/request.py +144 -0
- janito/change/validator.py +87 -269
- janito/change/view/content.py +63 -0
- janito/change/{viewer → view}/diff.py +44 -43
- janito/change/view/panels.py +201 -0
- janito/change/view/sections.py +69 -0
- janito/change/view/styling.py +140 -0
- janito/change/view/summary.py +37 -0
- janito/change/{viewer → view}/themes.py +62 -55
- janito/change/view/viewer.py +59 -0
- janito/cli/__init__.py +1 -1
- janito/cli/commands.py +68 -88
- janito/cli/functions.py +66 -111
- janito/common.py +132 -79
- janito/config.py +99 -101
- janito/data/change_prompt.txt +81 -0
- janito/data/system_prompt.txt +3 -0
- janito/qa.py +56 -57
- janito/version.py +22 -22
- janito/workspace/__init__.py +8 -6
- janito/workspace/analysis.py +120 -120
- janito/workspace/{types.py → models.py} +97 -98
- janito/workspace/show.py +115 -141
- janito/workspace/stats.py +42 -43
- janito/workspace/workset.py +135 -108
- janito/workspace/workspace.py +335 -114
- janito-0.8.0.dist-info/METADATA +106 -0
- janito-0.8.0.dist-info/RECORD +40 -0
- {janito-0.7.0.dist-info → janito-0.8.0.dist-info}/licenses/LICENSE +20 -20
- janito/__init__.py +0 -2
- janito/agents/openai.py +0 -57
- janito/agents/test.py +0 -34
- janito/change/__init__.py +0 -32
- janito/change/__main__.py +0 -0
- janito/change/analysis/__init__.py +0 -23
- janito/change/analysis/__main__.py +0 -7
- janito/change/analysis/analyze.py +0 -62
- janito/change/analysis/formatting.py +0 -78
- janito/change/analysis/options.py +0 -81
- janito/change/analysis/prompts.py +0 -90
- janito/change/analysis/view/__init__.py +0 -9
- janito/change/analysis/view/terminal.py +0 -181
- janito/change/applier/__init__.py +0 -5
- janito/change/applier/file.py +0 -58
- janito/change/applier/main.py +0 -156
- janito/change/applier/text.py +0 -247
- janito/change/applier/workspace_dir.py +0 -58
- janito/change/core.py +0 -124
- janito/change/history.py +0 -44
- janito/change/operations.py +0 -7
- janito/change/parser.py +0 -287
- janito/change/play.py +0 -54
- janito/change/preview.py +0 -82
- janito/change/prompts.py +0 -121
- janito/change/test.py +0 -0
- janito/change/viewer/__init__.py +0 -11
- janito/change/viewer/content.py +0 -66
- janito/change/viewer/panels.py +0 -533
- janito/change/viewer/styling.py +0 -114
- janito/clear_statement_parser/clear_statement_format.txt +0 -328
- janito/clear_statement_parser/examples.txt +0 -326
- janito/clear_statement_parser/models.py +0 -104
- janito/clear_statement_parser/parser.py +0 -496
- janito/cli/base.py +0 -30
- janito/cli/history.py +0 -61
- janito/cli/registry.py +0 -26
- janito/demo/__init__.py +0 -4
- janito/demo/data.py +0 -13
- janito/demo/mock_data.py +0 -20
- janito/demo/operations.py +0 -45
- janito/demo/runner.py +0 -59
- janito/demo/scenarios.py +0 -32
- janito/prompt.py +0 -36
- janito/review.py +0 -13
- janito/search_replace/README.md +0 -192
- janito/search_replace/__init__.py +0 -7
- janito/search_replace/__main__.py +0 -21
- janito/search_replace/core.py +0 -120
- janito/search_replace/logger.py +0 -35
- janito/search_replace/parser.py +0 -52
- janito/search_replace/play.py +0 -61
- janito/search_replace/replacer.py +0 -36
- janito/search_replace/searcher.py +0 -411
- janito/search_replace/strategy_result.py +0 -10
- janito/shell/__init__.py +0 -38
- janito/shell/bus.py +0 -31
- janito/shell/commands.py +0 -136
- janito/shell/history.py +0 -20
- janito/shell/processor.py +0 -32
- janito/shell/prompt.py +0 -48
- janito/shell/registry.py +0 -60
- janito/tui/__init__.py +0 -21
- janito/tui/base.py +0 -22
- janito/tui/flows/__init__.py +0 -5
- janito/tui/flows/changes.py +0 -65
- janito/tui/flows/content.py +0 -128
- janito/tui/flows/selection.py +0 -117
- janito/tui/screens/__init__.py +0 -3
- janito/tui/screens/app.py +0 -1
- janito-0.7.0.dist-info/METADATA +0 -167
- janito-0.7.0.dist-info/RECORD +0 -96
- {janito-0.7.0.dist-info → janito-0.8.0.dist-info}/WHEEL +0 -0
- {janito-0.7.0.dist-info → janito-0.8.0.dist-info}/entry_points.txt +0 -0
janito/workspace/show.py
CHANGED
@@ -1,141 +1,115 @@
|
|
1
|
-
from rich.traceback import install
|
2
|
-
install(show_locals=False)
|
3
|
-
|
4
|
-
from pathlib import Path
|
5
|
-
from typing import List, Set
|
6
|
-
from rich.columns import Columns
|
7
|
-
from rich.console import Console, Group
|
8
|
-
from rich.panel import Panel
|
9
|
-
from rich.rule import Rule
|
10
|
-
from rich.text import Text
|
11
|
-
from janito.config import config
|
12
|
-
from .
|
13
|
-
from .stats import collect_file_stats, _format_size
|
14
|
-
|
15
|
-
|
16
|
-
def show_workset_analysis(
|
17
|
-
files: List[FileInfo],
|
18
|
-
scan_paths: List[ScanPath],
|
19
|
-
cache_blocks: List[List[FileInfo]] = None
|
20
|
-
) -> None:
|
21
|
-
"""Display analysis of workspace content and configuration."""
|
22
|
-
|
23
|
-
console = Console()
|
24
|
-
content_sections = []
|
25
|
-
|
26
|
-
# Get statistics
|
27
|
-
dir_counts, file_types = collect_file_stats(files)
|
28
|
-
|
29
|
-
# Calculate path stats using relative paths
|
30
|
-
paths_stats = []
|
31
|
-
total_files = 0
|
32
|
-
total_size = 0
|
33
|
-
|
34
|
-
|
35
|
-
# Process all paths uniformly
|
36
|
-
for scan_path in sorted(scan_paths, key=lambda p: p.path):
|
37
|
-
|
38
|
-
path = scan_path.path
|
39
|
-
is_recursive = scan_path.is_recursive
|
40
|
-
path_str = str(path)
|
41
|
-
|
42
|
-
# Calculate stats based on scan type
|
43
|
-
if is_recursive:
|
44
|
-
path_files = sum(count for d, [count, _] in dir_counts.items()
|
45
|
-
if Path(d) == path or Path(d).is_relative_to(path))
|
46
|
-
path_size = sum(size for d, [_, size] in dir_counts.items()
|
47
|
-
if Path(d) == path or Path(d).is_relative_to(path))
|
48
|
-
else:
|
49
|
-
path_files = dir_counts.get(path_str, [0, 0])[0]
|
50
|
-
path_size = dir_counts.get(path_str, [0, 0])[1]
|
51
|
-
|
52
|
-
total_files += path_files
|
53
|
-
total_size += path_size
|
54
|
-
|
55
|
-
paths_stats.append(
|
56
|
-
f"[bold cyan]{path}[/bold cyan]"
|
57
|
-
f"[yellow]{'/**' if is_recursive else '/'}[/yellow] "
|
58
|
-
f"[[green]{path_files}[/green] "
|
59
|
-
f"{'total ' if is_recursive else ''}file(s), "
|
60
|
-
f"[blue]{_format_size(path_size)}[/blue]]"
|
61
|
-
)
|
62
|
-
|
63
|
-
# Build sections - Show paths first
|
64
|
-
if paths_stats
|
65
|
-
content_sections.extend([
|
66
|
-
"[bold yellow]📌 Included Paths[/bold yellow]",
|
67
|
-
Rule(style="yellow"),
|
68
|
-
])
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
""
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
f"[bold cyan]
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
"
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
"[bold blue]🕒 Cache Blocks[/bold blue]",
|
117
|
-
Rule(style="blue"),
|
118
|
-
])
|
119
|
-
|
120
|
-
block_names = ["Last 5 minutes", "Last hour", "Last 24 hours", "Older"]
|
121
|
-
for name, block in zip(block_names, blocks):
|
122
|
-
if block: # Only show non-empty blocks
|
123
|
-
content_sections.extend([
|
124
|
-
f"\n[bold]{name}[/bold] ({len(block)} files):",
|
125
|
-
Columns([
|
126
|
-
Text.assemble(
|
127
|
-
f"{f.name} - ",
|
128
|
-
(f"{f.content.splitlines()[0][:50]}...", "dim")
|
129
|
-
)
|
130
|
-
for f in block[:5] # Show first 5 files only
|
131
|
-
], padding=(0, 2)),
|
132
|
-
"" if block == blocks[-1] else Rule(style="dim")
|
133
|
-
])
|
134
|
-
|
135
|
-
# Display analysis
|
136
|
-
console.print("\n")
|
137
|
-
console.print(Panel(
|
138
|
-
Group(*content_sections),
|
139
|
-
title="[bold blue]Workset Analysis[/bold blue]",
|
140
|
-
title_align="center"
|
141
|
-
))
|
1
|
+
from rich.traceback import install
|
2
|
+
install(show_locals=False)
|
3
|
+
|
4
|
+
from pathlib import Path
|
5
|
+
from typing import List, Set
|
6
|
+
from rich.columns import Columns
|
7
|
+
from rich.console import Console, Group
|
8
|
+
from rich.panel import Panel
|
9
|
+
from rich.rule import Rule
|
10
|
+
from rich.text import Text
|
11
|
+
from janito.config import config
|
12
|
+
from .models import FileInfo, ScanPath
|
13
|
+
from .stats import collect_file_stats, _format_size
|
14
|
+
|
15
|
+
|
16
|
+
def show_workset_analysis(
|
17
|
+
files: List[FileInfo],
|
18
|
+
scan_paths: List[ScanPath],
|
19
|
+
cache_blocks: List[List[FileInfo]] = None
|
20
|
+
) -> None:
|
21
|
+
"""Display analysis of workspace content and configuration."""
|
22
|
+
|
23
|
+
console = Console()
|
24
|
+
content_sections = []
|
25
|
+
|
26
|
+
# Get statistics
|
27
|
+
dir_counts, file_types = collect_file_stats(files)
|
28
|
+
|
29
|
+
# Calculate path stats using relative paths
|
30
|
+
paths_stats = []
|
31
|
+
total_files = 0
|
32
|
+
total_size = 0
|
33
|
+
|
34
|
+
|
35
|
+
# Process all paths uniformly
|
36
|
+
for scan_path in sorted(scan_paths, key=lambda p: p.path):
|
37
|
+
|
38
|
+
path = scan_path.path
|
39
|
+
is_recursive = scan_path.is_recursive
|
40
|
+
path_str = str(path)
|
41
|
+
|
42
|
+
# Calculate stats based on scan type
|
43
|
+
if is_recursive:
|
44
|
+
path_files = sum(count for d, [count, _] in dir_counts.items()
|
45
|
+
if Path(d) == path or Path(d).is_relative_to(path))
|
46
|
+
path_size = sum(size for d, [_, size] in dir_counts.items()
|
47
|
+
if Path(d) == path or Path(d).is_relative_to(path))
|
48
|
+
else:
|
49
|
+
path_files = dir_counts.get(path_str, [0, 0])[0]
|
50
|
+
path_size = dir_counts.get(path_str, [0, 0])[1]
|
51
|
+
|
52
|
+
total_files += path_files
|
53
|
+
total_size += path_size
|
54
|
+
|
55
|
+
paths_stats.append(
|
56
|
+
f"[bold cyan]{path}[/bold cyan]"
|
57
|
+
f"[yellow]{'/**' if is_recursive else '/'}[/yellow] "
|
58
|
+
f"[[green]{path_files}[/green] "
|
59
|
+
f"{'total ' if is_recursive else ''}file(s), "
|
60
|
+
f"[blue]{_format_size(path_size)}[/blue]]"
|
61
|
+
)
|
62
|
+
|
63
|
+
# Build sections - Show paths first
|
64
|
+
if paths_stats:
|
65
|
+
content_sections.extend([
|
66
|
+
"[bold yellow]📌 Included Paths[/bold yellow]",
|
67
|
+
Rule(style="yellow"),
|
68
|
+
])
|
69
|
+
|
70
|
+
content_sections.append(
|
71
|
+
Text(" | ").join(Text.from_markup(path) for path in paths_stats)
|
72
|
+
)
|
73
|
+
|
74
|
+
# Add total summary if there are multiple paths
|
75
|
+
if len(paths_stats) > 1:
|
76
|
+
content_sections.extend([
|
77
|
+
"", # Empty line for spacing
|
78
|
+
f"[bold yellow]Total:[/bold yellow] [green]{total_files}[/green] files, "
|
79
|
+
f"[blue]{_format_size(total_size)}[/blue]"
|
80
|
+
])
|
81
|
+
content_sections.append("\n")
|
82
|
+
|
83
|
+
# Then show directory structure if verbose
|
84
|
+
if config.verbose:
|
85
|
+
dir_stats = [
|
86
|
+
f"📁 {directory}/ [{count} file(s), {_format_size(size)}]"
|
87
|
+
for directory, (count, size) in sorted(dir_counts.items())
|
88
|
+
]
|
89
|
+
content_sections.extend([
|
90
|
+
"[bold magenta]📂 Directory Structure[/bold magenta]",
|
91
|
+
Rule(style="magenta"),
|
92
|
+
Columns(dir_stats, equal=True, expand=True),
|
93
|
+
"\n"
|
94
|
+
])
|
95
|
+
|
96
|
+
type_stats = [
|
97
|
+
f"[bold cyan].{ext.lstrip('.')}[/bold cyan] [[green]{count}[/green] file(s)]"
|
98
|
+
if ext != 'no_ext'
|
99
|
+
else f"[bold cyan]no ext[/bold cyan] [[green]{count}[/green] file(s)]"
|
100
|
+
for ext, count in sorted(file_types.items())
|
101
|
+
]
|
102
|
+
content_sections.extend([
|
103
|
+
"[bold cyan]📑 File Types[/bold cyan]",
|
104
|
+
Rule(style="cyan"),
|
105
|
+
Text(" | ").join(Text.from_markup(stat) for stat in type_stats)
|
106
|
+
])
|
107
|
+
|
108
|
+
|
109
|
+
# Display analysis
|
110
|
+
console.print("\n")
|
111
|
+
console.print(Panel(
|
112
|
+
Group(*content_sections),
|
113
|
+
title="[bold blue]Workset Analysis[/bold blue]",
|
114
|
+
title_align="center"
|
115
|
+
))
|
janito/workspace/stats.py
CHANGED
@@ -1,43 +1,42 @@
|
|
1
|
-
from collections import defaultdict
|
2
|
-
from pathlib import Path
|
3
|
-
from typing import List, Dict, Tuple
|
4
|
-
from .
|
5
|
-
|
6
|
-
def collect_file_stats(files: List[FileInfo]) -> Tuple[Dict[str, List[int]], Dict[str, int]]:
|
7
|
-
"""Collect directory and file type statistics from files.
|
8
|
-
|
9
|
-
Args:
|
10
|
-
files: List of FileInfo objects to analyze
|
11
|
-
|
12
|
-
Returns:
|
13
|
-
Tuple containing:
|
14
|
-
- Dictionary of directory stats [count, size]
|
15
|
-
- Dictionary of file type counts
|
16
|
-
"""
|
17
|
-
dir_counts = defaultdict(lambda: [0, 0]) # [count, size]
|
18
|
-
file_types = defaultdict(int)
|
19
|
-
|
20
|
-
for file_info in files:
|
21
|
-
path = Path(file_info.name)
|
22
|
-
dir_path = str(path.parent)
|
23
|
-
file_size = len(file_info.content.encode('utf-8'))
|
24
|
-
|
25
|
-
# Update directory stats
|
26
|
-
dir_counts[dir_path][0] += 1
|
27
|
-
dir_counts[dir_path][1] += file_size
|
28
|
-
|
29
|
-
# Update file type stats
|
30
|
-
file_types[path.suffix.lower() or 'no_ext'] += 1
|
31
|
-
|
32
|
-
return dir_counts, file_types
|
33
|
-
|
34
|
-
def _format_size(size_bytes: int) -> str:
|
35
|
-
"""Format size in bytes to human readable format."""
|
36
|
-
size = size_bytes
|
37
|
-
for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
|
38
|
-
if size < 1024:
|
39
|
-
break
|
40
|
-
size //= 1024
|
41
|
-
return f"{size} {unit}"
|
42
|
-
|
43
|
-
# Remove _group_files_by_time function as it's now handled by Workset
|
1
|
+
from collections import defaultdict
|
2
|
+
from pathlib import Path
|
3
|
+
from typing import List, Dict, Tuple
|
4
|
+
from .models import FileInfo
|
5
|
+
|
6
|
+
def collect_file_stats(files: List[FileInfo]) -> Tuple[Dict[str, List[int]], Dict[str, int]]:
|
7
|
+
"""Collect directory and file type statistics from files.
|
8
|
+
|
9
|
+
Args:
|
10
|
+
files: List of FileInfo objects to analyze
|
11
|
+
|
12
|
+
Returns:
|
13
|
+
Tuple containing:
|
14
|
+
- Dictionary of directory stats [count, size]
|
15
|
+
- Dictionary of file type counts
|
16
|
+
"""
|
17
|
+
dir_counts = defaultdict(lambda: [0, 0]) # [count, size]
|
18
|
+
file_types = defaultdict(int)
|
19
|
+
|
20
|
+
for file_info in files:
|
21
|
+
path = Path(file_info.name)
|
22
|
+
dir_path = str(path.parent)
|
23
|
+
file_size = len(file_info.content.encode('utf-8'))
|
24
|
+
|
25
|
+
# Update directory stats
|
26
|
+
dir_counts[dir_path][0] += 1
|
27
|
+
dir_counts[dir_path][1] += file_size
|
28
|
+
|
29
|
+
# Update file type stats
|
30
|
+
file_types[path.suffix.lower() or 'no_ext'] += 1
|
31
|
+
|
32
|
+
return dir_counts, file_types
|
33
|
+
|
34
|
+
def _format_size(size_bytes: int) -> str:
|
35
|
+
"""Format size in bytes to human readable format."""
|
36
|
+
size = size_bytes
|
37
|
+
for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
|
38
|
+
if size < 1024:
|
39
|
+
break
|
40
|
+
size //= 1024
|
41
|
+
return f"{size} {unit}"
|
42
|
+
|
janito/workspace/workset.py
CHANGED
@@ -1,108 +1,135 @@
|
|
1
|
-
from pathlib import Path
|
2
|
-
from typing import List, Set
|
3
|
-
from .show import show_workset_analysis
|
4
|
-
from rich.console import Console
|
5
|
-
from janito.config import config
|
6
|
-
from .
|
7
|
-
from .workspace import Workspace
|
8
|
-
|
9
|
-
class PathNotRelativeError(Exception):
|
10
|
-
"""Raised when a path is not relative."""
|
11
|
-
pass
|
12
|
-
|
13
|
-
class Workset:
|
14
|
-
_instance = None
|
15
|
-
|
16
|
-
def __new__(cls):
|
17
|
-
if cls._instance is None:
|
18
|
-
cls._instance = super().__new__(cls)
|
19
|
-
cls._instance._init()
|
20
|
-
return cls._instance
|
21
|
-
|
22
|
-
def _init(self):
|
23
|
-
self._scan_paths: List[ScanPath] = []
|
24
|
-
self._content = WorksetContent()
|
25
|
-
self._workspace = Workspace()
|
26
|
-
if not config.skip_work:
|
27
|
-
self.
|
28
|
-
|
29
|
-
def add_scan_path(self, path: Path, scan_type: ScanType) -> None:
|
30
|
-
"""Add a path with specific scan type.
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
def
|
52
|
-
"""
|
53
|
-
|
54
|
-
paths
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
def
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
1
|
+
from pathlib import Path
|
2
|
+
from typing import List, Set
|
3
|
+
from .show import show_workset_analysis
|
4
|
+
from rich.console import Console
|
5
|
+
from janito.config import config
|
6
|
+
from .models import WorksetContent, ScanPath, ScanType
|
7
|
+
from .workspace import Workspace
|
8
|
+
|
9
|
+
class PathNotRelativeError(Exception):
|
10
|
+
"""Raised when a path is not relative."""
|
11
|
+
pass
|
12
|
+
|
13
|
+
class Workset:
|
14
|
+
_instance = None
|
15
|
+
|
16
|
+
def __new__(cls):
|
17
|
+
if cls._instance is None:
|
18
|
+
cls._instance = super().__new__(cls)
|
19
|
+
cls._instance._init()
|
20
|
+
return cls._instance
|
21
|
+
|
22
|
+
def _init(self):
|
23
|
+
self._scan_paths: List[ScanPath] = []
|
24
|
+
self._content = WorksetContent()
|
25
|
+
self._workspace = Workspace()
|
26
|
+
if not config.skip_work:
|
27
|
+
self.add_scan_path(Path("."))
|
28
|
+
|
29
|
+
def add_scan_path(self, path: Path, scan_type: ScanType = ScanType.PLAIN) -> None:
|
30
|
+
"""Add a path with specific scan type.
|
31
|
+
|
32
|
+
Args:
|
33
|
+
path: Relative path to add for scanning
|
34
|
+
scan_type: Type of scanning (PLAIN or RECURSIVE)
|
35
|
+
|
36
|
+
Raises:
|
37
|
+
PathNotRelativeError: If path is absolute
|
38
|
+
"""
|
39
|
+
if path.is_absolute():
|
40
|
+
raise PathNotRelativeError(f"Path must be relative: {path}")
|
41
|
+
|
42
|
+
scan_path = ScanPath(path, scan_type)
|
43
|
+
ScanPath.validate(path)
|
44
|
+
self._scan_paths.append(scan_path)
|
45
|
+
|
46
|
+
if config.debug:
|
47
|
+
Console(stderr=True).print(
|
48
|
+
f"[cyan]Debug: Added {scan_type.name.lower()} scan path: {path}[/cyan]"
|
49
|
+
)
|
50
|
+
|
51
|
+
def refresh(self) -> None:
|
52
|
+
"""Refresh content by scanning configured paths"""
|
53
|
+
self.clear()
|
54
|
+
paths = self.get_scan_paths()
|
55
|
+
|
56
|
+
if config.debug:
|
57
|
+
Console(stderr=True).print(f"[cyan]Debug: Refreshing workset with paths: {paths}[/cyan]")
|
58
|
+
|
59
|
+
self._workspace.scan_files(paths, self.get_recursive_paths())
|
60
|
+
self._content = self._workspace.content
|
61
|
+
|
62
|
+
def get_scan_paths(self) -> List[Path]:
|
63
|
+
"""Get effective scan paths based on configuration"""
|
64
|
+
paths = set()
|
65
|
+
paths.update(p.path for p in self._scan_paths)
|
66
|
+
return sorted(paths)
|
67
|
+
|
68
|
+
def get_recursive_paths(self) -> Set[Path]:
|
69
|
+
"""Get paths that should be scanned recursively"""
|
70
|
+
return {p.path for p in self._scan_paths if p.is_recursive}
|
71
|
+
|
72
|
+
def is_path_recursive(self, path: Path) -> bool:
|
73
|
+
"""Check if a path is configured for recursive scanning"""
|
74
|
+
return any(scan_path.is_recursive and scan_path.path == path
|
75
|
+
for scan_path in self._scan_paths)
|
76
|
+
|
77
|
+
@property
|
78
|
+
def paths(self) -> Set[Path]:
|
79
|
+
return {p.path for p in self._scan_paths}
|
80
|
+
|
81
|
+
@property
|
82
|
+
def recursive_paths(self) -> Set[Path]:
|
83
|
+
return self.get_recursive_paths()
|
84
|
+
|
85
|
+
def clear(self) -> None:
|
86
|
+
"""Clear workspace settings while maintaining current directory in scan paths"""
|
87
|
+
self._content = WorksetContent()
|
88
|
+
|
89
|
+
|
90
|
+
def show(self) -> None:
|
91
|
+
"""Display analysis of current workset content."""
|
92
|
+
show_workset_analysis(
|
93
|
+
files=self._content.files,
|
94
|
+
scan_paths=self._scan_paths,
|
95
|
+
cache_blocks=None
|
96
|
+
)
|
97
|
+
|
98
|
+
@property
|
99
|
+
def content(self) -> str:
|
100
|
+
"""Return the workset content as a string.
|
101
|
+
|
102
|
+
Format:
|
103
|
+
<workspace_base_directories>
|
104
|
+
dirname1
|
105
|
+
dirname2
|
106
|
+
...
|
107
|
+
<workset_files>
|
108
|
+
<file name=filename1>
|
109
|
+
```
|
110
|
+
file1_content
|
111
|
+
```
|
112
|
+
</file>
|
113
|
+
<file name=filename2>
|
114
|
+
```
|
115
|
+
file2_content
|
116
|
+
```
|
117
|
+
</file>
|
118
|
+
...
|
119
|
+
"""
|
120
|
+
content = "<workspace_base_directories>\n"
|
121
|
+
# Only include root directories if not skipping workspace
|
122
|
+
if not config.skip_work:
|
123
|
+
for dir in sorted(self._workspace.root_directories):
|
124
|
+
content += f"{dir}\n"
|
125
|
+
|
126
|
+
content += "<workset_files>\n"
|
127
|
+
for file in sorted(self._content.files):
|
128
|
+
content += f"<file name={file.name}>\n"
|
129
|
+
content += "```\n"
|
130
|
+
content += file.content
|
131
|
+
content += "\n```\n"
|
132
|
+
content += "</file>\n"
|
133
|
+
|
134
|
+
return content
|
135
|
+
|