@goodandready/dsh-goal 0.2.0 → 0.2.2
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/lib/card-form-state.js +65 -21
- package/lib/client.js +2306 -109
- package/lib/command-handler.js +276 -241
- package/lib/goal-engine.js +1033 -3
- package/lib/index.js +984 -4
- package/package.json +63 -63
- package/docs/design/DESIGN.md +0 -184
package/lib/command-handler.js
CHANGED
|
@@ -1,241 +1,276 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { GoalState, detectLanguage } from './goal-engine.js';
|
|
3
|
-
|
|
4
|
-
export const
|
|
5
|
-
export const
|
|
6
|
-
export const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
*
|
|
11
|
-
* @
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (lower === '
|
|
22
|
-
if (lower === '
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
*
|
|
30
|
-
* @
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
*
|
|
44
|
-
* @param {string}
|
|
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
|
-
if (
|
|
156
|
-
|
|
157
|
-
kind: '
|
|
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
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (
|
|
187
|
-
|
|
188
|
-
kind: '
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
:
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
}
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { GoalState, detectLanguage } from './goal-engine.js';
|
|
3
|
+
|
|
4
|
+
export const USAGE_EN = 'Usage: /goal [<goal>|clear|pause|resume]';
|
|
5
|
+
export const USAGE_ZH = '用法: /goal [<目标描述>|clear|pause|resume]';
|
|
6
|
+
export const USAGE_RU = 'Использование: /goal [<цель>|clear|pause|resume]';
|
|
7
|
+
export const USAGE = USAGE_EN;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Парсинг строки ввода для команды /goal
|
|
11
|
+
* @param {string} rawInput
|
|
12
|
+
* @returns {{ action: string, text?: string }}
|
|
13
|
+
*/
|
|
14
|
+
export function parseGoalInput(rawInput = '') {
|
|
15
|
+
const input = String(rawInput || '').trim();
|
|
16
|
+
if (!input) {
|
|
17
|
+
return { action: 'show' };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const lower = input.toLowerCase();
|
|
21
|
+
if (lower === 'clear') return { action: 'clear' };
|
|
22
|
+
if (lower === 'pause') return { action: 'pause' };
|
|
23
|
+
if (lower === 'resume') return { action: 'resume' };
|
|
24
|
+
|
|
25
|
+
return { action: 'start', text: input };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Создание валидного identified сообщения пользователя для отправки агенту через followup
|
|
30
|
+
* @param {string} text
|
|
31
|
+
* @returns {Object}
|
|
32
|
+
*/
|
|
33
|
+
export function createGoalUserMessage(text) {
|
|
34
|
+
return {
|
|
35
|
+
id: randomUUID(),
|
|
36
|
+
role: 'user',
|
|
37
|
+
content: [{ type: 'text', text }],
|
|
38
|
+
source: { kind: 'user' },
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Формирование стартового промпта с жестким требованием составить план работ
|
|
44
|
+
* @param {string} goalText
|
|
45
|
+
* @param {string} [explicitLang]
|
|
46
|
+
* @returns {string}
|
|
47
|
+
*/
|
|
48
|
+
export function formatGoalStartPrompt(goalText, explicitLang) {
|
|
49
|
+
const lang = explicitLang || detectLanguage(goalText);
|
|
50
|
+
if (lang === 'zh') {
|
|
51
|
+
return `🎯 目标模式已激活 (Goal Mode): "${goalText}"\n\n` +
|
|
52
|
+
`自主执行契约:\n` +
|
|
53
|
+
`1. 第一步必选动作:在开展任何工作或回复之前,你的第一个动作必须是调用 goal_set_milestones 工具,提交包含 3-7 个具体连续步骤的工作计划。用户将在界面中实时查看此计划。\n` +
|
|
54
|
+
`2. 第二步:计划确立后开始执行任务。在开始每个步骤前,通过 goal_update_progress(milestone_id, "in_progress") 标记其为进行中。\n` +
|
|
55
|
+
`3. 第三步:完成某一步骤后,通过 goal_update_progress(milestone_id, "completed", "阶段简要成果") 标记为已完成。\n` +
|
|
56
|
+
`4. 第四步:当所有里程碑全部完成后,调用 goal_finish(summary) 工具并附带详尽的最终成果总结。`;
|
|
57
|
+
}
|
|
58
|
+
if (lang === 'ru') {
|
|
59
|
+
return `🎯 Активирован режим цели (Goal Mode): "${goalText}"\n\n` +
|
|
60
|
+
`СТРОГИЙ КОНТРАКТ АВТОНОМНОГО РЕЖИМА:\n` +
|
|
61
|
+
`1. ОБЯЗАТЕЛЬНЫЙ ШАГ №1: Твоим ПЕРВЫМ действием ДО выполнения любой другой работы или ответа должен быть вызов инструмента goal_set_milestones с массивом из 3-7 конкретных, последовательных пунктов плана работ. Пользователь видит этот план в интерфейсе в реальном времени.\n` +
|
|
62
|
+
`2. ШАГ №2: После установки плана переходи к выполнению шагов. Перед началом работы над каждым шагом отметь его статус как in_progress через goal_update_progress(milestone_id, "in_progress").\n` +
|
|
63
|
+
`3. ШАГ №3: После успешного завершения шага отметь его статус как completed через goal_update_progress(milestone_id, "completed", "краткие итоги шага").\n` +
|
|
64
|
+
`4. ШАГ №4: Когда все пункты плана выполнены, вызови инструмент goal_finish(summary) с подробным резюме достигнутых результатов.`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return `🎯 Goal Mode activated: "${goalText}"\n\n` +
|
|
68
|
+
`STRICT AUTONOMOUS CONTRACT:\n` +
|
|
69
|
+
`1. MANDATORY STEP 1: Your FIRST action BEFORE doing any other work or reply MUST be calling tool goal_set_milestones with an array of 3-7 concrete, sequential milestones. The user sees this plan in real time.\n` +
|
|
70
|
+
`2. STEP 2: After establishing the plan, proceed to execute milestones. Before starting each milestone, mark it as in_progress via goal_update_progress(milestone_id, "in_progress").\n` +
|
|
71
|
+
`3. STEP 3: Upon completing a milestone, mark it as completed via goal_update_progress(milestone_id, "completed", "brief milestone outcome").\n` +
|
|
72
|
+
`4. STEP 4: When all milestones are completed, call tool goal_finish(summary) with a comprehensive summary of achieved results.`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Форматирование текстового ответа для UI и постановка/управление задачей агента
|
|
77
|
+
* @param {import('./goal-engine.js').GoalEngine} engine
|
|
78
|
+
* @param {{ action: string, text?: string }} parsed
|
|
79
|
+
* @param {Object} [config]
|
|
80
|
+
* @param {Object} [agent]
|
|
81
|
+
* @param {string} [sessionId='default']
|
|
82
|
+
* @returns {{ kind: 'success' | 'error', text: string }}
|
|
83
|
+
*/
|
|
84
|
+
export function executeGoalSlashCommand(engine, parsed, config = {}, agent = null, sessionId = 'default') {
|
|
85
|
+
const sid = sessionId || 'default';
|
|
86
|
+
const snap = engine.getSnapshot(sid);
|
|
87
|
+
|
|
88
|
+
const lang = (parsed.text ? detectLanguage(parsed.text) : (snap.lang || (snap.title ? detectLanguage(snap.title) : 'en'))) || 'en';
|
|
89
|
+
const isZh = lang === 'zh';
|
|
90
|
+
const isRu = lang === 'ru';
|
|
91
|
+
const usage = isZh ? USAGE_ZH : (isRu ? USAGE_RU : USAGE_EN);
|
|
92
|
+
|
|
93
|
+
switch (parsed.action) {
|
|
94
|
+
case 'show': {
|
|
95
|
+
if (!snap.hasActiveGoal) {
|
|
96
|
+
return {
|
|
97
|
+
kind: 'success',
|
|
98
|
+
text: isZh
|
|
99
|
+
? `🎯 目标模式 (Goal Mode): 当前未设置目标。\n${usage}`
|
|
100
|
+
: (isRu
|
|
101
|
+
? `🎯 Режим цели (Goal Mode): цель не установлена.\n${usage}`
|
|
102
|
+
: `🎯 Goal Mode: no active goal set.\n${usage}`),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const milestonesInfo = snap.milestones.length > 0
|
|
107
|
+
? (isZh ? `\n工作计划:\n` : (isRu ? `\nПлан работ:\n` : `\nWork plan:\n`)) + snap.milestones.map((m, i) => ` ${i + 1}. [${m.status}] ${m.title}`).join('\n')
|
|
108
|
+
: '';
|
|
109
|
+
|
|
110
|
+
const etaText = snap.formattedETA ? ` (ETA: ${snap.formattedETA})` : '';
|
|
111
|
+
const tokensInfo = (snap.tokensUsage && snap.tokensUsage.totalTokens > 0)
|
|
112
|
+
? (isZh
|
|
113
|
+
? `\nToken: ${snap.tokensUsage.totalTokens.toLocaleString()} (输入: ${snap.tokensUsage.promptTokens.toLocaleString()}, 输出: ${snap.tokensUsage.completionTokens.toLocaleString()})`
|
|
114
|
+
: (isRu
|
|
115
|
+
? `\nТокены: ${snap.tokensUsage.totalTokens.toLocaleString()} (in: ${snap.tokensUsage.promptTokens.toLocaleString()}, out: ${snap.tokensUsage.completionTokens.toLocaleString()})`
|
|
116
|
+
: `\nTokens: ${snap.tokensUsage.totalTokens.toLocaleString()} (in: ${snap.tokensUsage.promptTokens.toLocaleString()}, out: ${snap.tokensUsage.completionTokens.toLocaleString()})`))
|
|
117
|
+
: '';
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
kind: 'success',
|
|
121
|
+
text: isZh
|
|
122
|
+
? `🎯 目标: "${snap.title}"\n` +
|
|
123
|
+
`状态: ${snap.state}\n` +
|
|
124
|
+
`耗时: ${snap.formattedElapsed}${etaText}\n` +
|
|
125
|
+
`迭代轮次: ${snap.iterationsCount}/${snap.maxIterations}` +
|
|
126
|
+
tokensInfo +
|
|
127
|
+
milestonesInfo +
|
|
128
|
+
`\n\n快捷指令: /goal pause, /goal resume, /goal clear, /goal <新目标>`
|
|
129
|
+
: (isRu
|
|
130
|
+
? `🎯 Цель: «${snap.title}»\n` +
|
|
131
|
+
`Статус: ${snap.state}\n` +
|
|
132
|
+
`Время: ${snap.formattedElapsed}${etaText}\n` +
|
|
133
|
+
`Итераций: ${snap.iterationsCount}/${snap.maxIterations}` +
|
|
134
|
+
tokensInfo +
|
|
135
|
+
milestonesInfo +
|
|
136
|
+
`\n\nКоманды: /goal pause, /goal resume, /goal clear, /goal <новая цель>`
|
|
137
|
+
: `🎯 Goal: "${snap.title}"\n` +
|
|
138
|
+
`Status: ${snap.state}\n` +
|
|
139
|
+
`Duration: ${snap.formattedElapsed}${etaText}\n` +
|
|
140
|
+
`Iterations: ${snap.iterationsCount}/${snap.maxIterations}` +
|
|
141
|
+
tokensInfo +
|
|
142
|
+
milestonesInfo +
|
|
143
|
+
`\n\nCommands: /goal pause, /goal resume, /goal clear, /goal <new goal>`),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
case 'clear': {
|
|
148
|
+
if (!snap.hasActiveGoal) {
|
|
149
|
+
return {
|
|
150
|
+
kind: 'success',
|
|
151
|
+
text: isZh ? '当前未设置目标。' : (isRu ? 'Цель не была установлена.' : 'No active goal set.'),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
engine.clear(sid);
|
|
155
|
+
if (agent && typeof agent.cancel === 'function') {
|
|
156
|
+
try {
|
|
157
|
+
agent.cancel({ kind: 'user', reason: 'Goal cleared by user' });
|
|
158
|
+
} catch (err) {
|
|
159
|
+
console.warn('[dsh-goal] Failed to cancel agent on clear:', err);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
kind: 'success',
|
|
164
|
+
text: isZh ? '🎯 目标已清除。' : (isRu ? '🎯 Цель сброшена.' : '🎯 Goal cleared.'),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
case 'pause': {
|
|
169
|
+
if (!snap.hasActiveGoal) {
|
|
170
|
+
return {
|
|
171
|
+
kind: 'error',
|
|
172
|
+
text: isZh
|
|
173
|
+
? `无法暂停:未设置活动目标。\n${usage}`
|
|
174
|
+
: (isRu
|
|
175
|
+
? `Нельзя приостановить: активная цель отсутствует.\n${usage}`
|
|
176
|
+
: `Cannot pause: no active goal.\n${usage}`),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
if (snap.state === GoalState.PAUSED) {
|
|
180
|
+
return {
|
|
181
|
+
kind: 'success',
|
|
182
|
+
text: isZh ? '⏸ 目标当前已处于暂停状态。' : (isRu ? '⏸ Цель уже на паузе.' : '⏸ Goal is already paused.'),
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
engine.pause(isZh ? '用户命令暂停 (/goal pause)' : (isRu ? 'Пауза по команде пользователя (/goal pause)' : 'Paused by user command (/goal pause)'), sid);
|
|
186
|
+
if (agent && typeof agent.cancel === 'function') {
|
|
187
|
+
try {
|
|
188
|
+
agent.cancel({ kind: 'user', reason: 'Goal paused by user' });
|
|
189
|
+
} catch (err) {
|
|
190
|
+
console.warn('[dsh-goal] Failed to cancel agent on pause:', err);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
kind: 'success',
|
|
195
|
+
text: isZh
|
|
196
|
+
? `⏸ 目标已暂停: "${snap.title}"。智能体已停止,使用 /goal resume 或 ▶️ 按钮继续。`
|
|
197
|
+
: (isRu
|
|
198
|
+
? `⏸ Цель приостановлена: «${snap.title}». Работа агента остановлена. Используйте /goal resume или кнопку ▶️ для продолжения.`
|
|
199
|
+
: `⏸ Goal paused: "${snap.title}". Agent stopped. Use /goal resume or ▶️ button to continue.`),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
case 'resume': {
|
|
204
|
+
if (!snap.hasActiveGoal) {
|
|
205
|
+
return {
|
|
206
|
+
kind: 'error',
|
|
207
|
+
text: isZh
|
|
208
|
+
? `无法恢复:未设置活动目标。\n${usage}`
|
|
209
|
+
: (isRu
|
|
210
|
+
? `Нельзя возобновить: активная цель отсутствует.\n${usage}`
|
|
211
|
+
: `Cannot resume: no active goal.\n${usage}`),
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
if (snap.state === GoalState.RUNNING) {
|
|
215
|
+
return {
|
|
216
|
+
kind: 'success',
|
|
217
|
+
text: isZh ? '▶️ 目标已在运行中。' : (isRu ? '▶️ Цель уже выполняется.' : '▶️ Goal is already running.'),
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
engine.resume(sid);
|
|
221
|
+
if (agent && typeof agent.followup === 'function') {
|
|
222
|
+
try {
|
|
223
|
+
const resumePrompt = isZh
|
|
224
|
+
? `▶️ 用户已恢复目标运行。请继续推进工作计划中尚未完成的步骤。通过 goal_update_progress 实时更新进度。`
|
|
225
|
+
: (isRu
|
|
226
|
+
? `▶️ Цель возобновлена пользователем. Продолжай выполнение плана работ с того места, где остановился. Отмечай шаги через goal_update_progress.`
|
|
227
|
+
: `▶️ Goal resumed by user. Continue executing the work plan from where you stopped. Update steps via goal_update_progress.`);
|
|
228
|
+
agent.followup(createGoalUserMessage(resumePrompt));
|
|
229
|
+
} catch (err) {
|
|
230
|
+
console.warn('[dsh-goal] Failed to followup agent on resume:', err);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return {
|
|
234
|
+
kind: 'success',
|
|
235
|
+
text: isZh
|
|
236
|
+
? `▶️ 目标已恢复: "${snap.title}"。智能体继续工作中。`
|
|
237
|
+
: (isRu
|
|
238
|
+
? `▶️ Цель возобновлена: «${snap.title}». Агент продолжает работу.`
|
|
239
|
+
: `▶️ Goal resumed: "${snap.title}". Agent continues working.`),
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
case 'start': {
|
|
244
|
+
const maxIterations = config.maxIterations ?? snap.maxIterations ?? 25;
|
|
245
|
+
const cwd = config.cwd;
|
|
246
|
+
const newSnap = engine.startGoal(parsed.text, { maxIterations, lang, cwd }, sid);
|
|
247
|
+
|
|
248
|
+
if (agent && typeof agent.followup === 'function') {
|
|
249
|
+
try {
|
|
250
|
+
const userMsg = createGoalUserMessage(formatGoalStartPrompt(parsed.text, lang));
|
|
251
|
+
agent.followup(userMsg);
|
|
252
|
+
} catch (err) {
|
|
253
|
+
console.warn('[dsh-goal] Failed to dispatch goal to agent followup:', err);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
kind: 'success',
|
|
259
|
+
text: isZh
|
|
260
|
+
? `🎯 目标已激活: "${newSnap.title}" (上限 ${newSnap.maxIterations} 轮迭代)。\n` +
|
|
261
|
+
`暂停: /goal pause | 清除: /goal clear`
|
|
262
|
+
: (isRu
|
|
263
|
+
? `🎯 Активирована цель: «${newSnap.title}» (макс. ${newSnap.maxIterations} итераций).\n` +
|
|
264
|
+
`Для паузы: /goal pause | Для сброса: /goal clear`
|
|
265
|
+
: `🎯 Goal activated: "${newSnap.title}" (max ${newSnap.maxIterations} iterations).\n` +
|
|
266
|
+
`To pause: /goal pause | To clear: /goal clear`),
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
default:
|
|
271
|
+
return {
|
|
272
|
+
kind: 'error',
|
|
273
|
+
text: isZh ? `未知指令。${usage}` : (isRu ? `Неизвестная команда. ${usage}` : `Unknown command. ${usage}`),
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
}
|