chatccc 0.2.122 → 0.2.124
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 +14 -22
- package/package.json +63 -63
- package/src/__tests__/claude-adapter.test.ts +441 -441
- package/src/__tests__/orchestrator.test.ts +1 -43
- package/src/adapters/adapter-interface.ts +0 -7
- package/src/adapters/claude-adapter.ts +2 -6
- package/src/adapters/cursor-adapter.ts +1 -11
- package/src/adapters/resource-monitor.ts +140 -140
- package/src/cards.ts +0 -2
- package/src/config.ts +0 -18
- package/src/orchestrator.ts +15 -214
- package/src/session.ts +1 -4
|
@@ -169,48 +169,6 @@ describe("handleCommand WeChat processing ack", () => {
|
|
|
169
169
|
expect(platform.sendText).toHaveBeenCalledWith("wx-chat", "生成中...");
|
|
170
170
|
});
|
|
171
171
|
|
|
172
|
-
it.each([
|
|
173
|
-
{ tool: "claude" as const, sessionId: "sid-claude", text: "/ask 只回答不要改文件" },
|
|
174
|
-
{ tool: "cursor" as const, sessionId: "sid-cursor", text: "/plan 先给我计划" },
|
|
175
|
-
])("passes unsupported mode commands through as raw prompts for $tool", async ({ tool, sessionId, text }) => {
|
|
176
|
-
const platform = mockPlatform();
|
|
177
|
-
const prompt = vi.fn(async function* (_sessionId: string, userText: string) {
|
|
178
|
-
yield {
|
|
179
|
-
type: "assistant" as const,
|
|
180
|
-
blocks: [{ type: "text" as const, text: `收到: ${userText}` }],
|
|
181
|
-
};
|
|
182
|
-
});
|
|
183
|
-
_setAdapterForToolForTest(tool, {
|
|
184
|
-
displayName: tool,
|
|
185
|
-
sessionDescPrefix: `${tool} Session:`,
|
|
186
|
-
createSession: vi.fn(async () => ({ sessionId })),
|
|
187
|
-
prompt,
|
|
188
|
-
getSessionInfo: async (sessionId: string): Promise<SessionInfo> => ({
|
|
189
|
-
sessionId,
|
|
190
|
-
cwd: "F:\\repo",
|
|
191
|
-
}),
|
|
192
|
-
closeSession: async () => {},
|
|
193
|
-
});
|
|
194
|
-
await recordSessionRegistry({
|
|
195
|
-
chatId: "wx-chat",
|
|
196
|
-
sessionId,
|
|
197
|
-
tool,
|
|
198
|
-
chatName: "ready-session",
|
|
199
|
-
running: false,
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
await handleCommand(platform, text, "wx-chat", "wx-user", Date.now(), "p2p");
|
|
203
|
-
|
|
204
|
-
expect(platform.sendText).not.toHaveBeenCalledWith("wx-chat", expect.stringContaining("仅在以下 Agent 会话中可用"));
|
|
205
|
-
expect(prompt).toHaveBeenCalledWith(
|
|
206
|
-
sessionId,
|
|
207
|
-
expect.stringContaining(text),
|
|
208
|
-
"F:\\repo",
|
|
209
|
-
expect.any(AbortSignal),
|
|
210
|
-
expect.objectContaining({ permissionMode: undefined }),
|
|
211
|
-
);
|
|
212
|
-
});
|
|
213
|
-
|
|
214
172
|
it("cleans stale Feishu p2p binding, creates a group, and sends the private message as first prompt", async () => {
|
|
215
173
|
const platform = mockPlatform("feishu");
|
|
216
174
|
const prompt = vi.fn(async function* (_sessionId: string, userText: string) {
|
|
@@ -219,7 +177,7 @@ describe("handleCommand WeChat processing ack", () => {
|
|
|
219
177
|
blocks: [{ type: "text" as const, text: `收到: ${userText}` }],
|
|
220
178
|
};
|
|
221
179
|
});
|
|
222
|
-
_setAdapterForToolForTest("
|
|
180
|
+
_setAdapterForToolForTest("cursor", {
|
|
223
181
|
displayName: "Claude",
|
|
224
182
|
sessionDescPrefix: "Claude Session:",
|
|
225
183
|
createSession: vi.fn(async () => ({ sessionId: "sid-feishu-new" })),
|
|
@@ -128,13 +128,6 @@ export interface ToolPromptOptions {
|
|
|
128
128
|
onProcessStart?: (info: ToolProcessInfo) => void;
|
|
129
129
|
/** Called when the adapter leaves the prompt process scope normally or by abort. */
|
|
130
130
|
onProcessExit?: (info: ToolProcessInfo) => void;
|
|
131
|
-
/**
|
|
132
|
-
* 权限模式覆盖:
|
|
133
|
-
* - "plan": Claude Code --permission-mode plan(只读/规划)
|
|
134
|
-
* - "ask": Cursor Agent --mode ask(只读/问答)
|
|
135
|
-
* 不传或为 undefined 时使用适配器默认行为(正常编辑模式)。
|
|
136
|
-
*/
|
|
137
|
-
permissionMode?: "plan" | "ask";
|
|
138
131
|
}
|
|
139
132
|
|
|
140
133
|
// ---------------------------------------------------------------------------
|
|
@@ -274,17 +274,14 @@ function buildCliArgs(
|
|
|
274
274
|
isEmpty: (value: string) => boolean,
|
|
275
275
|
mcpConfigJson: string | null,
|
|
276
276
|
extraArgs: string[],
|
|
277
|
-
permissionMode?: "plan" | "ask",
|
|
278
277
|
): string[] {
|
|
279
|
-
const permMode = permissionMode === "plan" ? "plan" : permissionMode === "ask" ? "default" : "bypassPermissions";
|
|
280
|
-
const skipPermissions = permissionMode !== "plan" && permissionMode !== "ask";
|
|
281
278
|
const args = [
|
|
282
279
|
"-p",
|
|
283
280
|
"--output-format", "stream-json",
|
|
284
281
|
"--verbose",
|
|
285
282
|
"--setting-sources", "user,project,local",
|
|
286
|
-
"--permission-mode",
|
|
287
|
-
|
|
283
|
+
"--permission-mode", "bypassPermissions",
|
|
284
|
+
"--dangerously-skip-permissions",
|
|
288
285
|
"--settings", "{\"maxTurns\":0}",
|
|
289
286
|
];
|
|
290
287
|
|
|
@@ -435,7 +432,6 @@ class ClaudeAdapter implements ToolAdapter {
|
|
|
435
432
|
const args = buildCliArgs(
|
|
436
433
|
this.model, this.effort, this.isEmpty, mcpConfigJson,
|
|
437
434
|
["--resume", sessionId, "--input-format", "stream-json", "--replay-user-messages"],
|
|
438
|
-
options?.permissionMode,
|
|
439
435
|
);
|
|
440
436
|
|
|
441
437
|
const proc = spawnCli(args, cwd, env, true);
|
|
@@ -261,18 +261,8 @@ function spawnAgent(
|
|
|
261
261
|
cwd?: string,
|
|
262
262
|
stdinText?: string,
|
|
263
263
|
modelOverride?: string,
|
|
264
|
-
permissionMode?: "plan" | "ask",
|
|
265
264
|
): ChildProcess {
|
|
266
265
|
let allArgs = [...CURSOR_AGENT_ARGS, ...extraArgs];
|
|
267
|
-
if (permissionMode === "ask") {
|
|
268
|
-
// 替换已有的 --mode 或追加
|
|
269
|
-
const modeIdx = allArgs.findIndex((a, i) => a === "--mode" && i + 1 < allArgs.length);
|
|
270
|
-
if (modeIdx >= 0) {
|
|
271
|
-
allArgs[modeIdx + 1] = "ask";
|
|
272
|
-
} else {
|
|
273
|
-
allArgs.push("--mode", "ask");
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
266
|
if (modelOverride) {
|
|
277
267
|
// 替换全局 --model 为 per-session override
|
|
278
268
|
const modelIdx = allArgs.findIndex((a, i) => a === "--model" && i + 1 < allArgs.length);
|
|
@@ -380,7 +370,7 @@ class CursorAdapter implements ToolAdapter {
|
|
|
380
370
|
options?: ToolPromptOptions,
|
|
381
371
|
): AsyncIterable<UnifiedStreamMessage> {
|
|
382
372
|
console.log(`[Cursor debug] prompt start: sessionId=${sessionId}, cwd=${cwd}, userTextLen=${userText.length}`);
|
|
383
|
-
const proc = spawnAgent(["--resume", sessionId], cwd, userText, this.modelOverride
|
|
373
|
+
const proc = spawnAgent(["--resume", sessionId], cwd, userText, this.modelOverride);
|
|
384
374
|
this.activeProcs.add(proc);
|
|
385
375
|
if (proc.pid !== undefined) options?.onProcessStart?.({ pid: proc.pid });
|
|
386
376
|
|
|
@@ -1,141 +1,141 @@
|
|
|
1
|
-
// =============================================================================
|
|
2
|
-
// resource-monitor.ts — CLI 进程资源监控(CPU + 内存)
|
|
3
|
-
// =============================================================================
|
|
4
|
-
// 对所有 chatccc 启动的 CLI 进程持续监控 CPU 和内存占用。
|
|
5
|
-
// 若连续 3 分钟两项指标均无变化,判定为僵死,发出 "stuck" 事件。
|
|
6
|
-
// =============================================================================
|
|
7
|
-
|
|
8
|
-
import { exec } from "node:child_process";
|
|
9
|
-
import { EventEmitter } from "node:events";
|
|
10
|
-
|
|
11
|
-
const CHECK_INTERVAL_MS = 30_000; // 30 秒检查一次
|
|
12
|
-
const STUCK_THRESHOLD = 6; // 连续 6 次无变化 = 3 分钟
|
|
13
|
-
|
|
14
|
-
interface ProcessSnapshot {
|
|
15
|
-
cpu: number;
|
|
16
|
-
memory: number;
|
|
17
|
-
unchangedCount: number;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
interface TrackedProcess {
|
|
21
|
-
pid: number;
|
|
22
|
-
sessionId: string;
|
|
23
|
-
snapshot: ProcessSnapshot;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export const resourceMonitor = new EventEmitter();
|
|
27
|
-
|
|
28
|
-
const tracked = new Map<number, TrackedProcess>();
|
|
29
|
-
|
|
30
|
-
let timer: ReturnType<typeof setInterval> | null = null;
|
|
31
|
-
|
|
32
|
-
function startIfNeeded(): void {
|
|
33
|
-
if (timer) return;
|
|
34
|
-
timer = setInterval(checkAll, CHECK_INTERVAL_MS);
|
|
35
|
-
timer.unref?.();
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function stopIfIdle(): void {
|
|
39
|
-
if (tracked.size > 0) return;
|
|
40
|
-
if (timer) { clearInterval(timer); timer = null; }
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function registerProcess(pid: number, sessionId: string): void {
|
|
44
|
-
tracked.set(pid, { pid, sessionId, snapshot: { cpu: -1, memory: -1, unchangedCount: 0 } });
|
|
45
|
-
startIfNeeded();
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function unregisterProcess(pid: number): void {
|
|
49
|
-
tracked.delete(pid);
|
|
50
|
-
stopIfIdle();
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// ---------------------------------------------------------------------------
|
|
54
|
-
// 批量查询进程指标(Windows PowerShell)
|
|
55
|
-
// ---------------------------------------------------------------------------
|
|
56
|
-
|
|
57
|
-
function execPowerShell(script: string, timeoutMs: number): Promise<string> {
|
|
58
|
-
return new Promise((resolve, reject) => {
|
|
59
|
-
exec(
|
|
60
|
-
`powershell -NoProfile -Command "${script}"`,
|
|
61
|
-
{ timeout: timeoutMs, windowsHide: true },
|
|
62
|
-
(err, stdout) => {
|
|
63
|
-
if (err) reject(err);
|
|
64
|
-
else resolve(stdout);
|
|
65
|
-
},
|
|
66
|
-
);
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async function getProcessMetrics(pids: number[]): Promise<Map<number, { cpu: number; memory: number }>> {
|
|
71
|
-
const result = new Map<number, { cpu: number; memory: number }>();
|
|
72
|
-
if (pids.length === 0) return result;
|
|
73
|
-
|
|
74
|
-
const psScript = `Get-Process -Id ${pids.join(",")} -ErrorAction SilentlyContinue | ForEach-Object { "$($_.Id)|$($_.CPU)|$($_.WorkingSet64)" }`;
|
|
75
|
-
|
|
76
|
-
try {
|
|
77
|
-
const stdout = await execPowerShell(psScript, 10_000);
|
|
78
|
-
for (const line of stdout.trim().split(/\r?\n/)) {
|
|
79
|
-
const trimmed = line.trim();
|
|
80
|
-
if (!trimmed) continue;
|
|
81
|
-
const [idStr, cpuStr, memStr] = trimmed.split("|");
|
|
82
|
-
const id = parseInt(idStr, 10);
|
|
83
|
-
const cpu = parseFloat(cpuStr);
|
|
84
|
-
const memory = parseInt(memStr, 10);
|
|
85
|
-
if (!isNaN(id) && !isNaN(cpu) && !isNaN(memory)) {
|
|
86
|
-
result.set(id, { cpu, memory });
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
} catch {
|
|
90
|
-
// PowerShell 查询失败时跳过本轮,下次重试
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return result;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// ---------------------------------------------------------------------------
|
|
97
|
-
// 定时检查
|
|
98
|
-
// ---------------------------------------------------------------------------
|
|
99
|
-
|
|
100
|
-
async function checkAll(): Promise<void> {
|
|
101
|
-
if (tracked.size === 0) return;
|
|
102
|
-
|
|
103
|
-
const pids = [...tracked.keys()];
|
|
104
|
-
const metrics = await getProcessMetrics(pids);
|
|
105
|
-
|
|
106
|
-
for (const [pid, tp] of tracked) {
|
|
107
|
-
const m = metrics.get(pid);
|
|
108
|
-
if (!m) {
|
|
109
|
-
// 进程已不存在,停止追踪
|
|
110
|
-
tracked.delete(pid);
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const prev = tp.snapshot;
|
|
115
|
-
const cpuChanged = m.cpu !== prev.cpu;
|
|
116
|
-
// 内存允许 ±1% 波动,避免正常抖动触发误判
|
|
117
|
-
const memTolerance = prev.memory > 0 ? Math.max(prev.memory * 0.01, 1024 * 1024) : 1024 * 1024;
|
|
118
|
-
const memChanged = Math.abs(m.memory - prev.memory) > memTolerance;
|
|
119
|
-
|
|
120
|
-
if (!cpuChanged && !memChanged) {
|
|
121
|
-
tp.snapshot.unchangedCount++;
|
|
122
|
-
if (tp.snapshot.unchangedCount >= STUCK_THRESHOLD) {
|
|
123
|
-
const idleMinutes = Math.round(
|
|
124
|
-
(tp.snapshot.unchangedCount * CHECK_INTERVAL_MS) / 60_000,
|
|
125
|
-
);
|
|
126
|
-
resourceMonitor.emit("stuck", {
|
|
127
|
-
pid: tp.pid,
|
|
128
|
-
sessionId: tp.sessionId,
|
|
129
|
-
idleMinutes,
|
|
130
|
-
});
|
|
131
|
-
tracked.delete(pid);
|
|
132
|
-
}
|
|
133
|
-
} else {
|
|
134
|
-
tp.snapshot.unchangedCount = 0;
|
|
135
|
-
}
|
|
136
|
-
tp.snapshot.cpu = m.cpu;
|
|
137
|
-
tp.snapshot.memory = m.memory;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
stopIfIdle();
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// resource-monitor.ts — CLI 进程资源监控(CPU + 内存)
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// 对所有 chatccc 启动的 CLI 进程持续监控 CPU 和内存占用。
|
|
5
|
+
// 若连续 3 分钟两项指标均无变化,判定为僵死,发出 "stuck" 事件。
|
|
6
|
+
// =============================================================================
|
|
7
|
+
|
|
8
|
+
import { exec } from "node:child_process";
|
|
9
|
+
import { EventEmitter } from "node:events";
|
|
10
|
+
|
|
11
|
+
const CHECK_INTERVAL_MS = 30_000; // 30 秒检查一次
|
|
12
|
+
const STUCK_THRESHOLD = 6; // 连续 6 次无变化 = 3 分钟
|
|
13
|
+
|
|
14
|
+
interface ProcessSnapshot {
|
|
15
|
+
cpu: number;
|
|
16
|
+
memory: number;
|
|
17
|
+
unchangedCount: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface TrackedProcess {
|
|
21
|
+
pid: number;
|
|
22
|
+
sessionId: string;
|
|
23
|
+
snapshot: ProcessSnapshot;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const resourceMonitor = new EventEmitter();
|
|
27
|
+
|
|
28
|
+
const tracked = new Map<number, TrackedProcess>();
|
|
29
|
+
|
|
30
|
+
let timer: ReturnType<typeof setInterval> | null = null;
|
|
31
|
+
|
|
32
|
+
function startIfNeeded(): void {
|
|
33
|
+
if (timer) return;
|
|
34
|
+
timer = setInterval(checkAll, CHECK_INTERVAL_MS);
|
|
35
|
+
timer.unref?.();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function stopIfIdle(): void {
|
|
39
|
+
if (tracked.size > 0) return;
|
|
40
|
+
if (timer) { clearInterval(timer); timer = null; }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function registerProcess(pid: number, sessionId: string): void {
|
|
44
|
+
tracked.set(pid, { pid, sessionId, snapshot: { cpu: -1, memory: -1, unchangedCount: 0 } });
|
|
45
|
+
startIfNeeded();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function unregisterProcess(pid: number): void {
|
|
49
|
+
tracked.delete(pid);
|
|
50
|
+
stopIfIdle();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// 批量查询进程指标(Windows PowerShell)
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
function execPowerShell(script: string, timeoutMs: number): Promise<string> {
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
|
+
exec(
|
|
60
|
+
`powershell -NoProfile -Command "${script}"`,
|
|
61
|
+
{ timeout: timeoutMs, windowsHide: true },
|
|
62
|
+
(err, stdout) => {
|
|
63
|
+
if (err) reject(err);
|
|
64
|
+
else resolve(stdout);
|
|
65
|
+
},
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function getProcessMetrics(pids: number[]): Promise<Map<number, { cpu: number; memory: number }>> {
|
|
71
|
+
const result = new Map<number, { cpu: number; memory: number }>();
|
|
72
|
+
if (pids.length === 0) return result;
|
|
73
|
+
|
|
74
|
+
const psScript = `Get-Process -Id ${pids.join(",")} -ErrorAction SilentlyContinue | ForEach-Object { "$($_.Id)|$($_.CPU)|$($_.WorkingSet64)" }`;
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
const stdout = await execPowerShell(psScript, 10_000);
|
|
78
|
+
for (const line of stdout.trim().split(/\r?\n/)) {
|
|
79
|
+
const trimmed = line.trim();
|
|
80
|
+
if (!trimmed) continue;
|
|
81
|
+
const [idStr, cpuStr, memStr] = trimmed.split("|");
|
|
82
|
+
const id = parseInt(idStr, 10);
|
|
83
|
+
const cpu = parseFloat(cpuStr);
|
|
84
|
+
const memory = parseInt(memStr, 10);
|
|
85
|
+
if (!isNaN(id) && !isNaN(cpu) && !isNaN(memory)) {
|
|
86
|
+
result.set(id, { cpu, memory });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
} catch {
|
|
90
|
+
// PowerShell 查询失败时跳过本轮,下次重试
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
// 定时检查
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
|
|
100
|
+
async function checkAll(): Promise<void> {
|
|
101
|
+
if (tracked.size === 0) return;
|
|
102
|
+
|
|
103
|
+
const pids = [...tracked.keys()];
|
|
104
|
+
const metrics = await getProcessMetrics(pids);
|
|
105
|
+
|
|
106
|
+
for (const [pid, tp] of tracked) {
|
|
107
|
+
const m = metrics.get(pid);
|
|
108
|
+
if (!m) {
|
|
109
|
+
// 进程已不存在,停止追踪
|
|
110
|
+
tracked.delete(pid);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const prev = tp.snapshot;
|
|
115
|
+
const cpuChanged = m.cpu !== prev.cpu;
|
|
116
|
+
// 内存允许 ±1% 波动,避免正常抖动触发误判
|
|
117
|
+
const memTolerance = prev.memory > 0 ? Math.max(prev.memory * 0.01, 1024 * 1024) : 1024 * 1024;
|
|
118
|
+
const memChanged = Math.abs(m.memory - prev.memory) > memTolerance;
|
|
119
|
+
|
|
120
|
+
if (!cpuChanged && !memChanged) {
|
|
121
|
+
tp.snapshot.unchangedCount++;
|
|
122
|
+
if (tp.snapshot.unchangedCount >= STUCK_THRESHOLD) {
|
|
123
|
+
const idleMinutes = Math.round(
|
|
124
|
+
(tp.snapshot.unchangedCount * CHECK_INTERVAL_MS) / 60_000,
|
|
125
|
+
);
|
|
126
|
+
resourceMonitor.emit("stuck", {
|
|
127
|
+
pid: tp.pid,
|
|
128
|
+
sessionId: tp.sessionId,
|
|
129
|
+
idleMinutes,
|
|
130
|
+
});
|
|
131
|
+
tracked.delete(pid);
|
|
132
|
+
}
|
|
133
|
+
} else {
|
|
134
|
+
tp.snapshot.unchangedCount = 0;
|
|
135
|
+
}
|
|
136
|
+
tp.snapshot.cpu = m.cpu;
|
|
137
|
+
tp.snapshot.memory = m.memory;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
stopIfIdle();
|
|
141
141
|
}
|
package/src/cards.ts
CHANGED
|
@@ -131,8 +131,6 @@ export function buildHelpCard(
|
|
|
131
131
|
"发送 **/new cursor** 创建新 Cursor 会话",
|
|
132
132
|
"发送 **/new codex** 创建新 Codex 会话",
|
|
133
133
|
"发送 **/newh** 重置当前会话(沿用当前工作目录,不切换)",
|
|
134
|
-
"发送 **/plan <消息>** 在 Claude 会话中以只读规划模式提问",
|
|
135
|
-
"发送 **/ask <消息>** 在 Cursor 会话中以只读问答模式提问",
|
|
136
134
|
].join("\n");
|
|
137
135
|
return JSON.stringify({
|
|
138
136
|
config: { wide_screen_mode: true },
|
package/src/config.ts
CHANGED
|
@@ -122,24 +122,6 @@ export interface AppConfig {
|
|
|
122
122
|
export type AgentTool = "claude" | "cursor" | "codex";
|
|
123
123
|
export const AGENT_TOOLS: AgentTool[] = ["claude", "cursor", "codex"];
|
|
124
124
|
|
|
125
|
-
/**
|
|
126
|
-
* Agent 专属模式指令(内部配置,非用户可配)。
|
|
127
|
-
* 映射每个 Agent 支持的特殊指令及其对应的 CLI 权限模式。
|
|
128
|
-
*
|
|
129
|
-
* - claude: /plan → --permission-mode plan(只读/规划)
|
|
130
|
-
* - cursor: /ask → --mode ask(只读/问答)
|
|
131
|
-
* - codex: 暂无原生只读模式,不注册任何模式指令
|
|
132
|
-
*/
|
|
133
|
-
export const AGENT_MODE_COMMANDS: Record<AgentTool, { command: string; mode: "plan" | "ask"; description: string }[]> = {
|
|
134
|
-
claude: [
|
|
135
|
-
{ command: "/plan", mode: "plan", description: "以只读规划模式提问(Claude 专属)" },
|
|
136
|
-
],
|
|
137
|
-
cursor: [
|
|
138
|
-
{ command: "/ask", mode: "ask", description: "以只读问答模式提问(Cursor 专属)" },
|
|
139
|
-
],
|
|
140
|
-
codex: [],
|
|
141
|
-
};
|
|
142
|
-
|
|
143
125
|
/** 获取指定 agent 配置中所有模型相关的值(最多 100 个,去重) */
|
|
144
126
|
export function getAllModelsForTool(tool: AgentTool, cfg: AppConfig = config): string[] {
|
|
145
127
|
const seen = new Set<string>();
|