@hupan56/wlkj 3.1.24 → 3.1.26
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.
- package/bin/cli.js +1516 -1492
- package/package.json +1 -1
- package/templates/qoder/scripts/README.md +102 -102
- package/templates/qoder/scripts/deployment/setup/init_doctor.py +1 -1
- package/templates/qoder/scripts/deployment/setup/setup.py +1 -1
- package/templates/qoder/scripts/domain/report/add_session.py +2 -2
- package/templates/qoder/scripts/domain/task/task.py +1 -1
- package/templates/qoder/scripts/foundation/core/paths.py +705 -705
- package/templates/qoder/scripts/foundation/data/task_utils.py +427 -427
- package/templates/qoder/scripts/foundation/integrations/active_task.py +244 -244
- package/templates/root/AGENTS.md +1 -1
|
@@ -1,245 +1,245 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
QODER Pipeline - 会话级活跃任务管理
|
|
5
|
-
|
|
6
|
-
核心概念: "活跃任务" 是按 AI 会话/窗口存储的, 不是全局的。
|
|
7
|
-
|
|
8
|
-
存储位置: .qoder/.runtime/sessions/<session-id>
|
|
9
|
-
每个会话文件只包含一行: 活跃任务的相对路径。
|
|
10
|
-
|
|
11
|
-
会话身份来源 (按优先级):
|
|
12
|
-
1. 环境变量: QODER_SESSION_ID, CODEX_SESSION_ID, CODEX_THREAD_ID 等
|
|
13
|
-
2. Hook 输入中的 session_id 字段
|
|
14
|
-
3. TRELLIS_CONTEXT_ID 环境变量
|
|
15
|
-
4. 后备: 全局 .qoder/.current-task 文件
|
|
16
|
-
|
|
17
|
-
参考:
|
|
18
|
-
"""
|
|
19
|
-
|
|
20
|
-
from __future__ import annotations
|
|
21
|
-
|
|
22
|
-
import hashlib
|
|
23
|
-
import json
|
|
24
|
-
import os
|
|
25
|
-
import sys
|
|
26
|
-
from dataclasses import dataclass
|
|
27
|
-
from pathlib import Path
|
|
28
|
-
from typing import Any
|
|
29
|
-
|
|
30
|
-
# 支持的 AI 平台及其会话环境变量
|
|
31
|
-
# -----------------------------------------------------------------------------
|
|
32
|
-
# 单一事实源: nexus.registry.HOST_TABLE(宿主能力抽象层)。
|
|
33
|
-
# 本表从 HOST_TABLE 派生,保持 tuple[tuple[str, tuple[str, ...]]] 格式兼容。
|
|
34
|
-
# 新增宿主只需改 nexus.HOST_TABLE 一处,active_task 自动跟随。
|
|
35
|
-
# nexus 不可用时回退到内联定义(向后兼容,零破坏)。
|
|
36
|
-
# -----------------------------------------------------------------------------
|
|
37
|
-
try:
|
|
38
|
-
from capability.registry import HOST_TABLE as _HOST_TABLE
|
|
39
|
-
|
|
40
|
-
_ENV_SESSION_KEYS: tuple[tuple[str, tuple[str, ...]], ...] = tuple(
|
|
41
|
-
(name, tuple(info.get("detect", ())))
|
|
42
|
-
for name, info in _HOST_TABLE.items()
|
|
43
|
-
if name != "cli" and info.get("detect")
|
|
44
|
-
)
|
|
45
|
-
except ImportError:
|
|
46
|
-
_ENV_SESSION_KEYS = (
|
|
47
|
-
("qoderwork", ("QODER_SESSION_ID",)),
|
|
48
|
-
("codex", ("CODEX_SESSION_ID", "CODEX_THREAD_ID")),
|
|
49
|
-
("claude", ("CLAUDE_SESSION_ID", "CLAUDE_CODE_SESSION_ID")),
|
|
50
|
-
("cursor", ("CURSOR_SESSION_ID",)),
|
|
51
|
-
("opencode", ("OPENCODE_SESSION_ID", "OPENCODE_RUN_ID")),
|
|
52
|
-
("gemini", ("GEMINI_SESSION_ID",)),
|
|
53
|
-
("copilot", ("COPILOT_SESSION_ID",)),
|
|
54
|
-
)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
@dataclass(frozen=True)
|
|
58
|
-
class ActiveTask:
|
|
59
|
-
"""活跃任务状态。
|
|
60
|
-
|
|
61
|
-
Attributes:
|
|
62
|
-
task_path: 任务相对路径, None 表示无活跃任务。
|
|
63
|
-
source_type: 来源类型 (session / global-fallback / none)。
|
|
64
|
-
context_key: 会话标识键。
|
|
65
|
-
stale: 是否过期。
|
|
66
|
-
"""
|
|
67
|
-
task_path: str | None
|
|
68
|
-
source_type: str # "session" | "global-fallback" | "none"
|
|
69
|
-
context_key: str | None = None
|
|
70
|
-
stale: bool = False
|
|
71
|
-
|
|
72
|
-
@property
|
|
73
|
-
def source(self) -> str:
|
|
74
|
-
"""人类可读的来源标签。"""
|
|
75
|
-
if self.source_type == "session" and self.context_key:
|
|
76
|
-
return f"session:{self.context_key}"
|
|
77
|
-
if self.source_type == "global-fallback":
|
|
78
|
-
return "global-fallback"
|
|
79
|
-
return "none"
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
# =============================================================================
|
|
83
|
-
# 会话身份解析
|
|
84
|
-
# =============================================================================
|
|
85
|
-
|
|
86
|
-
def resolve_context_key() -> str | None:
|
|
87
|
-
"""解析当前会话的唯一标识键。
|
|
88
|
-
|
|
89
|
-
按平台优先级检查环境变量, 返回第一个找到的值。
|
|
90
|
-
|
|
91
|
-
Returns:
|
|
92
|
-
会话 ID 字符串, 无则返回 None。
|
|
93
|
-
"""
|
|
94
|
-
# 1. 检查通用环境变量
|
|
95
|
-
for env_var in ("TRELLIS_CONTEXT_ID", "QODER_CONTEXT_ID"):
|
|
96
|
-
val = os.environ.get(env_var)
|
|
97
|
-
if val:
|
|
98
|
-
return val
|
|
99
|
-
|
|
100
|
-
# 2. 检查各平台专用环境变量
|
|
101
|
-
for _platform, env_vars in _ENV_SESSION_KEYS:
|
|
102
|
-
for env_var in env_vars:
|
|
103
|
-
val = os.environ.get(env_var)
|
|
104
|
-
if val:
|
|
105
|
-
return val
|
|
106
|
-
|
|
107
|
-
return None
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
def _get_session_file(session_key: str, repo_root: Path) -> Path:
|
|
111
|
-
"""获取会话状态文件路径。
|
|
112
|
-
|
|
113
|
-
Args:
|
|
114
|
-
session_key: 会话标识。
|
|
115
|
-
repo_root: 项目根目录。
|
|
116
|
-
|
|
117
|
-
Returns:
|
|
118
|
-
会话文件路径。
|
|
119
|
-
"""
|
|
120
|
-
# 对 session_key 做 hash 避免特殊字符问题
|
|
121
|
-
key_hash = hashlib.sha256(session_key.encode()).hexdigest()[:16]
|
|
122
|
-
sessions_dir = repo_root / ".qoder" / ".runtime" / "sessions"
|
|
123
|
-
sessions_dir.mkdir(parents=True, exist_ok=True)
|
|
124
|
-
return sessions_dir / f"session-{key_hash}"
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
# =============================================================================
|
|
128
|
-
# 活跃任务读写
|
|
129
|
-
# =============================================================================
|
|
130
|
-
|
|
131
|
-
def resolve_active_task(repo_root: Path | None = None) -> ActiveTask:
|
|
132
|
-
"""解析当前会话的活跃任务。
|
|
133
|
-
|
|
134
|
-
优先级:
|
|
135
|
-
1. 会话级状态文件
|
|
136
|
-
2. 全局 .current-task 文件 (后备)
|
|
137
|
-
|
|
138
|
-
Args:
|
|
139
|
-
repo_root: 项目根目录, 默认自动检测。
|
|
140
|
-
|
|
141
|
-
Returns:
|
|
142
|
-
ActiveTask 对象。
|
|
143
|
-
"""
|
|
144
|
-
if repo_root is None:
|
|
145
|
-
from foundation.core.paths import get_repo_root
|
|
146
|
-
repo_root = get_repo_root()
|
|
147
|
-
|
|
148
|
-
# 1. 尝试会话级
|
|
149
|
-
session_key = resolve_context_key()
|
|
150
|
-
if session_key:
|
|
151
|
-
session_file = _get_session_file(session_key, repo_root)
|
|
152
|
-
if session_file.is_file():
|
|
153
|
-
try:
|
|
154
|
-
task_path = session_file.read_text(encoding="utf-8").strip()
|
|
155
|
-
if task_path:
|
|
156
|
-
return ActiveTask(
|
|
157
|
-
task_path=task_path,
|
|
158
|
-
source_type="session",
|
|
159
|
-
context_key=session_key,
|
|
160
|
-
)
|
|
161
|
-
except (OSError, IOError):
|
|
162
|
-
pass
|
|
163
|
-
|
|
164
|
-
# 2. 后备: 全局文件
|
|
165
|
-
from foundation.core.paths import get_current_task
|
|
166
|
-
global_task = get_current_task(repo_root)
|
|
167
|
-
if global_task:
|
|
168
|
-
return ActiveTask(
|
|
169
|
-
task_path=global_task,
|
|
170
|
-
source_type="global-fallback",
|
|
171
|
-
context_key=session_key,
|
|
172
|
-
)
|
|
173
|
-
|
|
174
|
-
return ActiveTask(task_path=None, source_type="none")
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
def set_active_task(task_path: str, repo_root: Path | None = None) -> ActiveTask | None:
|
|
178
|
-
"""设置当前会话的活跃任务。
|
|
179
|
-
|
|
180
|
-
Args:
|
|
181
|
-
task_path: 任务相对路径。
|
|
182
|
-
repo_root: 项目根目录, 默认自动检测。
|
|
183
|
-
|
|
184
|
-
Returns:
|
|
185
|
-
ActiveTask 对象, 无会话身份则返回 None。
|
|
186
|
-
"""
|
|
187
|
-
if repo_root is None:
|
|
188
|
-
from foundation.core.paths import get_repo_root
|
|
189
|
-
repo_root = get_repo_root()
|
|
190
|
-
|
|
191
|
-
session_key = resolve_context_key()
|
|
192
|
-
if not session_key:
|
|
193
|
-
# 无会话身份, 使用全局后备
|
|
194
|
-
from foundation.core.paths import set_current_task
|
|
195
|
-
set_current_task(task_path, repo_root)
|
|
196
|
-
return ActiveTask(
|
|
197
|
-
task_path=task_path,
|
|
198
|
-
source_type="global-fallback",
|
|
199
|
-
context_key=None,
|
|
200
|
-
)
|
|
201
|
-
|
|
202
|
-
session_file = _get_session_file(session_key, repo_root)
|
|
203
|
-
try:
|
|
204
|
-
session_file.parent.mkdir(parents=True, exist_ok=True)
|
|
205
|
-
session_file.write_text(task_path, encoding="utf-8")
|
|
206
|
-
return ActiveTask(
|
|
207
|
-
task_path=task_path,
|
|
208
|
-
source_type="session",
|
|
209
|
-
context_key=session_key,
|
|
210
|
-
)
|
|
211
|
-
except (OSError, IOError) as e:
|
|
212
|
-
print(f"Warning: Failed to write session file: {e}", file=sys.stderr)
|
|
213
|
-
return None
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
def clear_active_task(repo_root: Path | None = None) -> ActiveTask:
|
|
217
|
-
"""清除当前会话的活跃任务。
|
|
218
|
-
|
|
219
|
-
Args:
|
|
220
|
-
repo_root: 项目根目录, 默认自动检测。
|
|
221
|
-
|
|
222
|
-
Returns:
|
|
223
|
-
清除前的 ActiveTask 状态。
|
|
224
|
-
"""
|
|
225
|
-
if repo_root is None:
|
|
226
|
-
from foundation.core.paths import get_repo_root
|
|
227
|
-
repo_root = get_repo_root()
|
|
228
|
-
|
|
229
|
-
# 获取清除前的状态
|
|
230
|
-
active = resolve_active_task(repo_root)
|
|
231
|
-
|
|
232
|
-
# 清除会话文件
|
|
233
|
-
session_key = resolve_context_key()
|
|
234
|
-
if session_key:
|
|
235
|
-
session_file = _get_session_file(session_key, repo_root)
|
|
236
|
-
try:
|
|
237
|
-
session_file.unlink(missing_ok=True)
|
|
238
|
-
except (OSError, IOError):
|
|
239
|
-
pass
|
|
240
|
-
|
|
241
|
-
# 也清除全局后备
|
|
242
|
-
from foundation.core.paths import set_current_task
|
|
243
|
-
set_current_task(None, repo_root)
|
|
244
|
-
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
QODER Pipeline - 会话级活跃任务管理
|
|
5
|
+
|
|
6
|
+
核心概念: "活跃任务" 是按 AI 会话/窗口存储的, 不是全局的。
|
|
7
|
+
|
|
8
|
+
存储位置: .qoder/.runtime/sessions/<session-id>
|
|
9
|
+
每个会话文件只包含一行: 活跃任务的相对路径。
|
|
10
|
+
|
|
11
|
+
会话身份来源 (按优先级):
|
|
12
|
+
1. 环境变量: QODER_SESSION_ID, CODEX_SESSION_ID, CODEX_THREAD_ID 等
|
|
13
|
+
2. Hook 输入中的 session_id 字段
|
|
14
|
+
3. TRELLIS_CONTEXT_ID 环境变量
|
|
15
|
+
4. 后备: 全局 .qoder/.current-task 文件
|
|
16
|
+
|
|
17
|
+
参考: common/active_task.py 设计
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import hashlib
|
|
23
|
+
import json
|
|
24
|
+
import os
|
|
25
|
+
import sys
|
|
26
|
+
from dataclasses import dataclass
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
from typing import Any
|
|
29
|
+
|
|
30
|
+
# 支持的 AI 平台及其会话环境变量
|
|
31
|
+
# -----------------------------------------------------------------------------
|
|
32
|
+
# 单一事实源: nexus.registry.HOST_TABLE(宿主能力抽象层)。
|
|
33
|
+
# 本表从 HOST_TABLE 派生,保持 tuple[tuple[str, tuple[str, ...]]] 格式兼容。
|
|
34
|
+
# 新增宿主只需改 nexus.HOST_TABLE 一处,active_task 自动跟随。
|
|
35
|
+
# nexus 不可用时回退到内联定义(向后兼容,零破坏)。
|
|
36
|
+
# -----------------------------------------------------------------------------
|
|
37
|
+
try:
|
|
38
|
+
from capability.registry import HOST_TABLE as _HOST_TABLE
|
|
39
|
+
|
|
40
|
+
_ENV_SESSION_KEYS: tuple[tuple[str, tuple[str, ...]], ...] = tuple(
|
|
41
|
+
(name, tuple(info.get("detect", ())))
|
|
42
|
+
for name, info in _HOST_TABLE.items()
|
|
43
|
+
if name != "cli" and info.get("detect")
|
|
44
|
+
)
|
|
45
|
+
except ImportError:
|
|
46
|
+
_ENV_SESSION_KEYS = (
|
|
47
|
+
("qoderwork", ("QODER_SESSION_ID",)),
|
|
48
|
+
("codex", ("CODEX_SESSION_ID", "CODEX_THREAD_ID")),
|
|
49
|
+
("claude", ("CLAUDE_SESSION_ID", "CLAUDE_CODE_SESSION_ID")),
|
|
50
|
+
("cursor", ("CURSOR_SESSION_ID",)),
|
|
51
|
+
("opencode", ("OPENCODE_SESSION_ID", "OPENCODE_RUN_ID")),
|
|
52
|
+
("gemini", ("GEMINI_SESSION_ID",)),
|
|
53
|
+
("copilot", ("COPILOT_SESSION_ID",)),
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass(frozen=True)
|
|
58
|
+
class ActiveTask:
|
|
59
|
+
"""活跃任务状态。
|
|
60
|
+
|
|
61
|
+
Attributes:
|
|
62
|
+
task_path: 任务相对路径, None 表示无活跃任务。
|
|
63
|
+
source_type: 来源类型 (session / global-fallback / none)。
|
|
64
|
+
context_key: 会话标识键。
|
|
65
|
+
stale: 是否过期。
|
|
66
|
+
"""
|
|
67
|
+
task_path: str | None
|
|
68
|
+
source_type: str # "session" | "global-fallback" | "none"
|
|
69
|
+
context_key: str | None = None
|
|
70
|
+
stale: bool = False
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def source(self) -> str:
|
|
74
|
+
"""人类可读的来源标签。"""
|
|
75
|
+
if self.source_type == "session" and self.context_key:
|
|
76
|
+
return f"session:{self.context_key}"
|
|
77
|
+
if self.source_type == "global-fallback":
|
|
78
|
+
return "global-fallback"
|
|
79
|
+
return "none"
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# =============================================================================
|
|
83
|
+
# 会话身份解析
|
|
84
|
+
# =============================================================================
|
|
85
|
+
|
|
86
|
+
def resolve_context_key() -> str | None:
|
|
87
|
+
"""解析当前会话的唯一标识键。
|
|
88
|
+
|
|
89
|
+
按平台优先级检查环境变量, 返回第一个找到的值。
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
会话 ID 字符串, 无则返回 None。
|
|
93
|
+
"""
|
|
94
|
+
# 1. 检查通用环境变量
|
|
95
|
+
for env_var in ("TRELLIS_CONTEXT_ID", "QODER_CONTEXT_ID"):
|
|
96
|
+
val = os.environ.get(env_var)
|
|
97
|
+
if val:
|
|
98
|
+
return val
|
|
99
|
+
|
|
100
|
+
# 2. 检查各平台专用环境变量
|
|
101
|
+
for _platform, env_vars in _ENV_SESSION_KEYS:
|
|
102
|
+
for env_var in env_vars:
|
|
103
|
+
val = os.environ.get(env_var)
|
|
104
|
+
if val:
|
|
105
|
+
return val
|
|
106
|
+
|
|
107
|
+
return None
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _get_session_file(session_key: str, repo_root: Path) -> Path:
|
|
111
|
+
"""获取会话状态文件路径。
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
session_key: 会话标识。
|
|
115
|
+
repo_root: 项目根目录。
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
会话文件路径。
|
|
119
|
+
"""
|
|
120
|
+
# 对 session_key 做 hash 避免特殊字符问题
|
|
121
|
+
key_hash = hashlib.sha256(session_key.encode()).hexdigest()[:16]
|
|
122
|
+
sessions_dir = repo_root / ".qoder" / ".runtime" / "sessions"
|
|
123
|
+
sessions_dir.mkdir(parents=True, exist_ok=True)
|
|
124
|
+
return sessions_dir / f"session-{key_hash}"
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
# =============================================================================
|
|
128
|
+
# 活跃任务读写
|
|
129
|
+
# =============================================================================
|
|
130
|
+
|
|
131
|
+
def resolve_active_task(repo_root: Path | None = None) -> ActiveTask:
|
|
132
|
+
"""解析当前会话的活跃任务。
|
|
133
|
+
|
|
134
|
+
优先级:
|
|
135
|
+
1. 会话级状态文件
|
|
136
|
+
2. 全局 .current-task 文件 (后备)
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
repo_root: 项目根目录, 默认自动检测。
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
ActiveTask 对象。
|
|
143
|
+
"""
|
|
144
|
+
if repo_root is None:
|
|
145
|
+
from foundation.core.paths import get_repo_root
|
|
146
|
+
repo_root = get_repo_root()
|
|
147
|
+
|
|
148
|
+
# 1. 尝试会话级
|
|
149
|
+
session_key = resolve_context_key()
|
|
150
|
+
if session_key:
|
|
151
|
+
session_file = _get_session_file(session_key, repo_root)
|
|
152
|
+
if session_file.is_file():
|
|
153
|
+
try:
|
|
154
|
+
task_path = session_file.read_text(encoding="utf-8").strip()
|
|
155
|
+
if task_path:
|
|
156
|
+
return ActiveTask(
|
|
157
|
+
task_path=task_path,
|
|
158
|
+
source_type="session",
|
|
159
|
+
context_key=session_key,
|
|
160
|
+
)
|
|
161
|
+
except (OSError, IOError):
|
|
162
|
+
pass
|
|
163
|
+
|
|
164
|
+
# 2. 后备: 全局文件
|
|
165
|
+
from foundation.core.paths import get_current_task
|
|
166
|
+
global_task = get_current_task(repo_root)
|
|
167
|
+
if global_task:
|
|
168
|
+
return ActiveTask(
|
|
169
|
+
task_path=global_task,
|
|
170
|
+
source_type="global-fallback",
|
|
171
|
+
context_key=session_key,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
return ActiveTask(task_path=None, source_type="none")
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def set_active_task(task_path: str, repo_root: Path | None = None) -> ActiveTask | None:
|
|
178
|
+
"""设置当前会话的活跃任务。
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
task_path: 任务相对路径。
|
|
182
|
+
repo_root: 项目根目录, 默认自动检测。
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
ActiveTask 对象, 无会话身份则返回 None。
|
|
186
|
+
"""
|
|
187
|
+
if repo_root is None:
|
|
188
|
+
from foundation.core.paths import get_repo_root
|
|
189
|
+
repo_root = get_repo_root()
|
|
190
|
+
|
|
191
|
+
session_key = resolve_context_key()
|
|
192
|
+
if not session_key:
|
|
193
|
+
# 无会话身份, 使用全局后备
|
|
194
|
+
from foundation.core.paths import set_current_task
|
|
195
|
+
set_current_task(task_path, repo_root)
|
|
196
|
+
return ActiveTask(
|
|
197
|
+
task_path=task_path,
|
|
198
|
+
source_type="global-fallback",
|
|
199
|
+
context_key=None,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
session_file = _get_session_file(session_key, repo_root)
|
|
203
|
+
try:
|
|
204
|
+
session_file.parent.mkdir(parents=True, exist_ok=True)
|
|
205
|
+
session_file.write_text(task_path, encoding="utf-8")
|
|
206
|
+
return ActiveTask(
|
|
207
|
+
task_path=task_path,
|
|
208
|
+
source_type="session",
|
|
209
|
+
context_key=session_key,
|
|
210
|
+
)
|
|
211
|
+
except (OSError, IOError) as e:
|
|
212
|
+
print(f"Warning: Failed to write session file: {e}", file=sys.stderr)
|
|
213
|
+
return None
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def clear_active_task(repo_root: Path | None = None) -> ActiveTask:
|
|
217
|
+
"""清除当前会话的活跃任务。
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
repo_root: 项目根目录, 默认自动检测。
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
清除前的 ActiveTask 状态。
|
|
224
|
+
"""
|
|
225
|
+
if repo_root is None:
|
|
226
|
+
from foundation.core.paths import get_repo_root
|
|
227
|
+
repo_root = get_repo_root()
|
|
228
|
+
|
|
229
|
+
# 获取清除前的状态
|
|
230
|
+
active = resolve_active_task(repo_root)
|
|
231
|
+
|
|
232
|
+
# 清除会话文件
|
|
233
|
+
session_key = resolve_context_key()
|
|
234
|
+
if session_key:
|
|
235
|
+
session_file = _get_session_file(session_key, repo_root)
|
|
236
|
+
try:
|
|
237
|
+
session_file.unlink(missing_ok=True)
|
|
238
|
+
except (OSError, IOError):
|
|
239
|
+
pass
|
|
240
|
+
|
|
241
|
+
# 也清除全局后备
|
|
242
|
+
from foundation.core.paths import set_current_task
|
|
243
|
+
set_current_task(None, repo_root)
|
|
244
|
+
|
|
245
245
|
return active
|