@goodandready/dsh-goal 0.1.1 → 0.1.3

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 CHANGED
@@ -1,47 +1,143 @@
1
- # @goodandready/dsh-goal
1
+ # 📦 @goodandready/dsh-goal
2
2
 
3
- > **Goal Mode & Autonomous Execution Plugin for DeepSeek Harness (DSH)**
3
+ <div align="center">
4
4
 
5
- `dsh-goal` adds an autonomous goal-tracking loop with a sticky top status banner, milestone decomposition, live timer, and interactive controls (pause, resume, cancel, modal details).
5
+ <h3>Autonomous Goal Execution & Multi-Turn Task Tracking Engine with Sticky Header for DeepSeek Harness</h3>
6
+
7
+ <p align="center">
8
+ <a href="https://www.npmjs.com/package/@goodandready/dsh-goal"><img src="https://img.shields.io/npm/v/@goodandready/dsh-goal.svg?style=for-the-badge&color=6366f1&labelColor=1e1b4b" alt="npm version"></a>
9
+ <a href="LICENSE"><img src="https://img.shields.io/github/license/GooDAnDReaDY/dsh-goal.svg?style=for-the-badge&color=10b981&labelColor=064e3b" alt="license"></a>
10
+ <a href="https://github.com/topics/dsh-plugin"><img src="https://img.shields.io/badge/DSH-Plugin-8b5cf6.svg?style=for-the-badge&labelColor=2e1065" alt="DSH Plugin"></a>
11
+ <a href="https://nodejs.org"><img src="https://img.shields.io/badge/Node-20%2B-f59e0b.svg?style=for-the-badge&labelColor=451a03" alt="Node version"></a>
12
+ </p>
13
+
14
+ <!-- Author Showcase Link -->
15
+ <p align="center">
16
+ <a href="https://goodandready.app/"><img src="https://img.shields.io/badge/All_Author_Projects-goodandready.app-ff4500.svg?style=for-the-badge&logo=rocket&logoColor=white&labelColor=1a1a2e" alt="GoodAndReady Showcase"></a>
17
+ </p>
18
+
19
+ <p align="center">
20
+ <a href="README.md"><b>🇬🇧 English</b></a> •
21
+ <a href="docs/README.ru.md"><b>🇷🇺 Русский</b></a> •
22
+ <a href="docs/README.zh.md"><b>🇨🇳 中文说明</b></a>
23
+ </p>
24
+
25
+ </div>
26
+
27
+ ---
28
+
29
+ ## ⚡ Overview & The Problem
30
+
31
+ Complex engineering tasks require multi-step autonomy: decomposing high-level objectives into milestones, executing successive iterations without manual user re-prompting, and maintaining clear visibility into task progress.
32
+
33
+ **`@goodandready/dsh-goal`** brings autonomous goal execution to DeepSeek Harness via the `/goal` command:
34
+ * 🎯 **Sticky Top Goal Banner**: Pinned status header with live timer (`• 2s`, `• 1m 45s`), active goal title, and interactive control buttons.
35
+ * ⏸️ **Play / Pause / Cancel**: Instantly pause the autonomous loop or resume execution on demand.
36
+ * 📋 **Milestone Breakdown Drawer**: Interactive checklist showing sub-tasks, percentage completion, and iteration logs.
37
+ * 🤖 **Autonomous Agent Tools**: Provides `goal_set_milestones`, `goal_update_progress`, and `goal_finish` tools directly to the agent.
38
+ * 🛡️ **Safety Guardrails**: Configurable `maxIterations` limit to prevent runaway loops.
39
+
40
+ ---
41
+
42
+ ## 🏛️ Architecture
43
+
44
+ ```mermaid
45
+ graph TD
46
+ subgraph Input ["User Interaction"]
47
+ Cmd["Slash Command: /goal &lt;objective&gt;"]
48
+ API["REST API: POST /dsh-goal/action"]
49
+ end
50
+
51
+ subgraph GoalEngine ["Goal Lifecycle Engine (lib/index.js)"]
52
+ State["State Manager (IDLE, RUNNING, PAUSED, COMPLETED)"]
53
+ Milestones["Milestone Tracker & Decomposition"]
54
+ Disk["Persistence Store (~/.dsh/goal-state.json)"]
55
+ end
56
+
57
+ subgraph AgentLoop ["Autonomous Agent Drive"]
58
+ ToolSet["goal_set_milestones"]
59
+ ToolProgress["goal_update_progress"]
60
+ ToolFinish["goal_finish"]
61
+ LimitGuard{"maxIterations Guard"}
62
+ end
63
+
64
+ subgraph UI ["DSH Web Interface"]
65
+ Banner["Sticky Top Goal Banner"]
66
+ Timer["Live Elapsed Timer"]
67
+ Drawer["Milestone Checklist Modal"]
68
+ Settings["Settings Card (Schemastery)"]
69
+ end
70
+
71
+ Cmd --> GoalEngine
72
+ API --> GoalEngine
73
+ GoalEngine --> State
74
+ State --> Disk
75
+ State --> Banner
76
+ State --> Drawer
77
+ GoalEngine --> AgentLoop
78
+ AgentLoop --> LimitGuard
79
+ ToolSet --> GoalEngine
80
+ ToolProgress --> GoalEngine
81
+ ToolFinish --> GoalEngine
82
+ ```
6
83
 
7
84
  ---
8
85
 
9
- ## Features
86
+ ## 📦 Installation
10
87
 
11
- - 🎯 **Sticky Top Goal Banner**: Minimalist header pinned above the chat showing the active goal, elapsed timer (`• 2s`, `• 1m 45s`), and controls.
12
- - ⏸️ **Play / Pause / Cancel**: Instantly halt autonomous multi-turn loops or resume whenever ready.
13
- - ⛶ **Milestone Modal Drawer**: Interactive checklist of sub-tasks with progress bar and execution logs.
14
- - 🤖 **Agent Tools**:
15
- - `goal_set_milestones`: Autonomous breakdown of high-level goals into step-by-step tasks.
16
- - `goal_update_progress`: Step-by-step milestone completion tracking.
17
- - `goal_finish`: Goal completion with deliverable summary.
18
- - ⚙️ **Safety Limit & Settings**: Max iterations guardrail to prevent infinite agent loops.
19
- - 🌐 **Bilingual**: Full English and Russian localization.
88
+ ```bash
89
+ dsh plugin --profile web add @goodandready/dsh-goal
90
+ ```
91
+
92
+ Restart your DeepSeek Harness instance and refresh the browser.
20
93
 
21
94
  ---
22
95
 
23
- ## Installation
96
+ ## 💬 Usage & Quick Start
97
+
98
+ Start a goal directly in chat:
99
+
100
+ ```text
101
+ /goal Refactor the authentication middleware and add integration tests
102
+ ```
103
+
104
+ Or trigger via REST API:
24
105
 
25
106
  ```bash
26
- dsh plugin add @goodandready/dsh-goal
107
+ curl -X POST http://localhost:3080/dsh-goal/action \
108
+ -H "Content-Type: application/json" \
109
+ -d '{"action":"start","title":"Optimize database queries"}'
110
+ ```
111
+
112
+ ---
113
+
114
+ ## ⚙️ Configuration Reference (`settings.yaml`)
115
+
116
+ ```yaml
117
+ dsh-goal:
118
+ maxIterations: 25
119
+ autoDrive: true
120
+ enableSound: true
27
121
  ```
28
122
 
29
- ## Quick Start
123
+ | Parameter | Type | Default | Description |
124
+ |:---|:---|:---|:---|
125
+ | `maxIterations` | `number` | `25` | Safety limit: maximum autonomous iterations per goal |
126
+ | `autoDrive` | `boolean` | `true` | Keep the autonomous agent loop running between turns |
127
+ | `enableSound` | `boolean` | `true` | Play completion audio chime when a goal finishes |
128
+
129
+ ---
30
130
 
31
- 1. Start a goal in chat:
32
- ```text
33
- /goal Refactor auth tokens and write integration tests
34
- ```
35
- 2. Or use the REST API:
36
- ```bash
37
- curl -X POST http://localhost:3080/dsh-goal/action \
38
- -H "Content-Type: application/json" \
39
- -d '{"action":"start","title":"Optimize database queries"}'
40
- ```
41
- 3. Watch the sticky top banner update with live timer and milestones in real-time.
131
+ ## 🧪 Testing
132
+
133
+ Run the automated test suite:
134
+
135
+ ```bash
136
+ npm test
137
+ ```
42
138
 
43
139
  ---
44
140
 
45
- ## License
141
+ ## 📄 License
46
142
 
47
- MIT © [goodandready](https://goodandready.app)
143
+ MIT © [GooDAnDReaDY](https://github.com/GooDAnDReaDY)
@@ -0,0 +1,143 @@
1
+ # 📦 @goodandready/dsh-goal
2
+
3
+ <div align="center">
4
+
5
+ <h3>Движок автономного выполнения целей и многошагового трекинга задач для DeepSeek Harness</h3>
6
+
7
+ <p align="center">
8
+ <a href="https://www.npmjs.com/package/@goodandready/dsh-goal"><img src="https://img.shields.io/npm/v/@goodandready/dsh-goal.svg?style=for-the-badge&color=6366f1&labelColor=1e1b4b" alt="npm version"></a>
9
+ <a href="../LICENSE"><img src="https://img.shields.io/github/license/GooDAnDReaDY/dsh-goal.svg?style=for-the-badge&color=10b981&labelColor=064e3b" alt="license"></a>
10
+ <a href="https://github.com/topics/dsh-plugin"><img src="https://img.shields.io/badge/DSH-Plugin-8b5cf6.svg?style=for-the-badge&labelColor=2e1065" alt="DSH Plugin"></a>
11
+ <a href="https://nodejs.org"><img src="https://img.shields.io/badge/Node-20%2B-f59e0b.svg?style=for-the-badge&labelColor=451a03" alt="Node version"></a>
12
+ </p>
13
+
14
+ <!-- Обязательная кнопка перехода на витрину всех проектов -->
15
+ <p align="center">
16
+ <a href="https://goodandready.app/"><img src="https://img.shields.io/badge/Все_проекты_автора-goodandready.app-ff4500.svg?style=for-the-badge&logo=rocket&logoColor=white&labelColor=1a1a2e" alt="Все проекты автора"></a>
17
+ </p>
18
+
19
+ <p align="center">
20
+ <a href="../README.md"><b>🇬🇧 English</b></a> •
21
+ <a href="README.ru.md"><b>🇷🇺 Русский</b></a> •
22
+ <a href="README.zh.md"><b>🇨🇳 中文说明</b></a>
23
+ </p>
24
+
25
+ </div>
26
+
27
+ ---
28
+
29
+ ## ⚡ Обзор и решаемая проблема
30
+
31
+ Решение комплексных инженерных задач требует автономного многошагового выполнения: декомпозиции целей на майлстоуны, непрерывного выполнения итераций без ручного подталкивания пользователя и наглядного отслеживания прогресса.
32
+
33
+ **`@goodandready/dsh-goal`** внедряет автономный режим достижения целей в DeepSeek Harness через команду `/goal`:
34
+ * 🎯 **Плавающий баннер в шапке**: зафиксированный статус-бар над чатом с таймером выполнения (`• 2s`, `• 1m 45s`), заголовком цели и кнопками управления.
35
+ * ⏸️ **Управление циклом (Play / Pause / Cancel)**: мгновенная приостановка автономного цикла агента или его возобновление в любой момент.
36
+ * 📋 **Интерактивная панель майлстоунов**: модальное окно со списком подзадач, прогресс-баром и журналом выполнения.
37
+ * 🤖 **Инструменты агента**: `goal_set_milestones`, `goal_update_progress` и `goal_finish` для самостоятельной работы ИИ.
38
+ * 🛡️ **Защитные лимиты**: настраиваемое ограничение `maxIterations` для предотвращения бесконечных циклов.
39
+
40
+ ---
41
+
42
+ ## 🏛️ Архитектура
43
+
44
+ ```mermaid
45
+ graph TD
46
+ subgraph Input ["Взаимодействие"]
47
+ Cmd["Слеш-команда: /goal &lt;цель&gt;"]
48
+ API["REST API: POST /dsh-goal/action"]
49
+ end
50
+
51
+ subgraph GoalEngine ["Движок целей (lib/index.js)"]
52
+ State["Менеджер состояний (IDLE, RUNNING, PAUSED, COMPLETED)"]
53
+ Milestones["Трекер и декомпозиция майлстоунов"]
54
+ Disk["Хранилище состояний (~/.dsh/goal-state.json)"]
55
+ end
56
+
57
+ subgraph AgentLoop ["Автономный цикл агента"]
58
+ ToolSet["goal_set_milestones"]
59
+ ToolProgress["goal_update_progress"]
60
+ ToolFinish["goal_finish"]
61
+ LimitGuard{"Ограничение maxIterations"}
62
+ end
63
+
64
+ subgraph UI ["Интерфейс DSH"]
65
+ Banner["Баннер цели над чатом"]
66
+ Timer["Таймер выполнения"]
67
+ Drawer["Модальное окно задач"]
68
+ Settings["Карточка настроек (Schemastery)"]
69
+ end
70
+
71
+ Cmd --> GoalEngine
72
+ API --> GoalEngine
73
+ GoalEngine --> State
74
+ State --> Disk
75
+ State --> Banner
76
+ State --> Drawer
77
+ GoalEngine --> AgentLoop
78
+ AgentLoop --> LimitGuard
79
+ ToolSet --> GoalEngine
80
+ ToolProgress --> GoalEngine
81
+ ToolFinish --> GoalEngine
82
+ ```
83
+
84
+ ---
85
+
86
+ ## 📦 Установка
87
+
88
+ ```bash
89
+ dsh plugin --profile web add @goodandready/dsh-goal
90
+ ```
91
+
92
+ Перезапустите экземпляр DeepSeek Harness и обновите вкладку в браузере.
93
+
94
+ ---
95
+
96
+ ## 💬 Быстрый старт
97
+
98
+ Запустите цель прямо в чате:
99
+
100
+ ```text
101
+ /goal Провести рефакторинг middleware авторизации и написать интеграционные тесты
102
+ ```
103
+
104
+ Или через REST API:
105
+
106
+ ```bash
107
+ curl -X POST http://localhost:3080/dsh-goal/action \
108
+ -H "Content-Type: application/json" \
109
+ -d '{"action":"start","title":"Оптимизация SQL-запросов"}'
110
+ ```
111
+
112
+ ---
113
+
114
+ ## ⚙️ Таблица конфигурации (`settings.yaml`)
115
+
116
+ ```yaml
117
+ dsh-goal:
118
+ maxIterations: 25
119
+ autoDrive: true
120
+ enableSound: true
121
+ ```
122
+
123
+ | Параметр | Тип | По умолчанию | Описание |
124
+ |:---|:---|:---|:---|
125
+ | `maxIterations` | `number` | `25` | Защитный лимит: максимум автономных итераций на цель |
126
+ | `autoDrive` | `boolean` | `true` | Автоматически продолжать автономный цикл между тактами |
127
+ | `enableSound` | `boolean` | `true` | Звуковое уведомление при успешном завершении цели |
128
+
129
+ ---
130
+
131
+ ## 🧪 Тестирование
132
+
133
+ Запуск набора юнит-тестов:
134
+
135
+ ```bash
136
+ npm test
137
+ ```
138
+
139
+ ---
140
+
141
+ ## 📄 Лицензия
142
+
143
+ MIT © [GooDAnDReaDY](https://github.com/GooDAnDReaDY)
@@ -0,0 +1,143 @@
1
+ # 📦 @goodandready/dsh-goal
2
+
3
+ <div align="center">
4
+
5
+ <h3>面向 DeepSeek Harness 的自主目标执行、任务里程碑分解与顶部常驻状态条引擎</h3>
6
+
7
+ <p align="center">
8
+ <a href="https://www.npmjs.com/package/@goodandready/dsh-goal"><img src="https://img.shields.io/npm/v/@goodandready/dsh-goal.svg?style=for-the-badge&color=6366f1&labelColor=1e1b4b" alt="npm version"></a>
9
+ <a href="../LICENSE"><img src="https://img.shields.io/github/license/GooDAnDReaDY/dsh-goal.svg?style=for-the-badge&color=10b981&labelColor=064e3b" alt="license"></a>
10
+ <a href="https://github.com/topics/dsh-plugin"><img src="https://img.shields.io/badge/DSH-Plugin-8b5cf6.svg?style=for-the-badge&labelColor=2e1065" alt="DSH Plugin"></a>
11
+ <a href="https://nodejs.org"><img src="https://img.shields.io/badge/Node-20%2B-f59e0b.svg?style=for-the-badge&labelColor=451a03" alt="Node version"></a>
12
+ </p>
13
+
14
+ <!-- 作者所有项目展示页面链接 -->
15
+ <p align="center">
16
+ <a href="https://goodandready.app/"><img src="https://img.shields.io/badge/作者所有开源项目-goodandready.app-ff4500.svg?style=for-the-badge&logo=rocket&logoColor=white&labelColor=1a1a2e" alt="所有项目"></a>
17
+ </p>
18
+
19
+ <p align="center">
20
+ <a href="../README.md"><b>🇬🇧 English</b></a> •
21
+ <a href="README.ru.md"><b>🇷🇺 Русский</b></a> •
22
+ <a href="README.zh.md"><b>🇨🇳 中文说明</b></a>
23
+ </p>
24
+
25
+ </div>
26
+
27
+ ---
28
+
29
+ ## ⚡ 概述与核心解决痛点
30
+
31
+ 复杂的软件工程任务往往需要多步骤的自主循环:将宏观目标分解为里程碑子任务、多轮自动连续推进而无需用户反复手动下发指令,以及对执行过程保持清晰的可视化监控。
32
+
33
+ **`@goodandready/dsh-goal`** 通过 `/goal` 指令为 DeepSeek Harness 带来原生的目标自主执行系统:
34
+ * 🎯 **顶部常驻状态条**:固定在聊天窗口顶部的状态横幅,显示实时运行计时器(`• 2s`, `• 1m 45s`)、当前目标标题与控制按钮。
35
+ * ⏸️ **自主循环控制 (Play / Pause / Cancel)**:可随时暂停智能体自主多轮循环,或根据需要一键恢复。
36
+ * 📋 **里程碑任务抽屉**:交互式子任务清单,展示完成百分比进度条与执行日志。
37
+ * 🤖 **智能体原生工具**:提供 `goal_set_milestones`, `goal_update_progress`, `goal_finish` 工具。
38
+ * 🛡️ **安全防护机制**:支持配置 `maxIterations` 最大循环轮次,杜绝死循环消耗。
39
+
40
+ ---
41
+
42
+ ## 🏛️ 架构设计
43
+
44
+ ```mermaid
45
+ graph TD
46
+ subgraph Input ["用户交互与输入"]
47
+ Cmd["聊天斜杠指令: /goal &lt;目标描述&gt;"]
48
+ API["REST API 路由: POST /dsh-goal/action"]
49
+ end
50
+
51
+ subgraph GoalEngine ["目标生命周期引擎 (lib/index.js)"]
52
+ State["状态机 (IDLE, RUNNING, PAUSED, COMPLETED)"]
53
+ Milestones["里程碑分解与进度追踪器"]
54
+ Disk["状态持久化存储 (~/.dsh/goal-state.json)"]
55
+ end
56
+
57
+ subgraph AgentLoop ["智能体自主执行驱动"]
58
+ ToolSet["goal_set_milestones"]
59
+ ToolProgress["goal_update_progress"]
60
+ ToolFinish["goal_finish"]
61
+ LimitGuard{"maxIterations 熔断防护"}
62
+ end
63
+
64
+ subgraph UI ["DSH Web 前端界面"]
65
+ Banner["顶部常驻状态横幅"]
66
+ Timer["实时执行计时器"]
67
+ Drawer["里程碑清单详情模态框"]
68
+ Settings["设置面板卡片 (Schemastery)"]
69
+ end
70
+
71
+ Cmd --> GoalEngine
72
+ API --> GoalEngine
73
+ GoalEngine --> State
74
+ State --> Disk
75
+ State --> Banner
76
+ State --> Drawer
77
+ GoalEngine --> AgentLoop
78
+ AgentLoop --> LimitGuard
79
+ ToolSet --> GoalEngine
80
+ ToolProgress --> GoalEngine
81
+ ToolFinish --> GoalEngine
82
+ ```
83
+
84
+ ---
85
+
86
+ ## 📦 快速安装
87
+
88
+ ```bash
89
+ dsh plugin --profile web add @goodandready/dsh-goal
90
+ ```
91
+
92
+ 重启 DeepSeek Harness 实例并刷新浏览器页面。
93
+
94
+ ---
95
+
96
+ ## 💬 快速上手
97
+
98
+ 在聊天框中直接启动自主目标:
99
+
100
+ ```text
101
+ /goal 重构身份验证中间件并编写端到端集成测试
102
+ ```
103
+
104
+ 或通过 REST 接口调用:
105
+
106
+ ```bash
107
+ curl -X POST http://localhost:3080/dsh-goal/action \
108
+ -H "Content-Type: application/json" \
109
+ -d '{"action":"start","title":"优化数据库查询性能"}'
110
+ ```
111
+
112
+ ---
113
+
114
+ ## ⚙️ 配置参数参考 (`settings.yaml`)
115
+
116
+ ```yaml
117
+ dsh-goal:
118
+ maxIterations: 25
119
+ autoDrive: true
120
+ enableSound: true
121
+ ```
122
+
123
+ | 参数项 | 类型 | 默认值 | 说明 |
124
+ |:---|:---|:---|:---|
125
+ | `maxIterations` | `number` | `25` | 安全熔断阈值:单目标允许的最大自主轮次 |
126
+ | `autoDrive` | `boolean` | `true` | 是否在轮次结束后自动驱动智能体继续推进 |
127
+ | `enableSound` | `boolean` | `true` | 目标完成时是否播放提示音效 |
128
+
129
+ ---
130
+
131
+ ## 🧪 自动化测试
132
+
133
+ 运行自动化测试套件:
134
+
135
+ ```bash
136
+ npm test
137
+ ```
138
+
139
+ ---
140
+
141
+ ## 📄 许可证
142
+
143
+ MIT © [GooDAnDReaDY](https://github.com/GooDAnDReaDY)
@@ -30,12 +30,14 @@
30
30
  - **Футер**: Кнопки ручного добавления шага, паузы и закрытия.
31
31
 
32
32
  ### 2.3 Settings Card (`settings.plugin.item`)
33
- - Размещение во вкладке «Настройки → Плагины».
34
- - Параметры:
33
+ - Размещение во вкладке «Настройки → Плагины → Настройки плагинов», свёрнута по умолчанию, раскрытие по клику по заголовку (`aria-expanded`).
34
+ - Ключ слота (`key`) — пространство настроек `dsh-goal`; пока снимок пространства не в статусе `ready`, карточка не рисуется; при `writable: false` поля и кнопки заблокированы.
35
+ - Параметры (фактический набор, регистрируется schemastery-схемой):
35
36
  - `maxIterations`: максимальное число автономных шагов до принудительной остановки (дефолт: 25);
36
- - `autoDrive`: автоматически вызывать следующий шаг модели (дефолт: true);
37
- - `enableSound`: звуковой сигнал по завершении цели (дефолт: true);
38
- - `promptTemplate`: шаблон системного инжекта в контекст модели.
37
+ - `autoDrive`: автоматически продолжать цикл после каждого хода (дефолт: true);
38
+ - `enableSound`: звуковой сигнал по завершении цели (дефолт: true).
39
+ - Поля читаются из живого снимка пространства (`ctx.settingsScope.bind({ namespace })`), кнопки «Сбросить» / «Сохранить» в футере; сохраняются только изменённые поля, все записи выполняются подряд (не до первой ошибки), результат сверяется обратным чтением; неудачная запись не стирает введённые значения.
40
+ - `Changed in ядро 0.1.2-rc.1`: поле `promptTemplate` никогда не было реализовано в коде и из схемы удалено из описания; контракт карточки переехал на встроенные службы ядра (`slots` / `locale` / `settingsScope`), `dsh.client.inject` пуст.
39
41
 
40
42
  ---
41
43
 
@@ -70,3 +72,5 @@
70
72
  1. **Единый префикс классов**: `.dsh-goal-*` во всех CSS-правилах во избежание конфликтов стилей.
71
73
  2. **Точный макет баннера**: скругление углов 8–10px, шапка с отступами `8px 16px`, выравнивание по центру, кнопки действий справа с `gap: 10px`.
72
74
  3. **Безопасность импортов**: все `require('@deepseek-ai/dsh-client-ui-primitives')` обёрнуты в `try/catch` с SVG-фоллбеками.
75
+ 4. **2026-09-04 — rc.1-совместимость клиента**: `dsh.client.inject: []` — в boot-графе клиента ядра 0.1.2-rc.1 нет package-строк `dsh-client-runtime` (модуля в ядре нет вовсе) и `dsh-client-ui-slots`; `slots`/`locale`/`settingsScope` — встроенные службы ядра. `react`, `@deepseek-ai/dsh-client-ui-slots`, `@deepseek-ai/dsh-client-ui-primitives` отвечаются статической seed-таблицей шелла, но динамических package-строк под них в boot-графе нет — объявлять их в inject нельзя. Условие пересмотра: появление этих package-строк в boot-таблице ядра.
76
+ 5. **2026-09-04 — контракт карточки настроек**: key слота = пространство настроек (`dsh-goal`), чтение через `ctx.settingsScope.bind`, решение по статусу снимка (loading/unavailable/ready + writable), сохранение всех изменённых полей с обратной сверкой. Причина: контракт вкладки «Настройки → Плагины» ядра rc.1 (BashCard/AgentLoopCard как эталон). Пересмотр — при изменении контракта слота в ядре.
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Чистая логика состояния и валидации формы настроек dsh-goal.
3
+ * Не зависит от DOM/React, полностью тестируема через node --test.
4
+ */
5
+
6
+ export const DEFAULT_SETTINGS = {
7
+ maxIterations: 25,
8
+ autoDrive: true,
9
+ enableSound: true,
10
+ };
11
+
12
+ /**
13
+ * Парсинг числового значения настроек:
14
+ * - пустая строка / null / undefined -> undefined (сброс к базовому значению)
15
+ * - валидное число >= 1 -> number
16
+ * - иначе -> NaN (invalid)
17
+ */
18
+ export function parseNumberField(raw) {
19
+ if (raw === '' || raw === null || raw === undefined) {
20
+ return undefined;
21
+ }
22
+ const n = Number(raw);
23
+ if (!Number.isFinite(n) || n < 1 || !Number.isInteger(n)) {
24
+ return NaN;
25
+ }
26
+ return n;
27
+ }
28
+
29
+ /**
30
+ * Вычисление состояния конкретного поля:
31
+ * @param {string} field
32
+ * @param {any} draftValue
33
+ * @param {Object} snap - { value, base, user }
34
+ */
35
+ export function getFieldStatus(field, draftValue, snap) {
36
+ const baseVal = snap?.base?.[field] ?? DEFAULT_SETTINGS[field];
37
+ const userVal = snap?.user?.[field];
38
+ const effectiveVal = snap?.value?.[field] ?? baseVal;
39
+
40
+ const isOverridden = userVal !== undefined && userVal !== null;
41
+ const currentVal = draftValue !== undefined ? draftValue : effectiveVal;
42
+
43
+ let invalid = false;
44
+ let isDirty = false;
45
+
46
+ if (field === 'maxIterations') {
47
+ if (draftValue !== undefined) {
48
+ if (draftValue === '') {
49
+ // Пустая строка = сброс к базовому значению (не ошибка)
50
+ invalid = false;
51
+ isDirty = userVal !== undefined; // dirty если было переопределено
52
+ } else {
53
+ const parsed = parseNumberField(draftValue);
54
+ if (Number.isNaN(parsed)) {
55
+ invalid = true;
56
+ isDirty = true;
57
+ } else {
58
+ isDirty = parsed !== (userVal !== undefined ? userVal : baseVal);
59
+ }
60
+ }
61
+ }
62
+ } else {
63
+ // boolean fields (autoDrive, enableSound)
64
+ if (draftValue !== undefined) {
65
+ isDirty = draftValue !== (userVal !== undefined ? userVal : baseVal);
66
+ }
67
+ }
68
+
69
+ return {
70
+ value: currentVal,
71
+ baseValue: baseVal,
72
+ userValue: userVal,
73
+ isOverridden,
74
+ isDirty,
75
+ invalid,
76
+ };
77
+ }
78
+
79
+ /**
80
+ * Вычисление списка операций записи (writes) для scope.set:
81
+ * @param {Object|null} draft
82
+ * @param {Object} snap - { value, base, user }
83
+ * @returns {Array<[string, any]>}
84
+ */
85
+ export function computeSavePlan(draft, snap) {
86
+ if (!draft) return [];
87
+
88
+ const writes = [];
89
+ const fields = ['maxIterations', 'autoDrive', 'enableSound'];
90
+
91
+ for (const f of fields) {
92
+ if (draft[f] === undefined) continue;
93
+
94
+ const status = getFieldStatus(f, draft[f], snap);
95
+ if (status.invalid) continue;
96
+
97
+ if (status.isDirty) {
98
+ if (f === 'maxIterations' && draft[f] === '') {
99
+ // Сброс переопределения
100
+ writes.push([f, undefined]);
101
+ } else if (f === 'maxIterations') {
102
+ writes.push([f, parseNumberField(draft[f])]);
103
+ } else {
104
+ writes.push([f, Boolean(draft[f])]);
105
+ }
106
+ }
107
+ }
108
+
109
+ return writes;
110
+ }