@goodandready/dsh-goal 0.1.3 → 0.1.4

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.
@@ -1,189 +1,192 @@
1
- import { randomUUID } from 'node:crypto';
2
- import { GoalState } from './goal-engine.js';
3
-
4
- export const USAGE = 'Использование: /goal [<цель>|clear|pause|resume]';
5
-
6
- /**
7
- * Парсинг строки ввода для команды /goal
8
- * @param {string} rawInput
9
- * @returns {{ action: string, text?: string }}
10
- */
11
- export function parseGoalInput(rawInput = '') {
12
- const input = String(rawInput || '').trim();
13
- if (!input) {
14
- return { action: 'show' };
15
- }
16
-
17
- const lower = input.toLowerCase();
18
- if (lower === 'clear') return { action: 'clear' };
19
- if (lower === 'pause') return { action: 'pause' };
20
- if (lower === 'resume') return { action: 'resume' };
21
-
22
- return { action: 'start', text: input };
23
- }
24
-
25
- /**
26
- * Создание валидного identified сообщения пользователя для отправки агенту через followup
27
- * @param {string} text
28
- * @returns {Object}
29
- */
30
- export function createGoalUserMessage(text) {
31
- return {
32
- id: randomUUID(),
33
- role: 'user',
34
- content: [{ type: 'text', text }],
35
- source: { kind: 'user' },
36
- };
37
- }
38
-
39
- /**
40
- * Формирование стартового промпта с жестким требованием составить план работ
41
- * @param {string} goalText
42
- * @returns {string}
43
- */
44
- export function formatGoalStartPrompt(goalText) {
45
- return `🎯 Активирован режим цели: "${goalText}"\n\n` +
46
- `ОБЯЗАТЕЛЬНЫЙ ПЕРВЫЙ ШАГ: Немедленно вызови инструмент goal_set_milestones и передай массив из 3-7 конкретных пунктов плана работ для достижения этой цели. Пользователь видит этот план в интерфейсе в реальном времени.\n` +
47
- `После формирования плана последовательно выполняй каждый пункт. Перед выполнением шага обновляй его статус на in_progress через goal_update_progress, а по завершении на completed.\n` +
48
- `Когда все пункты выполнены, вызови инструмент goal_finish.`;
49
- }
50
-
51
- /**
52
- * Форматирование текстового ответа для UI и постановка/управление задачей агента
53
- * @param {import('./goal-engine.js').GoalEngine} engine
54
- * @param {{ action: string, text?: string }} parsed
55
- * @param {Object} [config]
56
- * @param {Object} [agent]
57
- * @returns {{ kind: 'success' | 'error', text: string }}
58
- */
59
- export function executeGoalSlashCommand(engine, parsed, config = {}, agent = null) {
60
- const snap = engine.getSnapshot();
61
-
62
- switch (parsed.action) {
63
- case 'show': {
64
- if (!snap.hasActiveGoal) {
65
- return {
66
- kind: 'success',
67
- text: `🎯 Режим цели (Goal Mode): цель не установлена.\n${USAGE}`,
68
- };
69
- }
70
-
71
- const milestonesInfo = snap.milestones.length > 0
72
- ? `\nПлан работ:\n` + snap.milestones.map((m, i) => ` ${i + 1}. [${m.status}] ${m.title}`).join('\n')
73
- : '';
74
-
75
- return {
76
- kind: 'success',
77
- text: `🎯 Цель: «${snap.title}»\n` +
78
- `Статус: ${snap.state}\n` +
79
- `Время: ${snap.formattedElapsed}\n` +
80
- `Итераций: ${snap.iterationsCount}/${snap.maxIterations}` +
81
- milestonesInfo +
82
- `\n\nКоманды: /goal pause, /goal resume, /goal clear, /goal <новая цель>`,
83
- };
84
- }
85
-
86
- case 'clear': {
87
- if (!snap.hasActiveGoal) {
88
- return {
89
- kind: 'success',
90
- text: 'Цель не была установлена.',
91
- };
92
- }
93
- engine.clear();
94
- if (agent && typeof agent.cancel === 'function') {
95
- try {
96
- agent.cancel({ kind: 'user', reason: 'Goal cleared by user' });
97
- } catch (err) {
98
- console.warn('[dsh-goal] Failed to cancel agent on clear:', err);
99
- }
100
- }
101
- return {
102
- kind: 'success',
103
- text: '🎯 Цель сброшена.',
104
- };
105
- }
106
-
107
- case 'pause': {
108
- if (!snap.hasActiveGoal) {
109
- return {
110
- kind: 'error',
111
- text: `Нельзя приостановить: активная цель отсутствует.\n${USAGE}`,
112
- };
113
- }
114
- if (snap.state === GoalState.PAUSED) {
115
- return {
116
- kind: 'success',
117
- text: '⏸ Цель уже на паузе.',
118
- };
119
- }
120
- engine.pause('Пауза по команде пользователя (/goal pause)');
121
- if (agent && typeof agent.cancel === 'function') {
122
- try {
123
- agent.cancel({ kind: 'user', reason: 'Goal paused by user' });
124
- } catch (err) {
125
- console.warn('[dsh-goal] Failed to cancel agent on pause:', err);
126
- }
127
- }
128
- return {
129
- kind: 'success',
130
- text: `⏸ Цель приостановлена: «${snap.title}». Работа агента остановлена. Используйте /goal resume или кнопку ▶️ для продолжения.`,
131
- };
132
- }
133
-
134
- case 'resume': {
135
- if (!snap.hasActiveGoal) {
136
- return {
137
- kind: 'error',
138
- text: `Нельзя возобновить: активная цель отсутствует.\n${USAGE}`,
139
- };
140
- }
141
- if (snap.state === GoalState.RUNNING) {
142
- return {
143
- kind: 'success',
144
- text: '▶️ Цель уже выполняется.',
145
- };
146
- }
147
- engine.resume();
148
- if (agent && typeof agent.followup === 'function') {
149
- try {
150
- const resumePrompt = `▶️ Цель возобновлена пользователем. Продолжай выполнение плана работ с того места, где остановился. Отмечай шаги через goal_update_progress.`;
151
- agent.followup(createGoalUserMessage(resumePrompt));
152
- } catch (err) {
153
- console.warn('[dsh-goal] Failed to followup agent on resume:', err);
154
- }
155
- }
156
- return {
157
- kind: 'success',
158
- text: `▶️ Цель возобновлена: «${snap.title}». Агент продолжает работу.`,
159
- };
160
- }
161
-
162
- case 'start': {
163
- const maxIterations = config.maxIterations ?? snap.maxIterations ?? 25;
164
- const newSnap = engine.startGoal(parsed.text, { maxIterations });
165
-
166
- // Если команда вызвана в контексте агента, отправляем цель в очередь модели
167
- if (agent && typeof agent.followup === 'function') {
168
- try {
169
- const userMsg = createGoalUserMessage(formatGoalStartPrompt(parsed.text));
170
- agent.followup(userMsg);
171
- } catch (err) {
172
- console.warn('[dsh-goal] Failed to dispatch goal to agent followup:', err);
173
- }
174
- }
175
-
176
- return {
177
- kind: 'success',
178
- text: `🎯 Активирована цель: «${newSnap.title}» (макс. ${newSnap.maxIterations} итераций).\n` +
179
- `Для паузы: /goal pause | Для сброса: /goal clear`,
180
- };
181
- }
182
-
183
- default:
184
- return {
185
- kind: 'error',
186
- text: `Неизвестная команда. ${USAGE}`,
187
- };
188
- }
189
- }
1
+ import { randomUUID } from 'node:crypto';
2
+ import { GoalState } from './goal-engine.js';
3
+
4
+ export const USAGE = 'Использование: /goal [<цель>|clear|pause|resume]';
5
+
6
+ /**
7
+ * Парсинг строки ввода для команды /goal
8
+ * @param {string} rawInput
9
+ * @returns {{ action: string, text?: string }}
10
+ */
11
+ export function parseGoalInput(rawInput = '') {
12
+ const input = String(rawInput || '').trim();
13
+ if (!input) {
14
+ return { action: 'show' };
15
+ }
16
+
17
+ const lower = input.toLowerCase();
18
+ if (lower === 'clear') return { action: 'clear' };
19
+ if (lower === 'pause') return { action: 'pause' };
20
+ if (lower === 'resume') return { action: 'resume' };
21
+
22
+ return { action: 'start', text: input };
23
+ }
24
+
25
+ /**
26
+ * Создание валидного identified сообщения пользователя для отправки агенту через followup
27
+ * @param {string} text
28
+ * @returns {Object}
29
+ */
30
+ export function createGoalUserMessage(text) {
31
+ return {
32
+ id: randomUUID(),
33
+ role: 'user',
34
+ content: [{ type: 'text', text }],
35
+ source: { kind: 'user' },
36
+ };
37
+ }
38
+
39
+ /**
40
+ * Формирование стартового промпта с жестким требованием составить план работ
41
+ * @param {string} goalText
42
+ * @returns {string}
43
+ */
44
+ export function formatGoalStartPrompt(goalText) {
45
+ return `🎯 Активирован режим цели (Goal Mode): "${goalText}"\n\n` +
46
+ `СТРОГИЙ КОНТРАКТ АВТОНОМНОГО РЕЖИМА:\n` +
47
+ `1. ОБЯЗАТЕЛЬНЫЙ ШАГ №1: Твоим ПЕРВЫМ действием ДО выполнения любой другой работы или ответа должен быть вызов инструмента goal_set_milestones с массивом из 3-7 конкретных, последовательных пунктов плана работ. Пользователь видит этот план в интерфейсе в реальном времени.\n` +
48
+ `2. ШАГ №2: После установки плана переходи к выполнению шагов. Перед началом работы над каждым шагом отметь его статус как in_progress через goal_update_progress(milestone_id, "in_progress").\n` +
49
+ `3. ШАГ №3: После успешного завершения шага отметь его статус как completed через goal_update_progress(milestone_id, "completed", "краткие итоги шага").\n` +
50
+ `4. ШАГ №4: Когда все пункты плана выполнены, вызови инструмент goal_finish(summary) с подробным резюме достигнутых результатов.`;
51
+ }
52
+
53
+ /**
54
+ * Форматирование текстового ответа для UI и постановка/управление задачей агента
55
+ * @param {import('./goal-engine.js').GoalEngine} engine
56
+ * @param {{ action: string, text?: string }} parsed
57
+ * @param {Object} [config]
58
+ * @param {Object} [agent]
59
+ * @returns {{ kind: 'success' | 'error', text: string }}
60
+ */
61
+ export function executeGoalSlashCommand(engine, parsed, config = {}, agent = null, sessionId = 'default') {
62
+ const sid = sessionId || 'default';
63
+ const snap = engine.getSnapshot(sid);
64
+
65
+ switch (parsed.action) {
66
+ case 'show': {
67
+ if (!snap.hasActiveGoal) {
68
+ return {
69
+ kind: 'success',
70
+ text: `🎯 Режим цели (Goal Mode): цель не установлена.\n${USAGE}`,
71
+ };
72
+ }
73
+
74
+ const milestonesInfo = snap.milestones.length > 0
75
+ ? `\nПлан работ:\n` + snap.milestones.map((m, i) => ` ${i + 1}. [${m.status}] ${m.title}`).join('\n')
76
+ : '';
77
+
78
+ return {
79
+ kind: 'success',
80
+ text: `🎯 Цель: «${snap.title}»\n` +
81
+ `Статус: ${snap.state}\n` +
82
+ `Время: ${snap.formattedElapsed}\n` +
83
+ `Итераций: ${snap.iterationsCount}/${snap.maxIterations}` +
84
+ milestonesInfo +
85
+ `\n\nКоманды: /goal pause, /goal resume, /goal clear, /goal <новая цель>`,
86
+ };
87
+ }
88
+
89
+ case 'clear': {
90
+ if (!snap.hasActiveGoal) {
91
+ return {
92
+ kind: 'success',
93
+ text: 'Цель не была установлена.',
94
+ };
95
+ }
96
+ engine.clear(sid);
97
+ if (agent && typeof agent.cancel === 'function') {
98
+ try {
99
+ agent.cancel({ kind: 'user', reason: 'Goal cleared by user' });
100
+ } catch (err) {
101
+ console.warn('[dsh-goal] Failed to cancel agent on clear:', err);
102
+ }
103
+ }
104
+ return {
105
+ kind: 'success',
106
+ text: '🎯 Цель сброшена.',
107
+ };
108
+ }
109
+
110
+ case 'pause': {
111
+ if (!snap.hasActiveGoal) {
112
+ return {
113
+ kind: 'error',
114
+ text: `Нельзя приостановить: активная цель отсутствует.\n${USAGE}`,
115
+ };
116
+ }
117
+ if (snap.state === GoalState.PAUSED) {
118
+ return {
119
+ kind: 'success',
120
+ text: ' Цель уже на паузе.',
121
+ };
122
+ }
123
+ engine.pause('Пауза по команде пользователя (/goal pause)', sid);
124
+ if (agent && typeof agent.cancel === 'function') {
125
+ try {
126
+ agent.cancel({ kind: 'user', reason: 'Goal paused by user' });
127
+ } catch (err) {
128
+ console.warn('[dsh-goal] Failed to cancel agent on pause:', err);
129
+ }
130
+ }
131
+ return {
132
+ kind: 'success',
133
+ text: `⏸ Цель приостановлена: «${snap.title}». Работа агента остановлена. Используйте /goal resume или кнопку ▶️ для продолжения.`,
134
+ };
135
+ }
136
+
137
+ case 'resume': {
138
+ if (!snap.hasActiveGoal) {
139
+ return {
140
+ kind: 'error',
141
+ text: `Нельзя возобновить: активная цель отсутствует.\n${USAGE}`,
142
+ };
143
+ }
144
+ if (snap.state === GoalState.RUNNING) {
145
+ return {
146
+ kind: 'success',
147
+ text: '▶️ Цель уже выполняется.',
148
+ };
149
+ }
150
+ engine.resume(sid);
151
+ if (agent && typeof agent.followup === 'function') {
152
+ try {
153
+ const resumePrompt = `▶️ Цель возобновлена пользователем. Продолжай выполнение плана работ с того места, где остановился. Отмечай шаги через goal_update_progress.`;
154
+ agent.followup(createGoalUserMessage(resumePrompt));
155
+ } catch (err) {
156
+ console.warn('[dsh-goal] Failed to followup agent on resume:', err);
157
+ }
158
+ }
159
+ return {
160
+ kind: 'success',
161
+ text: `▶️ Цель возобновлена: «${snap.title}». Агент продолжает работу.`,
162
+ };
163
+ }
164
+
165
+ case 'start': {
166
+ const maxIterations = config.maxIterations ?? snap.maxIterations ?? 25;
167
+ const newSnap = engine.startGoal(parsed.text, { maxIterations }, sid);
168
+
169
+ // Если команда вызвана в контексте агента, отправляем цель в очередь модели
170
+ if (agent && typeof agent.followup === 'function') {
171
+ try {
172
+ const userMsg = createGoalUserMessage(formatGoalStartPrompt(parsed.text));
173
+ agent.followup(userMsg);
174
+ } catch (err) {
175
+ console.warn('[dsh-goal] Failed to dispatch goal to agent followup:', err);
176
+ }
177
+ }
178
+
179
+ return {
180
+ kind: 'success',
181
+ text: `🎯 Активирована цель: «${newSnap.title}» (макс. ${newSnap.maxIterations} итераций).\n` +
182
+ `Для паузы: /goal pause | Для сброса: /goal clear`,
183
+ };
184
+ }
185
+
186
+ default:
187
+ return {
188
+ kind: 'error',
189
+ text: `Неизвестная команда. ${USAGE}`,
190
+ };
191
+ }
192
+ }