dsh-embedded-workbench 0.8.9 → 0.8.10
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.en-US.md +15 -4
- package/README.md +15 -4
- package/lib/index.js +184 -184
- package/lib/types/index.d.ts +43 -43
- package/package.json +5 -4
package/README.en-US.md
CHANGED
|
@@ -43,9 +43,9 @@ architecture principles, LVGL patterns, and claim fact-checking.
|
|
|
43
43
|
|
|
44
44
|
## Installation
|
|
45
45
|
|
|
46
|
-
###
|
|
46
|
+
### Claude Code install (recommended)
|
|
47
47
|
|
|
48
|
-
Add the marketplace to `~/.claude/settings.json`:
|
|
48
|
+
Add the marketplace to **Claude Code**'s `~/.claude/settings.json`:
|
|
49
49
|
|
|
50
50
|
```json
|
|
51
51
|
{
|
|
@@ -63,7 +63,7 @@ Then install from CLI:
|
|
|
63
63
|
claude plugin install embedded-workbench@embedded-workbench
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
###
|
|
66
|
+
### Claude Code manual install
|
|
67
67
|
|
|
68
68
|
```bash
|
|
69
69
|
git clone https://github.com/AmethystLuna/embedded-workbench.git ~/.claude/plugins/dev/embedded-workbench
|
|
@@ -87,7 +87,18 @@ Native dsh support ships as a cordis plugin bundle at the repository root (the r
|
|
|
87
87
|
- The bundle injects the first-model-step gate (1% Rule / Red Flags / Plan Verification Gate) into the first model step of every agent session — the dsh-native counterpart of the Claude `SessionStart` hook. It also registers a model-visible catalog entry (`cordis_inspect`).
|
|
88
88
|
- The 4 custom agents are intentionally not ported — dsh's native subagent tooling covers parallel multi-agent work.
|
|
89
89
|
|
|
90
|
-
Install
|
|
90
|
+
Install (native bundle, recommended):
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# from npm (package name: dsh-embedded-workbench)
|
|
94
|
+
dsh plugin --profile web add dsh-embedded-workbench
|
|
95
|
+
# or from GitHub source
|
|
96
|
+
dsh plugin --profile web add "github:AmethystLuna/embedded-workbench"
|
|
97
|
+
# when dsh is not installed globally
|
|
98
|
+
npx -p @deepseek-ai/dsh dsh plugin --profile web add dsh-embedded-workbench
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Restart the profile, then run `dsh --profile web --dump-config`: the `id: embedded-workbench` row must appear with `enabled: true`. More options (plain skill copy, project-level, ...) are in [`.dsh/INSTALL.md`](.dsh/INSTALL.md).
|
|
91
102
|
|
|
92
103
|
> DSH install note: the package name is `dsh-embedded-workbench`. In the web profile's `package.json`, both the dependency key and the `dsh.profile.bundles` entry must use the same name; a mismatch causes the dsh loader to fail with `ERR_MODULE_NOT_FOUND`.
|
|
93
104
|
|
package/README.md
CHANGED
|
@@ -40,9 +40,9 @@ MDK(AC5/AC6)、ARMCLANG、HardFault 分析、状态机、架构原则、LVGL
|
|
|
40
40
|
|
|
41
41
|
## 安装
|
|
42
42
|
|
|
43
|
-
###
|
|
43
|
+
### Claude Code 安装(推荐)
|
|
44
44
|
|
|
45
|
-
在 `~/.claude/settings.json` 中添加 marketplace:
|
|
45
|
+
在 **Claude Code** 的 `~/.claude/settings.json` 中添加 marketplace:
|
|
46
46
|
|
|
47
47
|
```json
|
|
48
48
|
{
|
|
@@ -60,7 +60,7 @@ MDK(AC5/AC6)、ARMCLANG、HardFault 分析、状态机、架构原则、LVGL
|
|
|
60
60
|
claude plugin install embedded-workbench@embedded-workbench
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
### 手动安装
|
|
63
|
+
### Claude Code 手动安装
|
|
64
64
|
|
|
65
65
|
```bash
|
|
66
66
|
git clone https://github.com/AmethystLuna/embedded-workbench.git ~/.claude/plugins/dev/embedded-workbench
|
|
@@ -84,7 +84,18 @@ git clone https://github.com/AmethystLuna/embedded-workbench.git ~/.claude/plugi
|
|
|
84
84
|
- bundle 将首步门禁(1% Rule / Red Flags / Plan Verification Gate)注入每个 agent 会话的第一个模型步骤——是 Claude `SessionStart` hook 在 dsh 的原生对应物,并注册了模型可见的目录条目(`cordis_inspect`)。
|
|
85
85
|
- 4 个自定义 agent 有意不移植——dsh 原生 subagent 工具已覆盖并行多 agent 工作。
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
安装(原生 bundle,推荐):
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# 从 npm 安装(包名 dsh-embedded-workbench)
|
|
91
|
+
dsh plugin --profile web add dsh-embedded-workbench
|
|
92
|
+
# 或从 GitHub 源码安装
|
|
93
|
+
dsh plugin --profile web add "github:AmethystLuna/embedded-workbench"
|
|
94
|
+
# 未全局安装 dsh 时可用 npx
|
|
95
|
+
npx -p @deepseek-ai/dsh dsh plugin --profile web add dsh-embedded-workbench
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
安装后重启 profile,运行 `dsh --profile web --dump-config` 应看到 `id: embedded-workbench` 且 `enabled: true`。更多方式(纯技能拷贝、项目级等)见 [`.dsh/INSTALL.md`](.dsh/INSTALL.md)。
|
|
88
99
|
|
|
89
100
|
> DSH 安装注意:npm 包名为 `dsh-embedded-workbench`(无 scope)。在 web profile 的 `package.json` 中,依赖键与 `dsh.profile.bundles` 必须写 `dsh-embedded-workbench`;否则 dsh 加载器会因找不到 `node_modules/dsh-embedded-workbench` 而启动失败。
|
|
90
101
|
|
package/lib/index.js
CHANGED
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* embedded-workbench — DeepSeek Harness native plugin for the Embedded
|
|
3
|
-
* Workbench toolbox. Injects the session-start gate text (1% Rule, Red
|
|
4
|
-
* Flags, Plan Verification Gate, skills roster) into the first model step
|
|
5
|
-
* of every agent session, mirroring the SessionStart hook the Claude Code
|
|
6
|
-
* plugin installs. The 8 skills ship in this package's `skills/` directory
|
|
7
|
-
* and are registered at apply time into dsh's `ctx.skills` registry through
|
|
8
|
-
* the standard filesystem provider, so they appear in every session catalog
|
|
9
|
-
* without a manual copy step.
|
|
10
|
-
*
|
|
11
|
-
* Injection listens on agent/pre-step and appends the gate to the FIRST
|
|
12
|
-
* model step that runs, once per session (guarded by the session's durable
|
|
13
|
-
* history). Session-start inbox injection was dropped: a blank-session preset
|
|
14
|
-
* switch (agentPreset.select -> recompose) can clear the inbox before the
|
|
15
|
-
* first step, losing the gate for the whole session. The pre-step decision is
|
|
16
|
-
* the durable path - anchored/bootstrap presets that strip first-step injected
|
|
17
|
-
* reminders (skill catalog, AGENTS.md, gate plugins) simply defer this message
|
|
18
|
-
* to the first step after their promotion, and the history guard re-injects it
|
|
19
|
-
* there. The default gate text is the dsh-native adaptation of
|
|
20
|
-
* `hooks/session-start-content.md`: behavior rules
|
|
21
|
-
* (1% Rule / Red Flags / Plan Verification Gate) stay in sync, while
|
|
22
|
-
* presentation is adapted to dsh's native skill catalog — no roster table
|
|
23
|
-
* (the model sees skills in its catalog) and no install instructions (those
|
|
24
|
-
* live in `.dsh/INSTALL.md`). Deployments override via Config.
|
|
25
|
-
*
|
|
26
|
-
* @module embedded-workbench-dsh
|
|
27
|
-
*/
|
|
28
|
-
import { fileURLToPath } from 'node:url';
|
|
29
|
-
import z from '@deepseek-ai/schemastery';
|
|
30
|
-
import { createUserMessage } from '@deepseek-ai/dsh-llm';
|
|
31
|
-
import { FileSystemSkillProvider } from '@deepseek-ai/dsh-skill-filesystem';
|
|
32
|
-
export const name = 'embedded-workbench';
|
|
33
|
-
// Skills are contributed through the registry service, which dsh-base always
|
|
34
|
-
// mounts before bundle rows such as this one apply.
|
|
35
|
-
export const inject = ['skills'];
|
|
36
|
-
// Absolute path of the package's shipped skills directory. `lib/index.js`
|
|
37
|
-
// lives one level below the package root, so `../skills` from the module URL
|
|
38
|
-
// lands on `<package>/skills` regardless of where the package was installed.
|
|
39
|
-
const SKILLS_DIR = fileURLToPath(new URL('../skills', import.meta.url));
|
|
40
|
-
const GATE_PLUGIN_ID = 'embedded-workbench';
|
|
1
|
+
/**
|
|
2
|
+
* embedded-workbench — DeepSeek Harness native plugin for the Embedded
|
|
3
|
+
* Workbench toolbox. Injects the session-start gate text (1% Rule, Red
|
|
4
|
+
* Flags, Plan Verification Gate, skills roster) into the first model step
|
|
5
|
+
* of every agent session, mirroring the SessionStart hook the Claude Code
|
|
6
|
+
* plugin installs. The 8 skills ship in this package's `skills/` directory
|
|
7
|
+
* and are registered at apply time into dsh's `ctx.skills` registry through
|
|
8
|
+
* the standard filesystem provider, so they appear in every session catalog
|
|
9
|
+
* without a manual copy step.
|
|
10
|
+
*
|
|
11
|
+
* Injection listens on agent/pre-step and appends the gate to the FIRST
|
|
12
|
+
* model step that runs, once per session (guarded by the session's durable
|
|
13
|
+
* history). Session-start inbox injection was dropped: a blank-session preset
|
|
14
|
+
* switch (agentPreset.select -> recompose) can clear the inbox before the
|
|
15
|
+
* first step, losing the gate for the whole session. The pre-step decision is
|
|
16
|
+
* the durable path - anchored/bootstrap presets that strip first-step injected
|
|
17
|
+
* reminders (skill catalog, AGENTS.md, gate plugins) simply defer this message
|
|
18
|
+
* to the first step after their promotion, and the history guard re-injects it
|
|
19
|
+
* there. The default gate text is the dsh-native adaptation of
|
|
20
|
+
* `hooks/session-start-content.md`: behavior rules
|
|
21
|
+
* (1% Rule / Red Flags / Plan Verification Gate) stay in sync, while
|
|
22
|
+
* presentation is adapted to dsh's native skill catalog — no roster table
|
|
23
|
+
* (the model sees skills in its catalog) and no install instructions (those
|
|
24
|
+
* live in `.dsh/INSTALL.md`). Deployments override via Config.
|
|
25
|
+
*
|
|
26
|
+
* @module embedded-workbench-dsh
|
|
27
|
+
*/
|
|
28
|
+
import { fileURLToPath } from 'node:url';
|
|
29
|
+
import z from '@deepseek-ai/schemastery';
|
|
30
|
+
import { createUserMessage } from '@deepseek-ai/dsh-llm';
|
|
31
|
+
import { FileSystemSkillProvider } from '@deepseek-ai/dsh-skill-filesystem';
|
|
32
|
+
export const name = 'embedded-workbench';
|
|
33
|
+
// Skills are contributed through the registry service, which dsh-base always
|
|
34
|
+
// mounts before bundle rows such as this one apply.
|
|
35
|
+
export const inject = ['skills'];
|
|
36
|
+
// Absolute path of the package's shipped skills directory. `lib/index.js`
|
|
37
|
+
// lives one level below the package root, so `../skills` from the module URL
|
|
38
|
+
// lands on `<package>/skills` regardless of where the package was installed.
|
|
39
|
+
const SKILLS_DIR = fileURLToPath(new URL('../skills', import.meta.url));
|
|
40
|
+
const GATE_PLUGIN_ID = 'embedded-workbench';
|
|
41
41
|
const DEFAULT_GATE_CONTENT = `<EXTREMELY_IMPORTANT>
|
|
42
42
|
Plugin embedded-workbench is active. You have embedded C/C++ firmware development skills — names and "Use when" triggers are in your skill catalog; load them with the skill tool. No custom agents in dsh: use the native subagent tooling for parallel work.
|
|
43
43
|
|
|
@@ -62,147 +62,147 @@ Plugin embedded-workbench is active. You have embedded C/C++ firmware developmen
|
|
|
62
62
|
To load workflows and engineering policies: load the embedded-workbench skill.
|
|
63
63
|
|
|
64
64
|
**Proactive features**: When you see state machines, protocol refactoring, behavioral claims ("always"/"never"), or multi-module tasks — suggest verification (logicprobe, or the built-in fact-check fallback if logicprobe is not installed), adversarial probing, or parallel subagents BEFORE the user asks. Most users do not know these exist.
|
|
65
|
-
</EXTREMELY_IMPORTANT>`;
|
|
66
|
-
export const Config = z.object({
|
|
67
|
-
enabled: z.boolean().default(true),
|
|
68
|
-
gateContent: z.string().default(DEFAULT_GATE_CONTENT),
|
|
69
|
-
});
|
|
70
|
-
function gateMessage(text) {
|
|
71
|
-
return createUserMessage({
|
|
72
|
-
content: [{ type: 'text', text }],
|
|
73
|
-
// `form` omitted — an undeclared context is the documented default.
|
|
74
|
-
source: { kind: 'plugin', plugin: GATE_PLUGIN_ID },
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
function readSessionEvents(session) {
|
|
78
|
-
const source = session;
|
|
79
|
-
if (typeof source.snapshotEvents === 'function') {
|
|
80
|
-
const snapshot = source.snapshotEvents();
|
|
81
|
-
if (Array.isArray(snapshot))
|
|
82
|
-
return snapshot;
|
|
83
|
-
}
|
|
84
|
-
if (Array.isArray(source.events))
|
|
85
|
-
return source.events;
|
|
86
|
-
return [];
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Model-visible catalog entry (cordis_inspect_list / cordis_inspect_query):
|
|
90
|
-
* lets the model read this plugin's runtime status without guessing. Mirrors
|
|
91
|
-
* the registration pattern of the official dsh-tool-cordis host providers.
|
|
92
|
-
*/
|
|
93
|
-
function inspectProvider(config) {
|
|
94
|
-
return {
|
|
95
|
-
manifest: {
|
|
96
|
-
id: 'embedded-workbench',
|
|
97
|
-
description: 'Session-start gate injection for the Embedded Workbench toolbox — folds the 1% Rule / Red Flags / Plan Verification Gate text into the first model step of every agent session.',
|
|
98
|
-
methods: [
|
|
99
|
-
{
|
|
100
|
-
name: 'status',
|
|
101
|
-
description: 'Read whether the gate injection is active and how large the injected gate text is.',
|
|
102
|
-
inputSchema: {
|
|
103
|
-
type: 'object',
|
|
104
|
-
properties: {},
|
|
105
|
-
additionalProperties: false,
|
|
106
|
-
},
|
|
107
|
-
outputSchema: {
|
|
108
|
-
type: 'object',
|
|
109
|
-
description: 'Gate-injection plugin status.',
|
|
110
|
-
properties: {
|
|
111
|
-
enabled: { type: 'boolean', description: 'Whether the gate folds into the first model step.' },
|
|
112
|
-
gateContentLength: { type: 'integer', description: 'Length in characters of the injected gate text.' },
|
|
113
|
-
},
|
|
114
|
-
required: ['enabled', 'gateContentLength'],
|
|
115
|
-
additionalProperties: false,
|
|
116
|
-
},
|
|
117
|
-
},
|
|
118
|
-
],
|
|
119
|
-
},
|
|
120
|
-
query: async (method) => {
|
|
121
|
-
if (method === 'status') {
|
|
122
|
-
return {
|
|
123
|
-
enabled: config.enabled,
|
|
124
|
-
gateContentLength: config.gateContent.length,
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
return null;
|
|
128
|
-
},
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
export function apply(ctx, config) {
|
|
132
|
-
// Catalog visibility is optional: register only when the inspect registry
|
|
133
|
-
// service is mounted, so headless assemblies without it keep the gate
|
|
134
|
-
// injection working. The registry may be provided AFTER this row applies
|
|
135
|
-
// (base-bundle rows can mount later), so registration is retried on the
|
|
136
|
-
// first agent/session-start — by then the app is fully booted.
|
|
137
|
-
let providerRegistered = false;
|
|
138
|
-
const registerProvider = () => {
|
|
139
|
-
if (providerRegistered)
|
|
140
|
-
return;
|
|
141
|
-
const inspect = ctx.get('cordisInspect');
|
|
142
|
-
if (inspect === undefined)
|
|
143
|
-
return;
|
|
144
|
-
try {
|
|
145
|
-
ctx.effect(() => inspect.register(inspectProvider(config)), 'embedded-workbench: inspect provider');
|
|
146
|
-
providerRegistered = true;
|
|
147
|
-
}
|
|
148
|
-
catch (err) {
|
|
149
|
-
console.warn('[embedded-workbench] inspect provider registration failed', err);
|
|
150
|
-
}
|
|
151
|
-
};
|
|
152
|
-
registerProvider();
|
|
153
|
-
// Ship the bundled skills through the registry: reuse the standard
|
|
154
|
-
// filesystem provider over this package's own `skills/` directory, so
|
|
155
|
-
// catalog discovery, frontmatter parsing, and SKILL.md loading behave
|
|
156
|
-
// exactly like project/user skills while the plugin stays self-contained.
|
|
157
|
-
// Registration lands in the global registry layer (this row mounts at the
|
|
158
|
-
// profile root), so every agent preset sees the skills. `registerProvider`
|
|
159
|
-
// returns the effect disposer; its teardown unregisters and invalidates.
|
|
160
|
-
ctx.skills.registerProvider((control) => {
|
|
161
|
-
return new FileSystemSkillProvider(ctx, control, {
|
|
162
|
-
providerName: 'embedded-workbench',
|
|
163
|
-
includeDefaultRoots: false,
|
|
164
|
-
customSkillDirs: [SKILLS_DIR],
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
if (!config.enabled)
|
|
168
|
-
return;
|
|
169
|
-
// Inject the gate once per session on the FIRST model step that runs,
|
|
170
|
-
// instead of at session-start: session-start injection lands in the agent's
|
|
171
|
-
// inbox, which a blank-session preset switch (agentPreset.select ->
|
|
172
|
-
// recompose) can clear before the first step - the gate would then be lost
|
|
173
|
-
// for the whole session. The pre-step decision is the durable path a
|
|
174
|
-
// first-step injection takes: the gate is appended to the first step's
|
|
175
|
-
// decision and enters session history there, so every later step (and a
|
|
176
|
-
// resume) skips it. Anchored/bootstrap presets that strip first-step
|
|
177
|
-
// injected reminders (skill catalog, AGENTS.md, gate plugins) simply defer
|
|
178
|
-
// this message to the first step after their promotion - the history guard
|
|
179
|
-
// re-injects it there, so the gate still lands exactly once per session.
|
|
180
|
-
ctx.on('agent/pre-step', async ({ agent }, next) => {
|
|
181
|
-
const decision = await next();
|
|
182
|
-
if (decision.kind === 'reject')
|
|
183
|
-
return decision;
|
|
184
|
-
registerProvider();
|
|
185
|
-
if (gateInHistory(agent.session))
|
|
186
|
-
return decision;
|
|
187
|
-
return {
|
|
188
|
-
kind: 'enter',
|
|
189
|
-
messages: [...decision.messages, gateMessage(config.gateContent)],
|
|
190
|
-
};
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
/**
|
|
194
|
-
* Whether the gate already entered this session's durable history. The
|
|
195
|
-
* pre-step listener re-appends the gate until it does; once a step committed
|
|
196
|
-
* it, every later step (and a resume of a session that kept it) skips the
|
|
197
|
-
* injection. A session whose gate was dropped before any step ran (e.g. an
|
|
198
|
-
* inbox cleared by a blank-session preset switch) simply re-injects on the
|
|
199
|
-
* first step that runs.
|
|
200
|
-
*/
|
|
201
|
-
function gateInHistory(session) {
|
|
202
|
-
return readSessionEvents(session).some((event) => {
|
|
203
|
-
if (event.type !== 'user/message')
|
|
204
|
-
return false;
|
|
205
|
-
const source = event.data?.source;
|
|
206
|
-
return source?.kind === 'plugin' && source.plugin === GATE_PLUGIN_ID;
|
|
207
|
-
});
|
|
208
|
-
}
|
|
65
|
+
</EXTREMELY_IMPORTANT>`;
|
|
66
|
+
export const Config = z.object({
|
|
67
|
+
enabled: z.boolean().default(true),
|
|
68
|
+
gateContent: z.string().default(DEFAULT_GATE_CONTENT),
|
|
69
|
+
});
|
|
70
|
+
function gateMessage(text) {
|
|
71
|
+
return createUserMessage({
|
|
72
|
+
content: [{ type: 'text', text }],
|
|
73
|
+
// `form` omitted — an undeclared context is the documented default.
|
|
74
|
+
source: { kind: 'plugin', plugin: GATE_PLUGIN_ID },
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
function readSessionEvents(session) {
|
|
78
|
+
const source = session;
|
|
79
|
+
if (typeof source.snapshotEvents === 'function') {
|
|
80
|
+
const snapshot = source.snapshotEvents();
|
|
81
|
+
if (Array.isArray(snapshot))
|
|
82
|
+
return snapshot;
|
|
83
|
+
}
|
|
84
|
+
if (Array.isArray(source.events))
|
|
85
|
+
return source.events;
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Model-visible catalog entry (cordis_inspect_list / cordis_inspect_query):
|
|
90
|
+
* lets the model read this plugin's runtime status without guessing. Mirrors
|
|
91
|
+
* the registration pattern of the official dsh-tool-cordis host providers.
|
|
92
|
+
*/
|
|
93
|
+
function inspectProvider(config) {
|
|
94
|
+
return {
|
|
95
|
+
manifest: {
|
|
96
|
+
id: 'embedded-workbench',
|
|
97
|
+
description: 'Session-start gate injection for the Embedded Workbench toolbox — folds the 1% Rule / Red Flags / Plan Verification Gate text into the first model step of every agent session.',
|
|
98
|
+
methods: [
|
|
99
|
+
{
|
|
100
|
+
name: 'status',
|
|
101
|
+
description: 'Read whether the gate injection is active and how large the injected gate text is.',
|
|
102
|
+
inputSchema: {
|
|
103
|
+
type: 'object',
|
|
104
|
+
properties: {},
|
|
105
|
+
additionalProperties: false,
|
|
106
|
+
},
|
|
107
|
+
outputSchema: {
|
|
108
|
+
type: 'object',
|
|
109
|
+
description: 'Gate-injection plugin status.',
|
|
110
|
+
properties: {
|
|
111
|
+
enabled: { type: 'boolean', description: 'Whether the gate folds into the first model step.' },
|
|
112
|
+
gateContentLength: { type: 'integer', description: 'Length in characters of the injected gate text.' },
|
|
113
|
+
},
|
|
114
|
+
required: ['enabled', 'gateContentLength'],
|
|
115
|
+
additionalProperties: false,
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
},
|
|
120
|
+
query: async (method) => {
|
|
121
|
+
if (method === 'status') {
|
|
122
|
+
return {
|
|
123
|
+
enabled: config.enabled,
|
|
124
|
+
gateContentLength: config.gateContent.length,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
return null;
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
export function apply(ctx, config) {
|
|
132
|
+
// Catalog visibility is optional: register only when the inspect registry
|
|
133
|
+
// service is mounted, so headless assemblies without it keep the gate
|
|
134
|
+
// injection working. The registry may be provided AFTER this row applies
|
|
135
|
+
// (base-bundle rows can mount later), so registration is retried on the
|
|
136
|
+
// first agent/session-start — by then the app is fully booted.
|
|
137
|
+
let providerRegistered = false;
|
|
138
|
+
const registerProvider = () => {
|
|
139
|
+
if (providerRegistered)
|
|
140
|
+
return;
|
|
141
|
+
const inspect = ctx.get('cordisInspect');
|
|
142
|
+
if (inspect === undefined)
|
|
143
|
+
return;
|
|
144
|
+
try {
|
|
145
|
+
ctx.effect(() => inspect.register(inspectProvider(config)), 'embedded-workbench: inspect provider');
|
|
146
|
+
providerRegistered = true;
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
console.warn('[embedded-workbench] inspect provider registration failed', err);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
registerProvider();
|
|
153
|
+
// Ship the bundled skills through the registry: reuse the standard
|
|
154
|
+
// filesystem provider over this package's own `skills/` directory, so
|
|
155
|
+
// catalog discovery, frontmatter parsing, and SKILL.md loading behave
|
|
156
|
+
// exactly like project/user skills while the plugin stays self-contained.
|
|
157
|
+
// Registration lands in the global registry layer (this row mounts at the
|
|
158
|
+
// profile root), so every agent preset sees the skills. `registerProvider`
|
|
159
|
+
// returns the effect disposer; its teardown unregisters and invalidates.
|
|
160
|
+
ctx.skills.registerProvider((control) => {
|
|
161
|
+
return new FileSystemSkillProvider(ctx, control, {
|
|
162
|
+
providerName: 'embedded-workbench',
|
|
163
|
+
includeDefaultRoots: false,
|
|
164
|
+
customSkillDirs: [SKILLS_DIR],
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
if (!config.enabled)
|
|
168
|
+
return;
|
|
169
|
+
// Inject the gate once per session on the FIRST model step that runs,
|
|
170
|
+
// instead of at session-start: session-start injection lands in the agent's
|
|
171
|
+
// inbox, which a blank-session preset switch (agentPreset.select ->
|
|
172
|
+
// recompose) can clear before the first step - the gate would then be lost
|
|
173
|
+
// for the whole session. The pre-step decision is the durable path a
|
|
174
|
+
// first-step injection takes: the gate is appended to the first step's
|
|
175
|
+
// decision and enters session history there, so every later step (and a
|
|
176
|
+
// resume) skips it. Anchored/bootstrap presets that strip first-step
|
|
177
|
+
// injected reminders (skill catalog, AGENTS.md, gate plugins) simply defer
|
|
178
|
+
// this message to the first step after their promotion - the history guard
|
|
179
|
+
// re-injects it there, so the gate still lands exactly once per session.
|
|
180
|
+
ctx.on('agent/pre-step', async ({ agent }, next) => {
|
|
181
|
+
const decision = await next();
|
|
182
|
+
if (decision.kind === 'reject')
|
|
183
|
+
return decision;
|
|
184
|
+
registerProvider();
|
|
185
|
+
if (gateInHistory(agent.session))
|
|
186
|
+
return decision;
|
|
187
|
+
return {
|
|
188
|
+
kind: 'enter',
|
|
189
|
+
messages: [...decision.messages, gateMessage(config.gateContent)],
|
|
190
|
+
};
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Whether the gate already entered this session's durable history. The
|
|
195
|
+
* pre-step listener re-appends the gate until it does; once a step committed
|
|
196
|
+
* it, every later step (and a resume of a session that kept it) skips the
|
|
197
|
+
* injection. A session whose gate was dropped before any step ran (e.g. an
|
|
198
|
+
* inbox cleared by a blank-session preset switch) simply re-injects on the
|
|
199
|
+
* first step that runs.
|
|
200
|
+
*/
|
|
201
|
+
function gateInHistory(session) {
|
|
202
|
+
return readSessionEvents(session).some((event) => {
|
|
203
|
+
if (event.type !== 'user/message')
|
|
204
|
+
return false;
|
|
205
|
+
const source = event.data?.source;
|
|
206
|
+
return source?.kind === 'plugin' && source.plugin === GATE_PLUGIN_ID;
|
|
207
|
+
});
|
|
208
|
+
}
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* embedded-workbench — DeepSeek Harness native plugin for the Embedded
|
|
3
|
-
* Workbench toolbox. Injects the session-start gate text (1% Rule, Red
|
|
4
|
-
* Flags, Plan Verification Gate, skills roster) into the first model step
|
|
5
|
-
* of every agent session, mirroring the SessionStart hook the Claude Code
|
|
6
|
-
* plugin installs. The 8 skills ship in this package's `skills/` directory
|
|
7
|
-
* and are registered at apply time into dsh's `ctx.skills` registry through
|
|
8
|
-
* the standard filesystem provider, so they appear in every session catalog
|
|
9
|
-
* without a manual copy step.
|
|
10
|
-
*
|
|
11
|
-
* Injection listens on agent/pre-step and appends the gate to the FIRST
|
|
12
|
-
* model step that runs, once per session (guarded by the session's durable
|
|
13
|
-
* history). Session-start inbox injection was dropped: a blank-session preset
|
|
14
|
-
* switch (agentPreset.select -> recompose) can clear the inbox before the
|
|
15
|
-
* first step, losing the gate for the whole session. The pre-step decision is
|
|
16
|
-
* the durable path - anchored/bootstrap presets that strip first-step injected
|
|
17
|
-
* reminders (skill catalog, AGENTS.md, gate plugins) simply defer this message
|
|
18
|
-
* to the first step after their promotion, and the history guard re-injects it
|
|
19
|
-
* there. The default gate text is the dsh-native adaptation of
|
|
20
|
-
* `hooks/session-start-content.md`: behavior rules
|
|
21
|
-
* (1% Rule / Red Flags / Plan Verification Gate) stay in sync, while
|
|
22
|
-
* presentation is adapted to dsh's native skill catalog — no roster table
|
|
23
|
-
* (the model sees skills in its catalog) and no install instructions (those
|
|
24
|
-
* live in `.dsh/INSTALL.md`). Deployments override via Config.
|
|
25
|
-
*
|
|
26
|
-
* @module embedded-workbench-dsh
|
|
27
|
-
*/
|
|
28
|
-
import type { Context } from '@deepseek-ai/cordis';
|
|
29
|
-
import z from '@deepseek-ai/schemastery';
|
|
30
|
-
export declare const name = "embedded-workbench";
|
|
31
|
-
export declare const inject: string[];
|
|
32
|
-
export interface Config {
|
|
33
|
-
enabled: boolean;
|
|
34
|
-
gateContent: string;
|
|
35
|
-
}
|
|
36
|
-
export declare const Config: z<Schemastery.ObjectS<{
|
|
37
|
-
enabled: z<boolean, boolean>;
|
|
38
|
-
gateContent: z<string, string>;
|
|
39
|
-
}>, Schemastery.ObjectT<{
|
|
40
|
-
enabled: z<boolean, boolean>;
|
|
41
|
-
gateContent: z<string, string>;
|
|
42
|
-
}>>;
|
|
43
|
-
export declare function apply(ctx: Context, config: Config): void;
|
|
1
|
+
/**
|
|
2
|
+
* embedded-workbench — DeepSeek Harness native plugin for the Embedded
|
|
3
|
+
* Workbench toolbox. Injects the session-start gate text (1% Rule, Red
|
|
4
|
+
* Flags, Plan Verification Gate, skills roster) into the first model step
|
|
5
|
+
* of every agent session, mirroring the SessionStart hook the Claude Code
|
|
6
|
+
* plugin installs. The 8 skills ship in this package's `skills/` directory
|
|
7
|
+
* and are registered at apply time into dsh's `ctx.skills` registry through
|
|
8
|
+
* the standard filesystem provider, so they appear in every session catalog
|
|
9
|
+
* without a manual copy step.
|
|
10
|
+
*
|
|
11
|
+
* Injection listens on agent/pre-step and appends the gate to the FIRST
|
|
12
|
+
* model step that runs, once per session (guarded by the session's durable
|
|
13
|
+
* history). Session-start inbox injection was dropped: a blank-session preset
|
|
14
|
+
* switch (agentPreset.select -> recompose) can clear the inbox before the
|
|
15
|
+
* first step, losing the gate for the whole session. The pre-step decision is
|
|
16
|
+
* the durable path - anchored/bootstrap presets that strip first-step injected
|
|
17
|
+
* reminders (skill catalog, AGENTS.md, gate plugins) simply defer this message
|
|
18
|
+
* to the first step after their promotion, and the history guard re-injects it
|
|
19
|
+
* there. The default gate text is the dsh-native adaptation of
|
|
20
|
+
* `hooks/session-start-content.md`: behavior rules
|
|
21
|
+
* (1% Rule / Red Flags / Plan Verification Gate) stay in sync, while
|
|
22
|
+
* presentation is adapted to dsh's native skill catalog — no roster table
|
|
23
|
+
* (the model sees skills in its catalog) and no install instructions (those
|
|
24
|
+
* live in `.dsh/INSTALL.md`). Deployments override via Config.
|
|
25
|
+
*
|
|
26
|
+
* @module embedded-workbench-dsh
|
|
27
|
+
*/
|
|
28
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
29
|
+
import z from '@deepseek-ai/schemastery';
|
|
30
|
+
export declare const name = "embedded-workbench";
|
|
31
|
+
export declare const inject: string[];
|
|
32
|
+
export interface Config {
|
|
33
|
+
enabled: boolean;
|
|
34
|
+
gateContent: string;
|
|
35
|
+
}
|
|
36
|
+
export declare const Config: z<Schemastery.ObjectS<{
|
|
37
|
+
enabled: z<boolean, boolean>;
|
|
38
|
+
gateContent: z<string, string>;
|
|
39
|
+
}>, Schemastery.ObjectT<{
|
|
40
|
+
enabled: z<boolean, boolean>;
|
|
41
|
+
gateContent: z<string, string>;
|
|
42
|
+
}>>;
|
|
43
|
+
export declare function apply(ctx: Context, config: Config): void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-embedded-workbench",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.10",
|
|
4
4
|
"description": "Embedded C/C++ firmware development toolbox — 4 agents, 8 skills covering FreeRTOS, ISR, NVM storage, Keil MDK, ARMCLANG, HardFault, state machines, architecture, LVGL patterns, and claim fact-checking. Ships a native DeepSeek Harness (dsh) bundle that injects the session-start gate into the first model step.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"patch": "./cordis.patch.yml"
|
|
33
33
|
},
|
|
34
34
|
"compatibility": {
|
|
35
|
-
"dsh": "^0.1.0-rc.7 || ^0.1.1-rc.1 || ^0.1.2-alpha.2 || ^0.1.2-alpha.3 || ^0.1.2-alpha.4 || ^0.1.2-alpha.5 || ^0.1.2-rc.1 || ^0.1.3-alpha.1 || ^0.1.3-alpha.2 || ^0.1.5-alpha.1 || ^0.1.5-rc.1 || ^0.1.5-alpha.2 || ^0.1.5-rc.2",
|
|
35
|
+
"dsh": "^0.1.0-rc.7 || ^0.1.1-rc.1 || ^0.1.2-alpha.2 || ^0.1.2-alpha.3 || ^0.1.2-alpha.4 || ^0.1.2-alpha.5 || ^0.1.2-rc.1 || ^0.1.3-alpha.1 || ^0.1.3-alpha.2 || ^0.1.5-alpha.1 || ^0.1.5-rc.1 || ^0.1.5-alpha.2 || ^0.1.5-rc.2 || ^0.1.6-alpha.1",
|
|
36
36
|
"dshReleases": {
|
|
37
37
|
"0.1.0-rc.7": "compatible",
|
|
38
38
|
"0.1.0-rc.8": "compatible",
|
|
@@ -48,7 +48,8 @@
|
|
|
48
48
|
"0.1.5-alpha.1": "compatible",
|
|
49
49
|
"0.1.5-alpha.2": "compatible",
|
|
50
50
|
"0.1.5-rc.1": "compatible",
|
|
51
|
-
"0.1.5-rc.2": "compatible"
|
|
51
|
+
"0.1.5-rc.2": "compatible",
|
|
52
|
+
"0.1.6-alpha.1": "compatible"
|
|
52
53
|
},
|
|
53
54
|
"profiles": [
|
|
54
55
|
"headless"
|
|
@@ -79,7 +80,7 @@
|
|
|
79
80
|
"@deepseek-ai/dsh-skill": "^0.1.0-rc.8",
|
|
80
81
|
"@deepseek-ai/dsh-skill-filesystem": "^0.1.0-rc.8",
|
|
81
82
|
"@deepseek-ai/dsh-timeout": "^0.0.1-rc.1",
|
|
82
|
-
"@types/node": "^26.
|
|
83
|
+
"@types/node": "^26.5.0",
|
|
83
84
|
"@deepseek-ai/schemastery": "^3.18.2",
|
|
84
85
|
"typescript": "^7.0.2"
|
|
85
86
|
},
|