pi-goal-pro 1.0.1 → 1.1.1
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/CHANGELOG.md +67 -0
- package/README.md +8 -118
- package/README.ru.md +252 -0
- package/index.ts +35 -51
- package/package.json +6 -3
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [1.1.1] - 2026-06-11
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Separate `test.yml` (push/PR) and `release.yml` (tag + workflow_dispatch) CI/CD pipelines
|
|
15
|
+
- `CHANGELOG.md` following Keep a Changelog format
|
|
16
|
+
- `README.ru.md` — Russian language documentation
|
|
17
|
+
- `workflow_dispatch` trigger for manual releases
|
|
18
|
+
- `npm publish --provenance` for supply chain security
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- Split monolithic CI/CD into focused workflows (test quality vs. release)
|
|
23
|
+
- README installation order: npm install first, manual copy second
|
|
24
|
+
- Updated `.gitignore` to exclude `dist/` and `.env`
|
|
25
|
+
- Cleaner package.json scripts (`npm test` = only tests, `npm run test:all` = lint + tests)
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- Biome linter warnings (non-null assertions → type casts)
|
|
30
|
+
- Message renderer cognitive complexity
|
|
31
|
+
|
|
32
|
+
## [1.1.0] - 2026-06-11
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
- 48 unit tests covering all pure functions
|
|
37
|
+
- Biome linter + strict TypeScript configuration
|
|
38
|
+
- GitHub Actions CI/CD pipeline
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
|
|
42
|
+
- Split README into English (`README.md`) and Russian (`README.ru.md`)
|
|
43
|
+
- Simplified message renderer to reduce cognitive complexity
|
|
44
|
+
|
|
45
|
+
### Fixed
|
|
46
|
+
|
|
47
|
+
- Non-null assertion warnings in `parseTokenBudget` and `parseMaxAutoTurns`
|
|
48
|
+
|
|
49
|
+
## [1.0.1] - 2026-06-11
|
|
50
|
+
|
|
51
|
+
### Added
|
|
52
|
+
|
|
53
|
+
- Initial npm publish
|
|
54
|
+
|
|
55
|
+
## [1.0.0] - 2026-06-11
|
|
56
|
+
|
|
57
|
+
### Added
|
|
58
|
+
|
|
59
|
+
- `/goal <objective> [--tokens N] [--max-turns N]` command
|
|
60
|
+
- `get_goal` and `update_goal` tools for the agent
|
|
61
|
+
- Auto-continuation loop with no-progress detection
|
|
62
|
+
- Evidence-based completion (evidence/blocker required)
|
|
63
|
+
- Token budget tracking with auto-pause
|
|
64
|
+
- User input suspends auto-continuation
|
|
65
|
+
- Session entry persistence (survives reload, compaction, tree navigation)
|
|
66
|
+
- Footer status bar
|
|
67
|
+
- Bilingual README (English + Russian)
|
package/README.md
CHANGED
|
@@ -22,15 +22,18 @@ Then walk away. The agent keeps going. When it's done, it reports with evidence.
|
|
|
22
22
|
|
|
23
23
|
## Installation
|
|
24
24
|
|
|
25
|
+
### Via npm (recommended)
|
|
26
|
+
|
|
25
27
|
```bash
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
cp ./index.ts ~/.pi/agent/extensions/pi-goal-pro/
|
|
28
|
+
pi install npm:pi-goal-pro
|
|
29
|
+
/reload
|
|
29
30
|
```
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
### Manual
|
|
32
33
|
|
|
33
|
-
```
|
|
34
|
+
```bash
|
|
35
|
+
mkdir -p ~/.pi/agent/extensions/pi-goal-pro
|
|
36
|
+
cp ./index.ts ~/.pi/agent/extensions/pi-goal-pro/
|
|
34
37
|
/reload
|
|
35
38
|
```
|
|
36
39
|
|
|
@@ -283,116 +286,3 @@ Inspired by and building upon:
|
|
|
283
286
|
## License
|
|
284
287
|
|
|
285
288
|
MIT
|
|
286
|
-
|
|
287
|
-
---
|
|
288
|
-
|
|
289
|
-
# pi-goal-pro 🎯
|
|
290
|
-
|
|
291
|
-
> Персистентные автономные цели для [Pi](https://pi.dev) — с детекцией отсутствия прогресса, завершением на основе доказательств, бюджетом токенов и автопродолжением.
|
|
292
|
-
|
|
293
|
-
Задай долгоживущую цель — и агент будет работать автономно, пока не закончит, не будет приостановлен или не упрётся в ограничение. Без необходимости повторять промпт каждый turn.
|
|
294
|
-
|
|
295
|
-
```bash
|
|
296
|
-
/goal Переписать auth модуль на JWT с нормальной обработкой ошибок
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
Можно отойти от клавиатуры. Агент продолжает сам. Когда закончит — отчитается с доказательствами.
|
|
300
|
-
|
|
301
|
-
---
|
|
302
|
-
|
|
303
|
-
## Установка
|
|
304
|
-
|
|
305
|
-
```bash
|
|
306
|
-
mkdir -p ~/.pi/agent/extensions/pi-goal-pro
|
|
307
|
-
# Скопировать файл расширения:
|
|
308
|
-
cp ./index.ts ~/.pi/agent/extensions/pi-goal-pro/
|
|
309
|
-
```
|
|
310
|
-
|
|
311
|
-
Перезагрузить Pi:
|
|
312
|
-
|
|
313
|
-
```
|
|
314
|
-
/reload
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
Проверить что загрузилось:
|
|
318
|
-
|
|
319
|
-
```
|
|
320
|
-
/goal status
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
---
|
|
324
|
-
|
|
325
|
-
## Быстрый старт
|
|
326
|
-
|
|
327
|
-
Задай цель — агент начнёт работать:
|
|
328
|
-
|
|
329
|
-
```text
|
|
330
|
-
/goal Добавить retry логику в API клиент с экспоненциальной задержкой
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
Агент начинает немедленно. Статус в футере:
|
|
334
|
-
|
|
335
|
-
```
|
|
336
|
-
🎯 goal active (1.2K/50K) ← статус в футере
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
Управление жизненным циклом:
|
|
340
|
-
|
|
341
|
-
```text
|
|
342
|
-
/goal status # Показать текущее состояние
|
|
343
|
-
/goal pause # Приостановить активную цель
|
|
344
|
-
/goal resume # Возобновить приостановленную
|
|
345
|
-
/goal clear # Удалить все цели
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
---
|
|
349
|
-
|
|
350
|
-
## Инструменты агента
|
|
351
|
-
|
|
352
|
-
Когда цель активна, агент получает два инструмента:
|
|
353
|
-
|
|
354
|
-
**`get_goal`** — прочитать состояние цели.
|
|
355
|
-
|
|
356
|
-
**`update_goal`** — завершить цель (с доказательствами) или признать недостижимой (с причиной):
|
|
357
|
-
|
|
358
|
-
```typescript
|
|
359
|
-
// Завершено — нужно подтверждение
|
|
360
|
-
update_goal({
|
|
361
|
-
status: "complete",
|
|
362
|
-
evidence: "JWT middleware реализован, 12 тестов проходят, CI без регрессий"
|
|
363
|
-
})
|
|
364
|
-
|
|
365
|
-
// Недостижимо — нужно объяснение
|
|
366
|
-
update_goal({
|
|
367
|
-
status: "unmet",
|
|
368
|
-
blocker: "Заблокировано решением по JWT библиотеке — ожидание security review"
|
|
369
|
-
})
|
|
370
|
-
```
|
|
371
|
-
|
|
372
|
-
---
|
|
373
|
-
|
|
374
|
-
## Как это работает
|
|
375
|
-
|
|
376
|
-
Состояние цели хранится в session entry (custom type `pi-goal-pro`). Оно переживает:
|
|
377
|
-
- Перезагрузку сессии (`/reload`)
|
|
378
|
-
- Компактизацию (compaction)
|
|
379
|
-
- Навигацию по дереву сессии (`/tree`)
|
|
380
|
-
- Возобновление сессии
|
|
381
|
-
|
|
382
|
-
Состояние привязано к ветке — при переходе на другую ветку восстанавливается состояние целей для этой ветки.
|
|
383
|
-
|
|
384
|
-
---
|
|
385
|
-
|
|
386
|
-
## Философия дизайна
|
|
387
|
-
|
|
388
|
-
1. **Пользователь владеет целью** — Агент не может молча изменить objective.
|
|
389
|
-
2. **Доказательства перед завершением** — Агент должен верифицировать по реальным артефактам.
|
|
390
|
-
3. **Никаких бесконечных циклов** — Детекция отсутствия прогресса, лимит turn-ов и бюджет токенов.
|
|
391
|
-
4. **Ввод пользователя приостанавливает** — Когда ты печатаешь, автопродолжение ставится на паузу.
|
|
392
|
-
5. **Состояние привязано к ветке** — `/tree` в другую точку восстанавливает цели этой точки.
|
|
393
|
-
|
|
394
|
-
---
|
|
395
|
-
|
|
396
|
-
## Лицензия
|
|
397
|
-
|
|
398
|
-
MIT
|
package/README.ru.md
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
|
|
2
|
+
<p align="center">
|
|
3
|
+
<img src="https://img.shields.io/badge/pi-extension-8B5CF6?style=flat-square&logo=pi-hole&logoColor=white" alt="pi extension">
|
|
4
|
+
<img src="https://img.shields.io/npm/v/pi-goal-pro?style=flat-square&color=cb3837" alt="npm">
|
|
5
|
+
<img src="https://img.shields.io/github/actions/workflow/status/izzzzzi/pi-goal-pro/ci.yml?style=flat-square&branch=main" alt="CI">
|
|
6
|
+
<img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" alt="MIT">
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
# pi-goal-pro 🎯
|
|
10
|
+
|
|
11
|
+
> Персистентные автономные цели для [Pi](https://pi.dev) — с детекцией отсутствия прогресса, завершением на основе доказательств, бюджетом токенов и автопродолжением.
|
|
12
|
+
|
|
13
|
+
Задай долгоживущую цель — и агент будет работать автономно, пока не закончит, не будет приостановлен или не упрётся в ограничение. Без необходимости повторять промпт каждый turn.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
/goal Переписать auth модуль на JWT с нормальной обработкой ошибок
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Можно отойти от клавиатуры. Агент продолжает сам. Когда закончит — отчитается с доказательствами.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Установка
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pi install npm:pi-goal-pro
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Или вручную:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
mkdir -p ~/.pi/agent/extensions/pi-goal-pro
|
|
33
|
+
cp ./index.ts ~/.pi/agent/extensions/pi-goal-pro/
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Перезагрузить Pi:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
/reload
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Проверить что загрузилось:
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
/goal status
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Быстрый старт
|
|
51
|
+
|
|
52
|
+
Задай цель — агент начнёт работать:
|
|
53
|
+
|
|
54
|
+
```text
|
|
55
|
+
/goal Добавить retry логику в API клиент с экспоненциальной задержкой
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Агент начинает немедленно. Статус в футере:
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
🎯 goal active (1.2K/50K) ← статус в футере
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Управление жизненным циклом:
|
|
65
|
+
|
|
66
|
+
```text
|
|
67
|
+
/goal status # Показать текущее состояние
|
|
68
|
+
/goal pause # Приостановить активную цель
|
|
69
|
+
/goal resume # Возобновить приостановленную
|
|
70
|
+
/goal clear # Удалить все цели
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Возможности
|
|
76
|
+
|
|
77
|
+
### 🎯 Установка цели
|
|
78
|
+
|
|
79
|
+
```text
|
|
80
|
+
/goal Переписать auth модуль
|
|
81
|
+
|
|
82
|
+
# С бюджетом токенов (автопауза при превышении):
|
|
83
|
+
/goal Переписать auth модуль --tokens 100k
|
|
84
|
+
|
|
85
|
+
# С лимитом авто-продолжений:
|
|
86
|
+
/goal Переписать auth модуль --max-turns 10
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 🤖 Инструменты агента
|
|
90
|
+
|
|
91
|
+
Когда цель активна, агент получает два инструмента:
|
|
92
|
+
|
|
93
|
+
**`get_goal`** — прочитать состояние цели:
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"active": {
|
|
98
|
+
"objective": "Переписать auth модуль на JWT",
|
|
99
|
+
"status": "active",
|
|
100
|
+
"tokens_used": 12400,
|
|
101
|
+
"token_budget": 50000,
|
|
102
|
+
"remaining_tokens": 37600,
|
|
103
|
+
"time_used_seconds": 89,
|
|
104
|
+
"auto_turns": 3,
|
|
105
|
+
"max_auto_turns": 25
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**`update_goal`** — завершить цель или признать недостижимой:
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
// Завершено — нужно подтверждение
|
|
114
|
+
update_goal({
|
|
115
|
+
status: "complete",
|
|
116
|
+
evidence: "JWT middleware реализован, 12 тестов проходят, CI без регрессий"
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
// Недостижимо — нужно объяснение
|
|
120
|
+
update_goal({
|
|
121
|
+
status: "unmet",
|
|
122
|
+
blocker: "Заблокировано решением по JWT библиотеке — ожидание security review"
|
|
123
|
+
})
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### 🔄 Автопродолжение
|
|
127
|
+
|
|
128
|
+
После каждого turn агента расширение автоматически отправляет continuation prompt, если:
|
|
129
|
+
- Цель всё ещё `active`
|
|
130
|
+
- Предыдущий turn был goal-driven
|
|
131
|
+
- Пользователь ничего не вводил (ввод приостанавливает автопродолжение)
|
|
132
|
+
- Не достигнуты лимиты
|
|
133
|
+
|
|
134
|
+
### 🛡️ Детекция отсутствия прогресса
|
|
135
|
+
|
|
136
|
+
Если агент генерирует очень мало выходных токенов (по умолчанию <50) 2 раза подряд, цель автоматически приостанавливается:
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
⏸ Goal paused (no progress for 2 turns). Use /goal resume to continue.
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Это предотвращает бесконечные циклы, когда агент просто подтверждает получение без реального прогресса.
|
|
143
|
+
|
|
144
|
+
### 💰 Бюджет токенов
|
|
145
|
+
|
|
146
|
+
Установи бюджет с `--tokens`:
|
|
147
|
+
|
|
148
|
+
```text
|
|
149
|
+
/goal Написать документацию для всех API эндпоинтов --tokens 100k
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Когда бюджет исчерпан, цель приостанавливается с wrap-up промптом — агент подводит итог.
|
|
153
|
+
|
|
154
|
+
### 📋 Завершение с доказательствами
|
|
155
|
+
|
|
156
|
+
Агент обязан предоставить конкретные доказательства перед отметкой цели как выполненной. Это предотвращает преждевременные "done" и гарантирует верификацию по реальным файлам, тестам и выводам команд.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Команды
|
|
161
|
+
|
|
162
|
+
| Команда | Описание |
|
|
163
|
+
|---------|----------|
|
|
164
|
+
| `/goal <objective>` | Установить новую цель |
|
|
165
|
+
| `/goal <text> --tokens N` | Цель с бюджетом токенов |
|
|
166
|
+
| `/goal <text> --max-turns N` | Цель с лимитом авто-продолжений |
|
|
167
|
+
| `/goal status` | Показать состояние |
|
|
168
|
+
| `/goal pause` | Приостановить |
|
|
169
|
+
| `/goal resume` | Возобновить |
|
|
170
|
+
| `/goal clear` | Удалить все |
|
|
171
|
+
| `/goal help` | Справка |
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Как это работает
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
/goal Переписать auth модуль
|
|
179
|
+
│
|
|
180
|
+
▼
|
|
181
|
+
✓ Цель создана, сохранена в session entry
|
|
182
|
+
✓ Агент получил get_goal + update_goal
|
|
183
|
+
✓ Первое продолжение запущено немедленно
|
|
184
|
+
│
|
|
185
|
+
▼
|
|
186
|
+
┌── Цикл автопродолжения ───────────────────┐
|
|
187
|
+
│ │
|
|
188
|
+
│ turn_start → turn_end → agent_end │
|
|
189
|
+
│ │ │
|
|
190
|
+
│ ┌─────────┴─────────┐ │
|
|
191
|
+
│ │ Цель активна? │ │
|
|
192
|
+
│ │ Нет прогресса? │ │
|
|
193
|
+
│ │ Пользователь ввёл?│ │
|
|
194
|
+
│ │ Бюджет исчерпан? │ │
|
|
195
|
+
│ │ Лимит turn-ов? │ │
|
|
196
|
+
│ └─────────┬─────────┘ │
|
|
197
|
+
│ │ │
|
|
198
|
+
│ ┌─────────┴──────────┐ │
|
|
199
|
+
│ │ Да → continuation │ │
|
|
200
|
+
│ │ Нет → stop/pause │ │
|
|
201
|
+
│ └────────────────────┘ │
|
|
202
|
+
│ │
|
|
203
|
+
└───────────────────────────────────────────┘
|
|
204
|
+
│
|
|
205
|
+
▼
|
|
206
|
+
Агент вызывает update_goal({ status: "complete", evidence })
|
|
207
|
+
→ Цель архивирована, агент остановлен
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Сохранение состояния
|
|
211
|
+
|
|
212
|
+
Состояние цели хранится в session entry (custom type `pi-goal-pro`). Оно переживает:
|
|
213
|
+
- Перезагрузку сессии (`/reload`)
|
|
214
|
+
- Компактизацию (compaction)
|
|
215
|
+
- Навигацию по дереву сессии (`/tree`)
|
|
216
|
+
- Возобновление сессии
|
|
217
|
+
|
|
218
|
+
Состояние привязано к ветке — при переходе на другую ветку восстанавливается состояние целей для этой ветки.
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Философия дизайна
|
|
223
|
+
|
|
224
|
+
1. **Пользователь владеет целью** — Агент не может молча изменить objective.
|
|
225
|
+
2. **Доказательства перед завершением** — Агент должен верифицировать по реальным артефактам.
|
|
226
|
+
3. **Никаких бесконечных циклов** — Детекция отсутствия прогресса, лимит turn-ов и бюджет токенов.
|
|
227
|
+
4. **Ввод пользователя приостанавливает** — Когда ты печатаешь, автопродолжение ставится на паузу.
|
|
228
|
+
5. **Состояние привязано к ветке** — `/tree` в другую точку восстанавливает цели этой точки.
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Разработка
|
|
233
|
+
|
|
234
|
+
Расширение в одном файле — не требует сборки. Правишь `index.ts`, затем `/reload`.
|
|
235
|
+
|
|
236
|
+
Запуск без установки:
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
pi -e ~/.pi/agent/extensions/pi-goal-pro/index.ts
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Тесты:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
node --test tests/
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Лицензия
|
|
251
|
+
|
|
252
|
+
MIT
|
package/index.ts
CHANGED
|
@@ -22,7 +22,6 @@
|
|
|
22
22
|
|
|
23
23
|
import { StringEnum } from '@earendil-works/pi-ai';
|
|
24
24
|
import type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';
|
|
25
|
-
import { matchesKey } from '@earendil-works/pi-tui';
|
|
26
25
|
import { Type } from 'typebox';
|
|
27
26
|
|
|
28
27
|
// ─── Types ───────────────────────────────────────────────────────────────
|
|
@@ -119,7 +118,7 @@ function parseTokenBudget(input: string): { objective: string; tokenBudget: numb
|
|
|
119
118
|
if (!Number.isFinite(num) || num <= 0) return { objective: input.trim(), tokenBudget: null };
|
|
120
119
|
const mult = m[2]?.toLowerCase() === 'm' ? 1_000_000 : m[2]?.toLowerCase() === 'k' ? 1_000 : 1;
|
|
121
120
|
const budget = Math.round(num * mult);
|
|
122
|
-
const idx = m.index
|
|
121
|
+
const idx = m.index as number;
|
|
123
122
|
const objective = (input.slice(0, idx) + input.slice(idx + m[0].length)).trim();
|
|
124
123
|
return { objective, tokenBudget: budget };
|
|
125
124
|
}
|
|
@@ -129,7 +128,7 @@ function parseMaxAutoTurns(input: string): { rest: string; maxAutoTurns: number
|
|
|
129
128
|
if (!m) return { rest: input.trim(), maxAutoTurns: null };
|
|
130
129
|
const turns = Number.parseInt(m[1], 10);
|
|
131
130
|
if (!Number.isFinite(turns) || turns <= 0) return { rest: input.trim(), maxAutoTurns: null };
|
|
132
|
-
const idx = m.index
|
|
131
|
+
const idx = m.index as number;
|
|
133
132
|
const rest = (input.slice(0, idx) + input.slice(idx + m[0].length)).trim();
|
|
134
133
|
return { rest, maxAutoTurns: turns };
|
|
135
134
|
}
|
|
@@ -692,65 +691,50 @@ Do not call update_goal unless the goal is actually complete.`;
|
|
|
692
691
|
|
|
693
692
|
// ── Message renderer for goal events ───────────────────────────────
|
|
694
693
|
|
|
694
|
+
const GOAL_KIND_LABELS: Record<string, (th: typeof theme) => string> = {
|
|
695
|
+
active: (th) => th.fg('accent', 'active'),
|
|
696
|
+
continuation: (th) => th.fg('muted', 'continuing'),
|
|
697
|
+
paused: (th) => th.fg('warning', 'paused'),
|
|
698
|
+
resumed: (th) => th.fg('accent', 'resumed'),
|
|
699
|
+
cleared: (th) => th.fg('dim', 'cleared'),
|
|
700
|
+
budget_limited: (th) => th.fg('warning', 'budget'),
|
|
701
|
+
complete: (th) => th.fg('success', 'achieved'),
|
|
702
|
+
unmet: (th) => th.fg('error', 'unmet'),
|
|
703
|
+
};
|
|
704
|
+
|
|
695
705
|
pi.registerMessageRenderer(`${GOAL_STORAGE_TYPE}:event`, (message, options, theme) => {
|
|
696
706
|
const details = message.details as GoalEvent | undefined;
|
|
697
707
|
const kind = details?.kind ?? 'continuation';
|
|
698
708
|
const state = details?.goal ?? null;
|
|
699
709
|
|
|
700
|
-
const
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
const prefix = theme.fg('accent', theme.bold('Goal'));
|
|
707
|
-
const kindLabel = this.kindLabel(kind, theme);
|
|
708
|
-
const statusText = theme.fg('dim', isExpanded ? '' : '(ctrl+o to expand)');
|
|
709
|
-
|
|
710
|
-
lines.push(`${prefix} ${kindLabel} ${!isExpanded ? statusText : ''}`);
|
|
711
|
-
|
|
712
|
-
if (isExpanded && state) {
|
|
713
|
-
lines.push(`${theme.fg('dim', ' Status: ')}${theme.fg('text', kind)}`);
|
|
714
|
-
lines.push(`${theme.fg('dim', ' Goal: ')}${theme.fg('text', state.objective)}`);
|
|
715
|
-
if (state.completionEvidence) {
|
|
716
|
-
lines.push(`${theme.fg('dim', ' Evidence: ')}${theme.fg('success', state.completionEvidence)}`);
|
|
717
|
-
}
|
|
718
|
-
if (state.blocker) {
|
|
719
|
-
lines.push(`${theme.fg('dim', ' Blocker: ')}${theme.fg('warning', state.blocker)}`);
|
|
720
|
-
}
|
|
721
|
-
const usage = state.tokenBudget
|
|
722
|
-
? `${formatTokens(state.tokensUsed)}/${formatTokens(state.tokenBudget)}`
|
|
723
|
-
: formatDuration(state.timeUsedMs);
|
|
724
|
-
lines.push(`${theme.fg('dim', ' Usage: ')}${theme.fg('text', usage)}`);
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
return lines;
|
|
728
|
-
}
|
|
710
|
+
const renderGoalEvent = (_width: number): string[] => {
|
|
711
|
+
const lines: string[] = [];
|
|
712
|
+
const isExpanded = options.expanded;
|
|
713
|
+
const prefix = theme.fg('accent', theme.bold('Goal'));
|
|
714
|
+
const kindLabel = (GOAL_KIND_LABELS[kind] ?? ((th: typeof theme) => th.fg('text', kind)))(theme);
|
|
715
|
+
const statusText = theme.fg('dim', isExpanded ? '' : '(ctrl+o to expand)');
|
|
729
716
|
|
|
730
|
-
|
|
717
|
+
lines.push(`${prefix} ${kindLabel} ${!isExpanded ? statusText : ''}`);
|
|
731
718
|
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
719
|
+
if (isExpanded && state) {
|
|
720
|
+
lines.push(`${theme.fg('dim', ' Status: ')}${theme.fg('text', kind)}`);
|
|
721
|
+
lines.push(`${theme.fg('dim', ' Goal: ')}${theme.fg('text', state.objective)}`);
|
|
722
|
+
if (state.completionEvidence) {
|
|
723
|
+
lines.push(`${theme.fg('dim', ' Evidence: ')}${theme.fg('success', state.completionEvidence)}`);
|
|
735
724
|
}
|
|
725
|
+
if (state.blocker) {
|
|
726
|
+
lines.push(`${theme.fg('dim', ' Blocker: ')}${theme.fg('warning', state.blocker)}`);
|
|
727
|
+
}
|
|
728
|
+
const usage = state.tokenBudget
|
|
729
|
+
? `${formatTokens(state.tokensUsed)}/${formatTokens(state.tokenBudget)}`
|
|
730
|
+
: formatDuration(state.timeUsedMs);
|
|
731
|
+
lines.push(`${theme.fg('dim', ' Usage: ')}${theme.fg('text', usage)}`);
|
|
736
732
|
}
|
|
737
733
|
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
active: th.fg('accent', 'active'),
|
|
741
|
-
continuation: th.fg('muted', 'continuing'),
|
|
742
|
-
paused: th.fg('warning', 'paused'),
|
|
743
|
-
resumed: th.fg('accent', 'resumed'),
|
|
744
|
-
cleared: th.fg('dim', 'cleared'),
|
|
745
|
-
budget_limited: th.fg('warning', 'budget'),
|
|
746
|
-
complete: th.fg('success', 'achieved'),
|
|
747
|
-
unmet: th.fg('error', 'unmet'),
|
|
748
|
-
};
|
|
749
|
-
return labels[k] ?? k;
|
|
750
|
-
}
|
|
751
|
-
})();
|
|
734
|
+
return lines;
|
|
735
|
+
};
|
|
752
736
|
|
|
753
|
-
return
|
|
737
|
+
return { render: renderGoalEvent, invalidate: () => {} };
|
|
754
738
|
});
|
|
755
739
|
|
|
756
740
|
// ── Commands ───────────────────────────────────────────────────────
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goal-pro",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Persistent autonomous goals for Pi — with no-progress detection, evidence-based completion, token budgets, and auto-continuation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.ts",
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
"files": [
|
|
9
9
|
"index.ts",
|
|
10
10
|
"README.md",
|
|
11
|
+
"README.ru.md",
|
|
12
|
+
"CHANGELOG.md",
|
|
11
13
|
"LICENSE"
|
|
12
14
|
],
|
|
13
15
|
"keywords": [
|
|
@@ -53,11 +55,12 @@
|
|
|
53
55
|
"tsx": "^4.0.0"
|
|
54
56
|
},
|
|
55
57
|
"scripts": {
|
|
56
|
-
"test": "npm run
|
|
58
|
+
"test": "npm run test:unit",
|
|
57
59
|
"test:unit": "tsx --test tests/index.test.ts",
|
|
60
|
+
"test:all": "npm run lint && npm run test:unit",
|
|
61
|
+
"lint": "biome lint .",
|
|
58
62
|
"check": "biome check .",
|
|
59
63
|
"format": "biome format --write .",
|
|
60
|
-
"lint": "biome lint .",
|
|
61
64
|
"dev": "pi -e ./index.ts"
|
|
62
65
|
}
|
|
63
66
|
}
|