codexmate 0.1.6 → 0.1.8
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/README.md +3 -1
- package/README.vi.md +6 -3
- package/README.zh.md +6 -3
- package/cli/system-prompt-files.js +177 -0
- package/cli.js +27 -0
- package/package.json +1 -1
- package/skills/{codexmate-project-context-recovery → context-recovery}/SKILL.md +2 -2
- package/web-ui/app.js +19 -0
- package/web-ui/modules/app.computed.session.mjs +1 -1
- package/web-ui/modules/app.methods.agents.mjs +5 -12
- package/web-ui/modules/app.methods.index.mjs +2 -0
- package/web-ui/modules/app.methods.session-actions.mjs +27 -0
- package/web-ui/modules/app.methods.session-browser.mjs +21 -1
- package/web-ui/modules/app.methods.session-trash.mjs +5 -0
- package/web-ui/modules/app.methods.system-prompt.mjs +294 -0
- package/web-ui/modules/i18n/locales/en.mjs +29 -0
- package/web-ui/modules/i18n/locales/ja.mjs +43 -0
- package/web-ui/modules/i18n/locales/vi.mjs +27 -0
- package/web-ui/modules/i18n/locales/zh-tw.mjs +27 -0
- package/web-ui/modules/i18n/locales/zh.mjs +27 -0
- package/web-ui/modules/request-token.mjs +9 -0
- package/web-ui/partials/index/panel-prompts.html +96 -1
- package/web-ui/partials/index/panel-settings.html +0 -23
- package/web-ui/partials/index/panel-trash.html +36 -1
- package/web-ui/res/web-ui-render.precompiled.js +750 -456
- package/web-ui/session-helpers.mjs +9 -0
- package/web-ui/styles/responsive.css +18 -0
- package/web-ui/styles/sessions-list.css +5 -0
- package/web-ui/styles/sessions-usage.css +20 -1
- package/web-ui/styles/system-prompt.css +23 -0
- package/web-ui/styles/trash-panel.css +9 -0
- package/web-ui/styles.css +1 -0
- /package/skills/{codexmate-project-context-recovery → context-recovery}/scripts/search_sessions.py +0 -0
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# Codex Mate
|
|
6
6
|
|
|
7
|
-
**One dashboard for all your local AI coding agents. Switch providers, manage sessions, and orchestrate tasks across Codex, Claude Code, OpenCode, KiloCode, and OpenClaw. Zero cloud, local-first control plane.**
|
|
7
|
+
**One dashboard for all your local AI coding agents. Switch providers, manage sessions, and orchestrate tasks across Codex, Claude Code, Gemini CLI, CodeBuddy Code, Pi, OpenCode, KiloCode, and OpenClaw. Zero cloud, local-first control plane.**
|
|
8
8
|
|
|
9
9
|
<p>
|
|
10
10
|
<a href="https://sakurabytecore.github.io/codexmate/">[Documentation]</a>
|
|
@@ -132,6 +132,8 @@ curl -fsSL https://raw.githubusercontent.com/SakuraByteCore/codexmate/main/scrip
|
|
|
132
132
|
- **CodeBuddy**: `npm install -g @tencent-ai/codebuddy-code`
|
|
133
133
|
- **KiloCode**: `npm install -g @kilocode/cli` (`kilo` / `kilocode`)
|
|
134
134
|
- **OpenCode**: install from the [official OpenCode docs](https://opencode.ai/)
|
|
135
|
+
- **OpenClaw**: see the [OpenClaw repository](https://github.com/moeru-ai/openclaw)
|
|
136
|
+
- **Pi**: sessions are discovered from `~/.pi/agent/sessions`
|
|
135
137
|
|
|
136
138
|
---
|
|
137
139
|
|
package/README.vi.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# Codex Mate
|
|
6
6
|
|
|
7
|
-
**Một dashboard duy nhất cho tất cả AI coding agent cục bộ của bạn. Chuyển đổi provider, quản lý phiên làm việc và điều phối tác vụ giữa Codex, Claude Code và OpenClaw. Không cloud, trung tâm điều khiển agent hoàn toàn cục bộ.**
|
|
7
|
+
**Một dashboard duy nhất cho tất cả AI coding agent cục bộ của bạn. Chuyển đổi provider, quản lý phiên làm việc và điều phối tác vụ giữa Codex, Claude Code, Gemini CLI, CodeBuddy Code, Pi, OpenCode, KiloCode và OpenClaw. Không cloud, trung tâm điều khiển agent hoàn toàn cục bộ.**
|
|
8
8
|
|
|
9
9
|
<p>
|
|
10
10
|
<a href="https://sakurabytecore.github.io/codexmate/">[Tài liệu]</a>
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
|
|
42
42
|
Bạn có bao giờ cảm thấy rối khi phải quản lý nhiều AI agent cục bộ? Mỗi tool lại có định dạng cấu hình, nơi lưu phiên và thư mục skill riêng.
|
|
43
43
|
|
|
44
|
-
**Codex Mate** cung cấp một trung tâm điều khiển thống nhất để giải quyết sự hỗn loạn đó. Đây là CLI + Web UI ưu tiên cục bộ, được thiết kế để quản lý [Codex](https://github.com/openai/codex), [Claude Code](https://github.com/anthropic-ai/claude-code) và [OpenClaw](https://github.com/moeru-ai/openclaw) một cách liền mạch.
|
|
44
|
+
**Codex Mate** cung cấp một trung tâm điều khiển thống nhất để giải quyết sự hỗn loạn đó. Đây là CLI + Web UI ưu tiên cục bộ, được thiết kế để quản lý [Codex](https://github.com/openai/codex), [Claude Code](https://github.com/anthropic-ai/claude-code), [OpenCode](https://opencode.ai/), KiloCode và [OpenClaw](https://github.com/moeru-ai/openclaw) một cách liền mạch.
|
|
45
45
|
|
|
46
46
|
### Điểm nổi bật
|
|
47
47
|
|
|
@@ -58,7 +58,7 @@ Khác với các wrapper đơn giản, Codex Mate hoạt động như một **Ag
|
|
|
58
58
|
|
|
59
59
|
| Tính năng | Trạng thái | Mô tả |
|
|
60
60
|
| --- | --- | --- |
|
|
61
|
-
| **Quản lý Provider** | ✅ | Chuyển đổi provider/model cho Codex, Claude và OpenClaw |
|
|
61
|
+
| **Quản lý Provider** | ✅ | Chuyển đổi provider/model cho Codex, Claude, OpenCode, KiloCode và OpenClaw |
|
|
62
62
|
| **Đồng bộ Agent trực tiếp** | ✅ | Giám sát cấu hình & trạng thái Codex/Claude theo thời gian thực |
|
|
63
63
|
| **Trình duyệt phiên** | ✅ | Liệt kê, xem trước, lọc và xuất phiên (Codex/Claude/Gemini/CodeBuddy/Pi) |
|
|
64
64
|
| **Phân tích sử dụng** | ✅ | Trực quan hóa xu hướng tin nhắn và dự án nổi bật |
|
|
@@ -115,7 +115,10 @@ curl -fsSL https://raw.githubusercontent.com/SakuraByteCore/codexmate/main/scrip
|
|
|
115
115
|
- **Claude Code**: `npm install -g @anthropic-ai/claude-code`
|
|
116
116
|
- **Gemini CLI**: `npm install -g @google/gemini-cli`
|
|
117
117
|
- **CodeBuddy**: `npm install -g @tencent-ai/codebuddy-code`
|
|
118
|
+
- **KiloCode**: `npm install -g @kilocode/cli` (`kilo` / `kilocode`)
|
|
118
119
|
- **OpenCode**: xem [tài liệu chính thức OpenCode](https://opencode.ai/)
|
|
120
|
+
- **OpenClaw**: xem [kho lưu trữ OpenClaw](https://github.com/moeru-ai/openclaw)
|
|
121
|
+
- **Pi**: phiên làm việc được phát hiện từ `~/.pi/agent/sessions`
|
|
119
122
|
|
|
120
123
|
---
|
|
121
124
|
|
package/README.zh.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# Codex Mate
|
|
6
6
|
|
|
7
|
-
**一站式本地 AI 编程智能体管理面板。统一管理 Codex、Claude Code、OpenCode 与 OpenClaw,支持 Provider 切换、会话管理与任务编排。纯本地优先,你的智能体控制中心。**
|
|
7
|
+
**一站式本地 AI 编程智能体管理面板。统一管理 Codex、Claude Code、Gemini CLI、CodeBuddy Code、Pi、OpenCode、KiloCode 与 OpenClaw,支持 Provider 切换、会话管理与任务编排。纯本地优先,你的智能体控制中心。**
|
|
8
8
|
|
|
9
9
|
<p>
|
|
10
10
|
<a href="https://sakurabytecore.github.io/codexmate/">[项目文档]</a>
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
|
|
49
49
|
你是否曾因管理多个本地 AI 智能体而感到疲惫?每个工具都有自己的配置格式、会话存储和 Skills 目录。
|
|
50
50
|
|
|
51
|
-
**Codex Mate** 提供了一个统一的控制平面,让混乱重归有序。这是一个本地优先的 CLI + Web UI,旨在无缝管理 [Codex](https://github.com/openai/codex)、[Claude Code](https://github.com/anthropic-ai/claude-code)、[OpenCode](https://opencode.ai/) 和 [OpenClaw](https://github.com/moeru-ai/openclaw)。
|
|
51
|
+
**Codex Mate** 提供了一个统一的控制平面,让混乱重归有序。这是一个本地优先的 CLI + Web UI,旨在无缝管理 [Codex](https://github.com/openai/codex)、[Claude Code](https://github.com/anthropic-ai/claude-code)、[OpenCode](https://opencode.ai/)、KiloCode 和 [OpenClaw](https://github.com/moeru-ai/openclaw)。
|
|
52
52
|
|
|
53
53
|
### 有什么独特之处?
|
|
54
54
|
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
|
|
69
69
|
| 特性 | 状态 | 描述 |
|
|
70
70
|
| --- | --- | --- |
|
|
71
|
-
| **Provider 管理** | ✅ | 切换 Codex、Claude、OpenCode 和 OpenClaw 的 provider/model |
|
|
71
|
+
| **Provider 管理** | ✅ | 切换 Codex、Claude、OpenCode、KiloCode 和 OpenClaw 的 provider/model |
|
|
72
72
|
| **状态实时同步** | ✅ | 实时感知 Codex/Claude 的配置与运行状态变更 |
|
|
73
73
|
| **会话浏览器** | ✅ | 跨 Codex、Claude Code、Gemini CLI、CodeBuddy Code 与 Pi 的本地会话进行检索、预览、筛选与导出 |
|
|
74
74
|
| **Usage 统计** | ✅ | 可视化消息趋势与热门项目统计 |
|
|
@@ -127,7 +127,10 @@ curl -fsSL https://raw.githubusercontent.com/SakuraByteCore/codexmate/main/scrip
|
|
|
127
127
|
- **Claude Code**: `npm install -g @anthropic-ai/claude-code`
|
|
128
128
|
- **Gemini CLI**: `npm install -g @google/gemini-cli`
|
|
129
129
|
- **CodeBuddy**: `npm install -g @tencent-ai/codebuddy-code`
|
|
130
|
+
- **KiloCode**: `npm install -g @kilocode/cli`(`kilo` / `kilocode`)
|
|
130
131
|
- **OpenCode**: 参照 [OpenCode 官方文档](https://opencode.ai/) 安装
|
|
132
|
+
- **OpenClaw**: 参见 [OpenClaw 仓库](https://github.com/moeru-ai/openclaw)
|
|
133
|
+
- **Pi**: 会话从 `~/.pi/agent/sessions` 自动发现
|
|
131
134
|
|
|
132
135
|
---
|
|
133
136
|
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function createSystemPromptFileController(deps = {}) {
|
|
4
|
+
const {
|
|
5
|
+
fs,
|
|
6
|
+
path,
|
|
7
|
+
os,
|
|
8
|
+
crypto,
|
|
9
|
+
buildLineDiff,
|
|
10
|
+
CONFIG_DIR,
|
|
11
|
+
PI_AGENT_DIR
|
|
12
|
+
} = deps;
|
|
13
|
+
|
|
14
|
+
if (!fs) throw new Error('createSystemPromptFileController 缺少 fs');
|
|
15
|
+
if (!path) throw new Error('createSystemPromptFileController 缺少 path');
|
|
16
|
+
if (!os) throw new Error('createSystemPromptFileController 缺少 os');
|
|
17
|
+
if (!crypto) throw new Error('createSystemPromptFileController 缺少 crypto');
|
|
18
|
+
if (typeof buildLineDiff !== 'function') throw new Error('createSystemPromptFileController 缺少 buildLineDiff');
|
|
19
|
+
if (typeof CONFIG_DIR !== 'string' || !CONFIG_DIR) throw new Error('createSystemPromptFileController 缺少 CONFIG_DIR');
|
|
20
|
+
if (typeof PI_AGENT_DIR !== 'string' || !PI_AGENT_DIR) throw new Error('createSystemPromptFileController 缺少 PI_AGENT_DIR');
|
|
21
|
+
|
|
22
|
+
const MODES = {
|
|
23
|
+
system: 'SYSTEM.md',
|
|
24
|
+
append: 'APPEND_SYSTEM.md'
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const SCOPES = ['global', 'project'];
|
|
28
|
+
|
|
29
|
+
function normalizeMode(mode) {
|
|
30
|
+
const key = typeof mode === 'string' ? mode.trim() : '';
|
|
31
|
+
if (!Object.prototype.hasOwnProperty.call(MODES, key)) {
|
|
32
|
+
throw new Error("Invalid system prompt mode: expected 'system' or 'append'");
|
|
33
|
+
}
|
|
34
|
+
return key;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function normalizeScope(scope) {
|
|
38
|
+
const key = typeof scope === 'string' ? scope.trim() : '';
|
|
39
|
+
if (!SCOPES.includes(key)) {
|
|
40
|
+
throw new Error("Invalid system prompt scope: expected 'global' or 'project'");
|
|
41
|
+
}
|
|
42
|
+
return key;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function hashContent(content) {
|
|
46
|
+
return crypto.createHash('sha256').update(String(content || '')).digest('hex');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function resolveSystemPromptFilePath(params = {}) {
|
|
50
|
+
const scope = normalizeScope(params.scope);
|
|
51
|
+
const mode = normalizeMode(params.mode);
|
|
52
|
+
const filename = MODES[mode];
|
|
53
|
+
let base;
|
|
54
|
+
if (scope === 'global') {
|
|
55
|
+
base = PI_AGENT_DIR;
|
|
56
|
+
} else {
|
|
57
|
+
const cwd = typeof params.cwd === 'string' && params.cwd.trim()
|
|
58
|
+
? params.cwd.trim()
|
|
59
|
+
: process.cwd();
|
|
60
|
+
base = path.join(path.resolve(cwd), '.pi');
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
scope,
|
|
64
|
+
mode,
|
|
65
|
+
filename,
|
|
66
|
+
path: path.join(base, filename),
|
|
67
|
+
replaceDefault: mode === 'system'
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function readSystemPromptFile(params = {}) {
|
|
72
|
+
const target = resolveSystemPromptFilePath(params);
|
|
73
|
+
const exists = fs.existsSync(target.path);
|
|
74
|
+
let content = '';
|
|
75
|
+
if (exists) {
|
|
76
|
+
try {
|
|
77
|
+
content = fs.readFileSync(target.path, 'utf8');
|
|
78
|
+
} catch (e) {
|
|
79
|
+
return { ...target, exists: false, content: '', hash: hashContent(''), error: '读取 system prompt 失败: ' + e.message };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
...target,
|
|
84
|
+
exists,
|
|
85
|
+
content,
|
|
86
|
+
hash: hashContent(content)
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function saveSystemPromptFile(params = {}) {
|
|
91
|
+
const content = typeof params.content === 'string' ? params.content : '';
|
|
92
|
+
if (!content.trim()) {
|
|
93
|
+
return { error: 'System prompt 不能为空' };
|
|
94
|
+
}
|
|
95
|
+
if (content.length > 2 * 1024 * 1024) {
|
|
96
|
+
return { error: '内容过大(最大 2MB)' };
|
|
97
|
+
}
|
|
98
|
+
const target = resolveSystemPromptFilePath(params);
|
|
99
|
+
const current = readSystemPromptFilePath(target);
|
|
100
|
+
if (current.error) {
|
|
101
|
+
return { error: current.error };
|
|
102
|
+
}
|
|
103
|
+
const baseHash = typeof params.baseHash === 'string' ? params.baseHash.trim() : '';
|
|
104
|
+
if (baseHash && baseHash !== current.hash) {
|
|
105
|
+
return { error: '文件已被外部修改,请重新加载后再保存' };
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
const dir = path.dirname(target.path);
|
|
109
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
110
|
+
const finalContent = content.endsWith('\n') ? content : content + '\n';
|
|
111
|
+
fs.writeFileSync(target.path, finalContent, { encoding: 'utf8', mode: 0o600 });
|
|
112
|
+
try { fs.chmodSync(target.path, 0o600); } catch (_) {}
|
|
113
|
+
} catch (e) {
|
|
114
|
+
return { error: '写入 system prompt 失败: ' + e.message };
|
|
115
|
+
}
|
|
116
|
+
return readSystemPromptFilePath(target);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function readSystemPromptFilePath(resolved) {
|
|
120
|
+
const exists = fs.existsSync(resolved.path);
|
|
121
|
+
let content = '';
|
|
122
|
+
if (exists) {
|
|
123
|
+
try {
|
|
124
|
+
content = fs.readFileSync(resolved.path, 'utf8');
|
|
125
|
+
} catch (e) {
|
|
126
|
+
return { ...resolved, exists: false, content: '', hash: hashContent(''), error: '读取 system prompt 失败: ' + e.message };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
...resolved,
|
|
131
|
+
exists,
|
|
132
|
+
content,
|
|
133
|
+
hash: hashContent(content)
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function buildSystemPromptDiff(params = {}) {
|
|
138
|
+
const hasBaseContent = typeof params.baseContent === 'string';
|
|
139
|
+
const target = resolveSystemPromptFilePath(params);
|
|
140
|
+
const current = readSystemPromptFilePath(target);
|
|
141
|
+
if (current.error) {
|
|
142
|
+
return { error: current.error };
|
|
143
|
+
}
|
|
144
|
+
const beforeText = hasBaseContent
|
|
145
|
+
? (typeof params.baseContent === 'string' ? params.baseContent : '')
|
|
146
|
+
: (current.content || '');
|
|
147
|
+
const afterText = typeof params.content === 'string' ? params.content : '';
|
|
148
|
+
const normalizedBefore = beforeText.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
149
|
+
const normalizedAfter = afterText.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
150
|
+
const diff = buildLineDiff(normalizedBefore, normalizedAfter);
|
|
151
|
+
const hasChanges = diff.truncated
|
|
152
|
+
? normalizedBefore !== normalizedAfter
|
|
153
|
+
: (diff.stats.added > 0 || diff.stats.removed > 0);
|
|
154
|
+
return {
|
|
155
|
+
diff: {
|
|
156
|
+
...diff,
|
|
157
|
+
hasChanges
|
|
158
|
+
},
|
|
159
|
+
path: target.path,
|
|
160
|
+
exists: current.exists,
|
|
161
|
+
scope: target.scope,
|
|
162
|
+
mode: target.mode
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
readSystemPromptFile,
|
|
168
|
+
saveSystemPromptFile,
|
|
169
|
+
buildSystemPromptDiff,
|
|
170
|
+
normalizeMode,
|
|
171
|
+
normalizeScope
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
module.exports = {
|
|
176
|
+
createSystemPromptFileController
|
|
177
|
+
};
|
package/cli.js
CHANGED
|
@@ -126,6 +126,9 @@ const {
|
|
|
126
126
|
const {
|
|
127
127
|
createAgentsFileController
|
|
128
128
|
} = require('./cli/agents-files');
|
|
129
|
+
const {
|
|
130
|
+
createSystemPromptFileController
|
|
131
|
+
} = require('./cli/system-prompt-files');
|
|
129
132
|
const {
|
|
130
133
|
createArchiveHelperController
|
|
131
134
|
} = require('./cli/archive-helpers');
|
|
@@ -308,6 +311,7 @@ const FAST_SESSION_DETAIL_PREVIEW_FILE_BYTES = 256 * 1024;
|
|
|
308
311
|
const FAST_SESSION_DETAIL_PREVIEW_CHUNK_BYTES = 64 * 1024;
|
|
309
312
|
const FAST_SESSION_DETAIL_PREVIEW_MAX_BYTES = 1024 * 1024;
|
|
310
313
|
const AGENTS_FILE_NAME = 'AGENTS.md';
|
|
314
|
+
const PI_AGENT_DIR = path.join(os.homedir(), '.pi', 'agent');
|
|
311
315
|
const MODELS_CACHE_TTL_MS = 60 * 1000;
|
|
312
316
|
const MODELS_NEGATIVE_CACHE_TTL_MS = 5 * 1000;
|
|
313
317
|
const MODELS_CACHE_MAX_ENTRIES = 50;
|
|
@@ -2102,6 +2106,20 @@ const {
|
|
|
2102
2106
|
}
|
|
2103
2107
|
});
|
|
2104
2108
|
|
|
2109
|
+
const {
|
|
2110
|
+
readSystemPromptFile,
|
|
2111
|
+
saveSystemPromptFile,
|
|
2112
|
+
buildSystemPromptDiff
|
|
2113
|
+
} = createSystemPromptFileController({
|
|
2114
|
+
fs,
|
|
2115
|
+
path,
|
|
2116
|
+
os,
|
|
2117
|
+
crypto,
|
|
2118
|
+
buildLineDiff,
|
|
2119
|
+
CONFIG_DIR,
|
|
2120
|
+
PI_AGENT_DIR
|
|
2121
|
+
});
|
|
2122
|
+
|
|
2105
2123
|
const {
|
|
2106
2124
|
readOpenclawConfigFile,
|
|
2107
2125
|
applyOpenclawConfig,
|
|
@@ -13479,6 +13497,15 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
|
|
|
13479
13497
|
case 'apply-openclaw-workspace-file':
|
|
13480
13498
|
result = applyOpenclawWorkspaceFile(params || {});
|
|
13481
13499
|
break;
|
|
13500
|
+
case 'get-system-prompt':
|
|
13501
|
+
result = readSystemPromptFile(params || {});
|
|
13502
|
+
break;
|
|
13503
|
+
case 'apply-system-prompt':
|
|
13504
|
+
result = saveSystemPromptFile(params || {});
|
|
13505
|
+
break;
|
|
13506
|
+
case 'preview-system-prompt-diff':
|
|
13507
|
+
result = buildSystemPromptDiff(params || {});
|
|
13508
|
+
break;
|
|
13482
13509
|
case 'switch':
|
|
13483
13510
|
case 'use':
|
|
13484
13511
|
case 'add':
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: context-recovery
|
|
3
3
|
description: Recovers project handoff context from local Codex, Claude Code, Gemini, CodeBuddy, and codexmate-derived sessions. Use when the user asks what happened in prior project/PR/branch/file/error work, needs a handoff brief, wants old decisions or validations recovered, or asks to summarize cross-session project activity with evidence.
|
|
4
4
|
---
|
|
5
5
|
|
|
@@ -13,7 +13,7 @@ Old sessions are historical evidence. Mutable facts such as PR state, CI, releas
|
|
|
13
13
|
|
|
14
14
|
## Quick Start
|
|
15
15
|
|
|
16
|
-
Generate a context brief first. Resolve `scripts/search_sessions.py` relative to this skill directory; after npm installation the full package path is `node_modules/codexmate/skills/
|
|
16
|
+
Generate a context brief first. Resolve `scripts/search_sessions.py` relative to this skill directory; after npm installation the full package path is `node_modules/codexmate/skills/context-recovery/scripts/search_sessions.py`.
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
19
|
python3 scripts/search_sessions.py "SakuraByteCore/codexmate feat/task-orchestration-tab" --mode brief --source all --path-filter codexmate --match all --format text --limit 8
|
package/web-ui/app.js
CHANGED
|
@@ -154,6 +154,25 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
154
154
|
agentsContext: 'codex',
|
|
155
155
|
agentsModalTitle: 'AGENTS.md 编辑器',
|
|
156
156
|
agentsModalHint: '保存后会写入目标 AGENTS.md(与 config.toml 同级)。',
|
|
157
|
+
sysPromptScope: 'global',
|
|
158
|
+
sysPromptMode: 'append',
|
|
159
|
+
sysPromptContent: '',
|
|
160
|
+
sysPromptOriginalContent: '',
|
|
161
|
+
sysPromptPath: '',
|
|
162
|
+
sysPromptExists: false,
|
|
163
|
+
sysPromptHash: '',
|
|
164
|
+
sysPromptLoading: false,
|
|
165
|
+
sysPromptSaving: false,
|
|
166
|
+
sysPromptDiffVisible: false,
|
|
167
|
+
sysPromptDiffLoading: false,
|
|
168
|
+
sysPromptDiffError: '',
|
|
169
|
+
sysPromptDiffLines: [],
|
|
170
|
+
sysPromptDiffStats: { added: 0, removed: 0, unchanged: 0 },
|
|
171
|
+
sysPromptDiffTruncated: false,
|
|
172
|
+
sysPromptDiffHasChangesValue: false,
|
|
173
|
+
sysPromptDiffFingerprint: '',
|
|
174
|
+
_sysPromptDiffPreviewRequestToken: null,
|
|
175
|
+
_sysPromptOpenRequestToken: null,
|
|
157
176
|
skillsTargetApp: 'codex',
|
|
158
177
|
skillsRootPath: '',
|
|
159
178
|
skillsList: [],
|
|
@@ -773,7 +773,7 @@ export function createSessionComputed() {
|
|
|
773
773
|
const rows = currentKeys
|
|
774
774
|
.map((key) => byDay.get(key))
|
|
775
775
|
.filter(Boolean)
|
|
776
|
-
.sort((a, b) =>
|
|
776
|
+
.sort((a, b) => a.key.localeCompare(b.key, 'en-US'));
|
|
777
777
|
const rowsWithCompare = rows.map((row) => {
|
|
778
778
|
if (!compareEnabled) {
|
|
779
779
|
return { ...row, compareEnabled: false, prevKey: '', prevTokenTotal: 0 };
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
isAgentsDiffPreviewPayloadTooLarge,
|
|
5
5
|
shouldApplyAgentsDiffPreviewResponse
|
|
6
6
|
} from '../logic.mjs';
|
|
7
|
+
import { issueLatestRequestToken, isLatestRequestToken } from './request-token.mjs';
|
|
7
8
|
|
|
8
9
|
function isValidOpenclawWorkspaceFileName(fileName) {
|
|
9
10
|
if (typeof fileName !== 'string') {
|
|
@@ -19,16 +20,6 @@ function isValidOpenclawWorkspaceFileName(fileName) {
|
|
|
19
20
|
return true;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
function issueLatestRequestToken(context, key) {
|
|
23
|
-
const token = (Number(context[key]) || 0) + 1;
|
|
24
|
-
context[key] = token;
|
|
25
|
-
return token;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function isLatestRequestToken(context, key, token) {
|
|
29
|
-
return !!context && context[key] === token;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
23
|
export function createAgentsMethods(options = {}) {
|
|
33
24
|
const {
|
|
34
25
|
api,
|
|
@@ -894,15 +885,17 @@ export function createAgentsMethods(options = {}) {
|
|
|
894
885
|
this.showMessage(this.t('prompts.presets.toast.deleted'), 'success');
|
|
895
886
|
},
|
|
896
887
|
switchPromptsSubTab(subTab) {
|
|
897
|
-
const normalized = subTab === 'claude-project' ? subTab : 'codex';
|
|
888
|
+
const normalized = subTab === 'claude-project' || subTab === 'system' ? subTab : 'codex';
|
|
898
889
|
if (normalized === 'claude-project' && !this.projectPathOptions.length && !this.projectPathOptionsLoading) {
|
|
899
890
|
this.loadProjectPathOptions();
|
|
900
891
|
}
|
|
901
892
|
if (this.promptsSubTab === normalized) {
|
|
902
|
-
this.
|
|
893
|
+
if (normalized === 'system') this.loadSystemPrompt();
|
|
894
|
+
else this.loadPromptsContent();
|
|
903
895
|
return;
|
|
904
896
|
}
|
|
905
897
|
this.promptsSubTab = normalized;
|
|
898
|
+
if (normalized === 'system') this.loadSystemPrompt();
|
|
906
899
|
this.$nextTick(() => {
|
|
907
900
|
document.querySelector('.main-panel')?.scrollTo({ top: 0, left: 0, behavior: 'auto' });
|
|
908
901
|
});
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
SESSION_TRASH_PAGE_SIZE
|
|
13
13
|
} from './app.constants.mjs';
|
|
14
14
|
import { createAgentsMethods } from './app.methods.agents.mjs';
|
|
15
|
+
import { createSystemPromptMethods } from './app.methods.system-prompt.mjs';
|
|
15
16
|
import { createClaudeConfigMethods } from './app.methods.claude-config.mjs';
|
|
16
17
|
import { createCodexConfigMethods } from './app.methods.codex-config.mjs';
|
|
17
18
|
import { createInstallMethods } from './app.methods.install.mjs';
|
|
@@ -85,6 +86,7 @@ export function createAppMethods() {
|
|
|
85
86
|
...createSkillsMethods({ api }),
|
|
86
87
|
...createPluginsMethods(),
|
|
87
88
|
...createAgentsMethods({ api, apiWithMeta }),
|
|
89
|
+
...createSystemPromptMethods({ api, apiWithMeta }),
|
|
88
90
|
...createProvidersMethods({ api }),
|
|
89
91
|
...createProviderCacheMethods({ api }),
|
|
90
92
|
...createWebUiPreferencesMethods({ api }),
|
|
@@ -308,10 +308,37 @@ export function createSessionActionMethods(options = {}) {
|
|
|
308
308
|
|
|
309
309
|
setSessionTrashEnabled(value) {
|
|
310
310
|
const enabled = this.normalizeSessionTrashEnabled(value);
|
|
311
|
+
const changed = this.sessionTrashEnabled !== enabled;
|
|
311
312
|
this.sessionTrashEnabled = enabled;
|
|
312
313
|
if (typeof this.persistWebUiPreferences === 'function') {
|
|
313
314
|
this.persistWebUiPreferences({ sessionTrashEnabled: enabled });
|
|
314
315
|
}
|
|
316
|
+
// 关闭/开启回收站后立即反映当前状态,无需用户手动刷新
|
|
317
|
+
if (changed && typeof this.applySessionTrashEnabledChange === 'function') {
|
|
318
|
+
this.applySessionTrashEnabledChange(enabled);
|
|
319
|
+
}
|
|
320
|
+
},
|
|
321
|
+
|
|
322
|
+
applySessionTrashEnabledChange(enabled) {
|
|
323
|
+
if (typeof this.invalidateSessionTrashRequests === 'function') {
|
|
324
|
+
this.invalidateSessionTrashRequests();
|
|
325
|
+
}
|
|
326
|
+
if (enabled) {
|
|
327
|
+
// 重新开启:立即拉取最新回收站内容
|
|
328
|
+
if (typeof this.loadSessionTrash === 'function') {
|
|
329
|
+
void this.loadSessionTrash({ forceRefresh: true });
|
|
330
|
+
}
|
|
331
|
+
} else {
|
|
332
|
+
// 关闭回收站:即时清空已展示的列表与计数,避免残留陈旧数据需手动刷新
|
|
333
|
+
this.sessionTrashItems = [];
|
|
334
|
+
this.sessionTrashVisibleCount = 0;
|
|
335
|
+
this.sessionTrashTotalCount = 0;
|
|
336
|
+
this.sessionTrashCountLoadedOnce = false;
|
|
337
|
+
this.sessionTrashLoadedOnce = false;
|
|
338
|
+
this.sessionTrashLastLoadFailed = false;
|
|
339
|
+
this.sessionTrashRestoring = {};
|
|
340
|
+
this.sessionTrashPurging = {};
|
|
341
|
+
}
|
|
315
342
|
},
|
|
316
343
|
|
|
317
344
|
setSessionTimelineStyle(style) {
|
|
@@ -783,6 +783,7 @@ export function createSessionBrowserMethods(options = {}) {
|
|
|
783
783
|
this.sessionsUsageCompareEnabled = false;
|
|
784
784
|
}
|
|
785
785
|
void this.loadSessionsUsage({ range });
|
|
786
|
+
this.scrollSessionsUsageDayIntoView({ behavior: 'auto' });
|
|
786
787
|
},
|
|
787
788
|
|
|
788
789
|
toggleSessionsUsageCompare() {
|
|
@@ -801,12 +802,29 @@ export function createSessionBrowserMethods(options = {}) {
|
|
|
801
802
|
selectSessionsUsageDay(dayKey) {
|
|
802
803
|
const normalized = typeof dayKey === 'string' ? dayKey.trim() : '';
|
|
803
804
|
this.sessionsUsageSelectedDayKey = normalized;
|
|
805
|
+
this.scrollSessionsUsageDayIntoView({ behavior: 'smooth', align: 'nearest' });
|
|
804
806
|
},
|
|
805
807
|
|
|
806
808
|
clearSessionsUsageDay() {
|
|
807
809
|
this.sessionsUsageSelectedDayKey = '';
|
|
808
810
|
},
|
|
809
811
|
|
|
812
|
+
scrollSessionsUsageDayIntoView(options = {}) {
|
|
813
|
+
if (typeof document === 'undefined' || !document) return;
|
|
814
|
+
const behavior = options.behavior === 'smooth' ? 'smooth' : 'auto';
|
|
815
|
+
const inline = options.align === 'nearest' ? 'nearest' : 'end';
|
|
816
|
+
const scrollFn = () => {
|
|
817
|
+
const node = document.querySelector('.usage-wave-label.active');
|
|
818
|
+
if (!node || typeof node.scrollIntoView !== 'function') return;
|
|
819
|
+
node.scrollIntoView({ inline, block: 'nearest', behavior });
|
|
820
|
+
};
|
|
821
|
+
if (typeof this.$nextTick === 'function') {
|
|
822
|
+
this.$nextTick(scrollFn);
|
|
823
|
+
} else {
|
|
824
|
+
scrollFn();
|
|
825
|
+
}
|
|
826
|
+
},
|
|
827
|
+
|
|
810
828
|
async loadSessionsUsage(options = {}) {
|
|
811
829
|
if (this.sessionsUsageLoading) return;
|
|
812
830
|
const normalizedRange = typeof options.range === 'string'
|
|
@@ -855,7 +873,9 @@ export function createSessionBrowserMethods(options = {}) {
|
|
|
855
873
|
this.sessionsUsageLoadedLimit = limit;
|
|
856
874
|
this.sessionsUsageLastLoadedRange = range;
|
|
857
875
|
if (!this.sessionsUsageSelectedDayKey && Array.isArray(this.sessionUsageDailyTableRows) && this.sessionUsageDailyTableRows.length > 0) {
|
|
858
|
-
|
|
876
|
+
const dayKeys = this.sessionUsageDailyTableRows.map((row) => row.key).filter(Boolean).sort((a, b) => b.localeCompare(a, 'en-US'));
|
|
877
|
+
this.sessionsUsageSelectedDayKey = dayKeys[0] || this.sessionUsageDailyTableRows[this.sessionUsageDailyTableRows.length - 1].key;
|
|
878
|
+
this.scrollSessionsUsageDayIntoView({ behavior: 'auto' });
|
|
859
879
|
}
|
|
860
880
|
}
|
|
861
881
|
}
|
|
@@ -89,6 +89,11 @@ export function createSessionTrashMethods(options = {}) {
|
|
|
89
89
|
},
|
|
90
90
|
|
|
91
91
|
getSessionTrashViewState() {
|
|
92
|
+
const trashEnabled = this.sessionTrashEnabled !== false;
|
|
93
|
+
// 回收站已关闭:即便残留旧列表也只展示已禁用态,不展示陈旧内容
|
|
94
|
+
if (!trashEnabled) {
|
|
95
|
+
return 'disabled';
|
|
96
|
+
}
|
|
92
97
|
if (this.sessionTrashLoading && !this.sessionTrashLoadedOnce) {
|
|
93
98
|
return 'loading';
|
|
94
99
|
}
|