-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathcore.py
More file actions
183 lines (147 loc) · 6.96 KB
/
core.py
File metadata and controls
183 lines (147 loc) · 6.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
"""Claude Code core - session management and plugin lifecycle."""
import time
import sublime
import sublime_plugin
from typing import Dict, Optional
from .session import Session, load_saved_sessions, save_sessions
_auto_sleep_timer = None
def plugin_loaded() -> None:
"""Called when plugin is loaded. Start MCP server and notalone client."""
# Initialize session registry on sublime module (singleton)
if not hasattr(sublime, '_claude_sessions'):
sublime._claude_sessions = {}
# Start MCP server
from . import mcp_server
mcp_server.start()
# Start global notalone client (receives all injects for sublime.* sessions)
from . import notalone
notalone.start()
# Register orphaned claude output views as sleeping sessions
def register_orphans():
import re
saved_sessions = load_saved_sessions()
for window in sublime.windows():
for view in window.views():
if not view.settings().get("claude_output"):
continue
if view.id() in sublime._claude_sessions:
continue
# Extract session name from view title
name = view.name()
name = re.sub(r'^[◉◇•❓⏸⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]\s*', '', name)
if name.startswith("Claude: "):
name = name[8:]
if name.startswith("[") and "] " in name:
name = name[name.index("] ") + 2:]
if name.endswith("\u2026"):
name = name[:-1]
session_name = name if name and name != "Claude" else None
# Find resume_id from saved sessions
resume_id = None
if session_name:
for saved in saved_sessions:
saved_name = saved.get("name") or ""
if not saved.get("session_id"):
continue
if saved_name == session_name or saved_name.startswith(session_name):
resume_id = saved.get("session_id")
session_name = saved_name
break
if not resume_id:
continue
# Ensure scratch is restored (may have been unset by previous buggy code)
if not view.is_scratch():
view.set_scratch(True)
backend = view.settings().get("claude_backend", "claude")
session = Session(window, resume_id=resume_id, backend=backend)
session.name = session_name
session.output.view = view
session.draft_prompt = ""
sublime._claude_sessions[view.id()] = session
session._apply_sleep_ui()
schedule_auto_sleep()
sublime.set_timeout(register_orphans, 500)
# Sync order table bookmarks after windows are ready
def sync_orders():
from .order_table import sync_bookmarks
for window in sublime.windows():
sync_bookmarks(window)
sublime.set_timeout(sync_orders, 1000)
def plugin_unloaded() -> None:
"""Called when plugin is unloaded. Stop MCP server and notalone client."""
from . import mcp_server
mcp_server.stop()
from . import notalone
notalone.stop()
def get_session_for_view(view: sublime.View) -> Optional[Session]:
"""Get session for a specific output view."""
return sublime._claude_sessions.get(view.id())
def get_active_session(window: sublime.Window) -> Optional[Session]:
"""Get session for active view if it's a Claude output, or last active Claude session in window."""
view = window.active_view()
if view and view.settings().get("claude_output"):
return sublime._claude_sessions.get(view.id())
# Check for last active Claude view in this window
active_view_id = window.settings().get("claude_active_view")
if active_view_id and active_view_id in sublime._claude_sessions:
session = sublime._claude_sessions[active_view_id]
if session.window == window:
return session
# Fallback: return any session in this window
for view_id, session in sublime._claude_sessions.items():
if session.window == window:
return session
return None
def create_session(window: sublime.Window, resume_id: Optional[str] = None, fork: bool = False, profile: Optional[dict] = None, initial_context: Optional[dict] = None, backend: str = "claude") -> Session:
"""Create a new session (always creates new, doesn't reuse)."""
# Clear active marker from previous active session
old_active = window.settings().get("claude_active_view")
if old_active and old_active in sublime._claude_sessions:
old_session = sublime._claude_sessions[old_active]
old_session.output.set_name(old_session.name or "Claude")
s = Session(window, resume_id=resume_id, fork=fork, profile=profile, initial_context=initial_context, backend=backend)
s.output.show() # Create view first
if s.output.view and backend != "claude":
s.output.view.settings().set("claude_backend", backend)
backend_names = {"codex": "Codex", "copilot": "Copilot"}
s.output.set_name(backend_names.get(backend, backend.title()))
# Apply backend-specific background
backend_themes = {
"codex": "Packages/ClaudeCode/ClaudeOutput-codex.hidden-tmTheme",
"copilot": "Packages/ClaudeCode/ClaudeOutput-copilot.hidden-tmTheme",
}
theme = backend_themes.get(backend)
if theme:
s.output.view.settings().set("color_scheme", theme)
s.start()
# Register by view id and mark as active
if s.output.view:
view_id = s.output.view.id()
sublime._claude_sessions[view_id] = s
window.settings().set("claude_active_view", view_id)
print(f"[Claude] create_session: view_id={view_id}")
else:
print(f"[Claude] create_session: ERROR - no output view!")
schedule_auto_sleep()
return s
def _check_auto_sleep():
global _auto_sleep_timer
_auto_sleep_timer = None
settings = sublime.load_settings("ClaudeCode.sublime-settings")
timeout_min = settings.get("auto_sleep_minutes", 60)
if not timeout_min or timeout_min <= 0:
return
threshold = time.time() - (timeout_min * 60)
for view_id, session in list(sublime._claude_sessions.items()):
if (session.initialized
and not session.working
and not session.is_sleeping
and session.last_activity > 0
and session.last_activity < threshold):
print(f"[Claude] auto-sleep: {session.name} idle for >{timeout_min}m")
session.sleep()
schedule_auto_sleep()
def schedule_auto_sleep():
global _auto_sleep_timer
if _auto_sleep_timer is None and hasattr(sublime, '_claude_sessions') and sublime._claude_sessions:
_auto_sleep_timer = sublime.set_timeout(_check_auto_sleep, 60000)