aline-ai 0.6.2__py3-none-any.whl → 0.6.3__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.
- {aline_ai-0.6.2.dist-info → aline_ai-0.6.3.dist-info}/METADATA +1 -1
- {aline_ai-0.6.2.dist-info → aline_ai-0.6.3.dist-info}/RECORD +28 -30
- realign/__init__.py +1 -1
- realign/adapters/__init__.py +0 -3
- realign/cli.py +0 -1
- realign/commands/export_shares.py +154 -226
- realign/commands/watcher.py +28 -79
- realign/config.py +1 -47
- realign/dashboard/app.py +2 -8
- realign/dashboard/screens/event_detail.py +0 -3
- realign/dashboard/screens/session_detail.py +0 -1
- realign/dashboard/widgets/config_panel.py +109 -249
- realign/dashboard/widgets/events_table.py +71 -128
- realign/dashboard/widgets/sessions_table.py +76 -135
- realign/dashboard/widgets/watcher_panel.py +0 -2
- realign/db/sqlite_db.py +1 -2
- realign/events/event_summarizer.py +76 -35
- realign/events/session_summarizer.py +73 -32
- realign/hooks.py +383 -574
- realign/llm_client.py +201 -520
- realign/triggers/__init__.py +0 -2
- realign/triggers/next_turn_trigger.py +4 -5
- realign/triggers/registry.py +1 -4
- realign/watcher_core.py +3 -35
- realign/adapters/antigravity.py +0 -159
- realign/triggers/antigravity_trigger.py +0 -140
- {aline_ai-0.6.2.dist-info → aline_ai-0.6.3.dist-info}/WHEEL +0 -0
- {aline_ai-0.6.2.dist-info → aline_ai-0.6.3.dist-info}/entry_points.txt +0 -0
- {aline_ai-0.6.2.dist-info → aline_ai-0.6.3.dist-info}/licenses/LICENSE +0 -0
- {aline_ai-0.6.2.dist-info → aline_ai-0.6.3.dist-info}/top_level.txt +0 -0
realign/triggers/__init__.py
CHANGED
|
@@ -8,7 +8,6 @@ from .base import TurnTrigger, TurnInfo
|
|
|
8
8
|
from .claude_trigger import ClaudeTrigger
|
|
9
9
|
from .codex_trigger import CodexTrigger
|
|
10
10
|
from .gemini_trigger import GeminiTrigger
|
|
11
|
-
from .antigravity_trigger import AntigravityTrigger
|
|
12
11
|
from .next_turn_trigger import NextTurnTrigger
|
|
13
12
|
from .registry import TriggerRegistry, get_global_registry
|
|
14
13
|
|
|
@@ -19,7 +18,6 @@ __all__ = [
|
|
|
19
18
|
"ClaudeTrigger",
|
|
20
19
|
"CodexTrigger",
|
|
21
20
|
"GeminiTrigger",
|
|
22
|
-
"AntigravityTrigger",
|
|
23
21
|
"TriggerRegistry",
|
|
24
22
|
"get_global_registry",
|
|
25
23
|
]
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
NextTurnTrigger
|
|
3
3
|
|
|
4
4
|
A generic delegating trigger that auto-detects the session format and dispatches
|
|
5
|
-
to the appropriate concrete trigger (Claude/Codex/Gemini
|
|
5
|
+
to the appropriate concrete trigger (Claude/Codex/Gemini).
|
|
6
6
|
|
|
7
7
|
This is used as a stable default trigger name ("next_turn") across the codebase.
|
|
8
8
|
"""
|
|
@@ -17,16 +17,15 @@ from .base import TurnInfo, TurnTrigger
|
|
|
17
17
|
from .claude_trigger import ClaudeTrigger
|
|
18
18
|
from .codex_trigger import CodexTrigger
|
|
19
19
|
from .gemini_trigger import GeminiTrigger
|
|
20
|
-
from .antigravity_trigger import AntigravityTrigger
|
|
21
20
|
|
|
22
21
|
|
|
23
22
|
class NextTurnTrigger(TurnTrigger):
|
|
24
23
|
"""Auto-detect trigger for any supported session type."""
|
|
25
24
|
|
|
26
25
|
def select_trigger(self, session_file: Path) -> Optional[TurnTrigger]:
|
|
27
|
-
#
|
|
26
|
+
# Skip directories (not a supported session format)
|
|
28
27
|
if session_file.is_dir():
|
|
29
|
-
return
|
|
28
|
+
return None
|
|
30
29
|
|
|
31
30
|
# Heuristics based on path
|
|
32
31
|
path_str = str(session_file).lower()
|
|
@@ -87,7 +86,7 @@ class NextTurnTrigger(TurnTrigger):
|
|
|
87
86
|
def get_supported_formats(self) -> List[str]:
|
|
88
87
|
# Union of underlying triggers.
|
|
89
88
|
formats: List[str] = []
|
|
90
|
-
for cls in (ClaudeTrigger, CodexTrigger, GeminiTrigger
|
|
89
|
+
for cls in (ClaudeTrigger, CodexTrigger, GeminiTrigger):
|
|
91
90
|
try:
|
|
92
91
|
formats.extend(cls(self.config).get_supported_formats())
|
|
93
92
|
except Exception:
|
realign/triggers/registry.py
CHANGED
|
@@ -11,7 +11,6 @@ from .base import TurnTrigger
|
|
|
11
11
|
from .claude_trigger import ClaudeTrigger
|
|
12
12
|
from .codex_trigger import CodexTrigger
|
|
13
13
|
from .gemini_trigger import GeminiTrigger
|
|
14
|
-
from .antigravity_trigger import AntigravityTrigger
|
|
15
14
|
from .next_turn_trigger import NextTurnTrigger
|
|
16
15
|
|
|
17
16
|
logger = logging.getLogger(__name__)
|
|
@@ -28,7 +27,6 @@ class TriggerRegistry:
|
|
|
28
27
|
"claude": ClaudeTrigger,
|
|
29
28
|
"codex": CodexTrigger,
|
|
30
29
|
"gemini": GeminiTrigger,
|
|
31
|
-
"antigravity": AntigravityTrigger,
|
|
32
30
|
}
|
|
33
31
|
|
|
34
32
|
# 注册所有triggers (用于list_triggers和get_trigger)
|
|
@@ -36,7 +34,6 @@ class TriggerRegistry:
|
|
|
36
34
|
self.register("claude", ClaudeTrigger)
|
|
37
35
|
self.register("codex", CodexTrigger)
|
|
38
36
|
self.register("gemini", GeminiTrigger)
|
|
39
|
-
self.register("antigravity", AntigravityTrigger)
|
|
40
37
|
|
|
41
38
|
def register(self, name: str, trigger_class: Type[TurnTrigger]):
|
|
42
39
|
"""
|
|
@@ -71,7 +68,7 @@ class TriggerRegistry:
|
|
|
71
68
|
根据session类型获取对应的trigger
|
|
72
69
|
|
|
73
70
|
Args:
|
|
74
|
-
session_type: session类型 ("claude", "codex", "gemini"
|
|
71
|
+
session_type: session类型 ("claude", "codex", "gemini")
|
|
75
72
|
config: 可选的配置字典
|
|
76
73
|
|
|
77
74
|
Returns:
|
realign/watcher_core.py
CHANGED
|
@@ -23,7 +23,7 @@ logger = setup_logger("realign.watcher_core", "watcher_core.log")
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
# Session type detection
|
|
26
|
-
SessionType = Literal["claude", "codex", "gemini", "
|
|
26
|
+
SessionType = Literal["claude", "codex", "gemini", "unknown"]
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
def is_path_blacklisted(project_path: Path) -> bool:
|
|
@@ -540,9 +540,6 @@ class DialogueWatcher:
|
|
|
540
540
|
current_count = self._count_complete_turns(session_file)
|
|
541
541
|
last_count = self.last_stop_reason_counts.get(session_path, 0)
|
|
542
542
|
|
|
543
|
-
if session_type == "antigravity":
|
|
544
|
-
return [1] if current_count >= 1 else []
|
|
545
|
-
|
|
546
543
|
if current_count <= last_count:
|
|
547
544
|
return []
|
|
548
545
|
|
|
@@ -974,9 +971,9 @@ class DialogueWatcher:
|
|
|
974
971
|
adapter = registry.auto_detect_adapter(session_file)
|
|
975
972
|
if adapter:
|
|
976
973
|
# Map adapter name to SessionType
|
|
977
|
-
# Adapter names: "claude", "codex", "gemini"
|
|
974
|
+
# Adapter names: "claude", "codex", "gemini"
|
|
978
975
|
name = adapter.name
|
|
979
|
-
if name in ["claude", "codex", "gemini"
|
|
976
|
+
if name in ["claude", "codex", "gemini"]:
|
|
980
977
|
return name
|
|
981
978
|
|
|
982
979
|
return "unknown"
|
|
@@ -1956,35 +1953,6 @@ class DialogueWatcher:
|
|
|
1956
1953
|
indent=2,
|
|
1957
1954
|
)
|
|
1958
1955
|
|
|
1959
|
-
if session_format == "antigravity_markdown":
|
|
1960
|
-
print(
|
|
1961
|
-
f"[Debug] Extracting Antigravity content for turn {turn_number}",
|
|
1962
|
-
file=sys.stderr,
|
|
1963
|
-
)
|
|
1964
|
-
# Directly read artifact files from directory (don't rely on trigger.user_message which is empty)
|
|
1965
|
-
session_dir = session_file if session_file.is_dir() else session_file.parent
|
|
1966
|
-
artifacts = ["task.md", "walkthrough.md", "implementation_plan.md"]
|
|
1967
|
-
content_parts = []
|
|
1968
|
-
for filename in artifacts:
|
|
1969
|
-
path = session_dir / filename
|
|
1970
|
-
if path.exists():
|
|
1971
|
-
try:
|
|
1972
|
-
text = path.read_text(encoding="utf-8")
|
|
1973
|
-
content_parts.append(f"--- {filename} ---\n{text}")
|
|
1974
|
-
except Exception as e:
|
|
1975
|
-
print(f"[Debug] Failed to read {filename}: {e}", file=sys.stderr)
|
|
1976
|
-
full_content = "\n\n".join(content_parts)
|
|
1977
|
-
if full_content:
|
|
1978
|
-
print(
|
|
1979
|
-
f"[Debug] Antigravity content extracted: {len(full_content)} chars",
|
|
1980
|
-
file=sys.stderr,
|
|
1981
|
-
)
|
|
1982
|
-
return full_content
|
|
1983
|
-
print(
|
|
1984
|
-
f"[Debug] Antigravity content empty - no artifact files found", file=sys.stderr
|
|
1985
|
-
)
|
|
1986
|
-
return ""
|
|
1987
|
-
|
|
1988
1956
|
# For JSONL formats, extract by line numbers
|
|
1989
1957
|
start_line = group.get("start_line") or (group.get("lines") or [None])[0]
|
|
1990
1958
|
end_line = group.get("end_line") or (group.get("lines") or [None])[-1]
|
realign/adapters/antigravity.py
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Antigravity IDE Adapter
|
|
3
|
-
|
|
4
|
-
Handles session discovery for Antigravity IDE (Gemini in IDE).
|
|
5
|
-
Since .pb conversation files are encrypted, this adapter uses
|
|
6
|
-
readable brain artifacts (walkthrough.md, task.md) as session indicators.
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
import re
|
|
10
|
-
from pathlib import Path
|
|
11
|
-
from typing import List, Optional, Dict, Any
|
|
12
|
-
|
|
13
|
-
from .base import SessionAdapter
|
|
14
|
-
from ..triggers.antigravity_trigger import AntigravityTrigger
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class AntigravityAdapter(SessionAdapter):
|
|
18
|
-
"""Adapter for Antigravity IDE sessions."""
|
|
19
|
-
|
|
20
|
-
name = "antigravity"
|
|
21
|
-
trigger_class = AntigravityTrigger
|
|
22
|
-
|
|
23
|
-
def discover_sessions(self) -> List[Path]:
|
|
24
|
-
"""
|
|
25
|
-
Find all active Antigravity IDE sessions.
|
|
26
|
-
Returns the directory path for each conversation that contains relevant artifacts.
|
|
27
|
-
"""
|
|
28
|
-
sessions = []
|
|
29
|
-
gemini_brain = Path.home() / ".gemini" / "antigravity" / "brain"
|
|
30
|
-
|
|
31
|
-
if not gemini_brain.exists():
|
|
32
|
-
return sessions
|
|
33
|
-
|
|
34
|
-
try:
|
|
35
|
-
for conv_dir in gemini_brain.iterdir():
|
|
36
|
-
if not conv_dir.is_dir():
|
|
37
|
-
continue
|
|
38
|
-
|
|
39
|
-
# Check for key artifacts
|
|
40
|
-
has_artifacts = any(
|
|
41
|
-
(conv_dir / filename).exists()
|
|
42
|
-
for filename in ["task.md", "walkthrough.md", "implementation_plan.md"]
|
|
43
|
-
)
|
|
44
|
-
|
|
45
|
-
if has_artifacts:
|
|
46
|
-
sessions.append(conv_dir)
|
|
47
|
-
except Exception:
|
|
48
|
-
pass
|
|
49
|
-
|
|
50
|
-
return sessions
|
|
51
|
-
|
|
52
|
-
def discover_sessions_for_project(self, project_path: Path) -> List[Path]:
|
|
53
|
-
"""
|
|
54
|
-
Find sessions for a specific project.
|
|
55
|
-
"""
|
|
56
|
-
all_sessions = self.discover_sessions()
|
|
57
|
-
project_sessions = []
|
|
58
|
-
|
|
59
|
-
for session in all_sessions:
|
|
60
|
-
extracted_path = self.extract_project_path(session)
|
|
61
|
-
if extracted_path and extracted_path == project_path:
|
|
62
|
-
project_sessions.append(session)
|
|
63
|
-
|
|
64
|
-
return project_sessions
|
|
65
|
-
|
|
66
|
-
def extract_project_path(self, session_file: Path) -> Optional[Path]:
|
|
67
|
-
"""
|
|
68
|
-
Infer project root from Antigravity brain artifacts.
|
|
69
|
-
|
|
70
|
-
Antigravity stores readable artifacts (task.md / walkthrough.md / implementation_plan.md).
|
|
71
|
-
We attempt to extract a `file://...` reference, then walk upward to find a `.git` root.
|
|
72
|
-
"""
|
|
73
|
-
session_dir = session_file if session_file.is_dir() else session_file.parent
|
|
74
|
-
candidates: List[Path] = []
|
|
75
|
-
|
|
76
|
-
artifact_names = ["task.md", "walkthrough.md", "implementation_plan.md"]
|
|
77
|
-
for name in artifact_names:
|
|
78
|
-
p = session_dir / name
|
|
79
|
-
if not p.exists():
|
|
80
|
-
continue
|
|
81
|
-
try:
|
|
82
|
-
text = p.read_text(encoding="utf-8", errors="ignore")
|
|
83
|
-
except Exception:
|
|
84
|
-
continue
|
|
85
|
-
|
|
86
|
-
# Capture file:// paths from Markdown links or plain text.
|
|
87
|
-
# Example: file:///Users/me/Project/src/main.py or file://.../src/main.py
|
|
88
|
-
for match in re.findall(r"file://([^)\s]+)", text):
|
|
89
|
-
raw = match.strip()
|
|
90
|
-
if raw.startswith("/"):
|
|
91
|
-
candidates.append(Path(raw))
|
|
92
|
-
else:
|
|
93
|
-
# Some tools emit file://Users/... (missing leading slash)
|
|
94
|
-
candidates.append(Path("/" + raw))
|
|
95
|
-
|
|
96
|
-
for candidate in candidates:
|
|
97
|
-
try:
|
|
98
|
-
# Prefer the nearest VCS root
|
|
99
|
-
path = candidate
|
|
100
|
-
if path.is_file():
|
|
101
|
-
path = path.parent
|
|
102
|
-
for parent in [path] + list(path.parents):
|
|
103
|
-
git_dir = parent / ".git"
|
|
104
|
-
if git_dir.exists():
|
|
105
|
-
return parent
|
|
106
|
-
except Exception:
|
|
107
|
-
continue
|
|
108
|
-
|
|
109
|
-
# Fallback: if we found a candidate that exists, return its parent directory.
|
|
110
|
-
for candidate in candidates:
|
|
111
|
-
try:
|
|
112
|
-
if candidate.exists():
|
|
113
|
-
return candidate.parent if candidate.is_file() else candidate
|
|
114
|
-
except Exception:
|
|
115
|
-
continue
|
|
116
|
-
|
|
117
|
-
return None
|
|
118
|
-
|
|
119
|
-
def get_session_metadata(self, session_file: Path) -> Dict[str, Any]:
|
|
120
|
-
"""Extract rich metadata from Antigravity brain artifacts."""
|
|
121
|
-
metadata = super().get_session_metadata(session_file)
|
|
122
|
-
|
|
123
|
-
if not session_file.exists():
|
|
124
|
-
return metadata
|
|
125
|
-
|
|
126
|
-
session_dir = session_file if session_file.is_dir() else session_file.parent
|
|
127
|
-
|
|
128
|
-
try:
|
|
129
|
-
# We just return the turn count (always 1 if exists)
|
|
130
|
-
# No task progress parsing required
|
|
131
|
-
metadata["turn_count"] = self.trigger.count_complete_turns(session_file)
|
|
132
|
-
|
|
133
|
-
except Exception:
|
|
134
|
-
pass
|
|
135
|
-
|
|
136
|
-
return metadata
|
|
137
|
-
|
|
138
|
-
def is_session_valid(self, session_file: Path) -> bool:
|
|
139
|
-
"""Check if this is an Antigravity artifact directory."""
|
|
140
|
-
if not session_file.is_dir():
|
|
141
|
-
if (
|
|
142
|
-
session_file.parent.name == "brain"
|
|
143
|
-
and session_file.parent.parent.name == "antigravity"
|
|
144
|
-
):
|
|
145
|
-
# It's a directory inside brain
|
|
146
|
-
return True
|
|
147
|
-
return False
|
|
148
|
-
|
|
149
|
-
# Check parent hierarchy
|
|
150
|
-
# .../.gemini/antigravity/brain/<uuid>
|
|
151
|
-
try:
|
|
152
|
-
if (
|
|
153
|
-
session_file.parent.name == "brain"
|
|
154
|
-
and session_file.parent.parent.name == "antigravity"
|
|
155
|
-
):
|
|
156
|
-
return True
|
|
157
|
-
except Exception:
|
|
158
|
-
pass
|
|
159
|
-
return False
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
AntigravityTrigger - Trigger for Antigravity IDE (Markdown artifacts)
|
|
3
|
-
|
|
4
|
-
Since Antigravity produces walkthrough.md and task.md instead of turn-based JSONL,
|
|
5
|
-
this trigger uses file content/structure as a signal for "turns".
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from datetime import datetime
|
|
9
|
-
from pathlib import Path
|
|
10
|
-
from typing import List, Optional, Dict, Any
|
|
11
|
-
import re
|
|
12
|
-
from .base import TurnTrigger, TurnInfo
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class AntigravityTrigger(TurnTrigger):
|
|
16
|
-
"""
|
|
17
|
-
Trigger for Antigravity IDE markdown artifacts.
|
|
18
|
-
Each distinct Task in task.md or major change in walkthrough.md can be
|
|
19
|
-
treated as a 'turn'.
|
|
20
|
-
"""
|
|
21
|
-
|
|
22
|
-
def get_supported_formats(self) -> List[str]:
|
|
23
|
-
return ["antigravity_markdown"]
|
|
24
|
-
|
|
25
|
-
def detect_session_format(self, session_file: Path) -> Optional[str]:
|
|
26
|
-
"""Detect if this is an Antigravity brain artifact directory."""
|
|
27
|
-
try:
|
|
28
|
-
# Check if directory
|
|
29
|
-
if session_file.is_dir():
|
|
30
|
-
if session_file.parent.name == "brain" and "antigravity" in str(session_file):
|
|
31
|
-
return "antigravity_markdown"
|
|
32
|
-
return None
|
|
33
|
-
|
|
34
|
-
# Legacy/Fallback: Check if file
|
|
35
|
-
if session_file.suffix == ".md":
|
|
36
|
-
path_str = str(session_file)
|
|
37
|
-
if "gemini" in path_str and "brain" in path_str:
|
|
38
|
-
return "antigravity_markdown"
|
|
39
|
-
if ".antigravity" in path_str or "antigravity" in path_str.lower():
|
|
40
|
-
return "antigravity_markdown"
|
|
41
|
-
return None
|
|
42
|
-
except Exception:
|
|
43
|
-
return None
|
|
44
|
-
|
|
45
|
-
def count_complete_turns(self, session_file: Path) -> int:
|
|
46
|
-
"""
|
|
47
|
-
Antigravity sessions are effectively "single-turn" persistent states.
|
|
48
|
-
We return 1 if the artifacts exist, 0 otherwise.
|
|
49
|
-
|
|
50
|
-
The watcher will handle change detection via content hashing or mtime,
|
|
51
|
-
even if this count stays at 1.
|
|
52
|
-
|
|
53
|
-
Args:
|
|
54
|
-
session_file: Path to brain directory (or file)
|
|
55
|
-
|
|
56
|
-
Returns:
|
|
57
|
-
int: 1 if artifacts exist, 0 otherwise.
|
|
58
|
-
"""
|
|
59
|
-
if not session_file.exists():
|
|
60
|
-
return 0
|
|
61
|
-
|
|
62
|
-
session_dir = session_file if session_file.is_dir() else session_file.parent
|
|
63
|
-
artifacts = ["task.md", "walkthrough.md", "implementation_plan.md"]
|
|
64
|
-
|
|
65
|
-
has_artifacts = False
|
|
66
|
-
for filename in artifacts:
|
|
67
|
-
path = session_dir / filename
|
|
68
|
-
if path.exists():
|
|
69
|
-
has_artifacts = True
|
|
70
|
-
break
|
|
71
|
-
|
|
72
|
-
return 1 if has_artifacts else 0
|
|
73
|
-
|
|
74
|
-
def extract_turn_info(self, session_file: Path, turn_number: int) -> Optional[TurnInfo]:
|
|
75
|
-
"""Extract information by aggregating artifacts."""
|
|
76
|
-
if not session_file.exists():
|
|
77
|
-
return None
|
|
78
|
-
|
|
79
|
-
# Ensure we point to the directory
|
|
80
|
-
session_dir = session_file if session_file.is_dir() else session_file.parent
|
|
81
|
-
|
|
82
|
-
content_parts = []
|
|
83
|
-
artifacts = ["task.md", "walkthrough.md", "implementation_plan.md"]
|
|
84
|
-
|
|
85
|
-
# Aggregate content
|
|
86
|
-
for filename in artifacts:
|
|
87
|
-
path = session_dir / filename
|
|
88
|
-
if path.exists():
|
|
89
|
-
text = path.read_text(encoding="utf-8")
|
|
90
|
-
content_parts.append(f"--- {filename} ---\n{text}")
|
|
91
|
-
|
|
92
|
-
full_content = "\n\n".join(content_parts)
|
|
93
|
-
if not full_content:
|
|
94
|
-
return None
|
|
95
|
-
|
|
96
|
-
# Always use current time for timestamp as this is an evolving state
|
|
97
|
-
timestamp = datetime.now().isoformat()
|
|
98
|
-
|
|
99
|
-
return TurnInfo(
|
|
100
|
-
turn_number=1, # Always Turn 1
|
|
101
|
-
user_message="", # Empty - full content used elsewhere for summary generation
|
|
102
|
-
start_line=1,
|
|
103
|
-
end_line=len(full_content.splitlines()) if full_content else 0,
|
|
104
|
-
timestamp=timestamp,
|
|
105
|
-
)
|
|
106
|
-
|
|
107
|
-
def is_turn_complete(self, session_file: Path, turn_number: int) -> bool:
|
|
108
|
-
# For Antigravity, if we have artifacts, it's "complete" in the sense that it exists.
|
|
109
|
-
return self.count_complete_turns(session_file) >= 1
|
|
110
|
-
|
|
111
|
-
def get_detailed_analysis(self, session_file: Path) -> Dict[str, Any]:
|
|
112
|
-
"""
|
|
113
|
-
Get detailed analysis of the session.
|
|
114
|
-
Since we treat the state as a single accumulated turn, we return one turn group.
|
|
115
|
-
"""
|
|
116
|
-
current_turn = self.count_complete_turns(session_file)
|
|
117
|
-
|
|
118
|
-
groups = []
|
|
119
|
-
# Return a single entry representing the current state
|
|
120
|
-
if current_turn > 0:
|
|
121
|
-
info = self.extract_turn_info(session_file, 1)
|
|
122
|
-
if info:
|
|
123
|
-
groups.append(
|
|
124
|
-
{
|
|
125
|
-
"turn_number": 1,
|
|
126
|
-
"user_message": info.user_message,
|
|
127
|
-
"summary_message": "Antigravity Session State",
|
|
128
|
-
"turn_status": "completed",
|
|
129
|
-
"start_line": info.start_line,
|
|
130
|
-
"end_line": info.end_line,
|
|
131
|
-
"timestamp": info.timestamp,
|
|
132
|
-
}
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
return {
|
|
136
|
-
"groups": groups,
|
|
137
|
-
"total_turns": 1 if current_turn > 0 else 0, # Conceptually one continuous session
|
|
138
|
-
"latest_turn_id": 1 if current_turn > 0 else 0,
|
|
139
|
-
"format": "antigravity_markdown",
|
|
140
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|