Skip to content
dsh.fish
Bundle

dsh-ask-template

DSH 客户端插件 + 配套 skill:/ask 直接把五段结构化空白模板写进输入框草稿(零模型调用)

Source
maboloshi
License
MIT
Updated
Updated yesterday

Readme

# dsh-ask-template

DSH(DeepSeek Harness)Web 里 `/ask` 的改造:**把结构化填表从「模型弹表」变成「零模型调用填表」**。

本仓库交付配套的两半:

| 半 | 载体 | 手势 | 谁在弹表 | 模型调用 |
| --- | --- | --- | --- | --- |
| **客户端命令**(仓库主体) | DSH 客户端插件 | 裸 `/ask` + 回车 | 浏览器本地写草稿 | **0 次** |
| **配套 skill** `ask-user` | 技能文件(模型驱动) | `/ask-user`,或 `/` 菜单选行 | 模型调 `ask_user_question` | 有 |

两者共用同一套五段字段,区别只是"谁来弹这张表":

- 想省一次模型往返、自己打字填 —— 用 `/ask`(命令,本插件);
- 想让 agent 在对话里逐项问你、你点选即可 —— 用 `/ask-user`(skill)。

## 命令这一半是怎么工作的

敲 `/ask` + 回车时,全程不碰模型:

1. `/` 命令源在**裸 token**(`/ask` 后没有空格参数)上命中本插件的命令贡献;
2. 贡献是 `action` 类型:消费掉 `/ask` 这个词,执行一个纯客户端回调,不提交任何消息;
3. 回调里 `ctx.sessions.scope(sessionId)` 取到会话作用域,`ctx.conversation.input.for(actx).setDraft(模板)` 把空白模板整段写进输入框草稿;
4. 你填完按回车 —— 这时才是普通用户消息,模型只看到填好的五段表。

`/ask 参数…`(带空格)不命中命令,会照旧当普通文本发出,所以 skill 那条路依然可用。

## 五段模板

```text
【背景情况】

【核心问题】

【约束与要求】

【输出格式】
未提供

【已有尝试】
未提供
```

两个选填段预填 `未提供`,不动即为该项的最终取值;必填三段留空等你填。
`未提供` 是"没填"的正式表达,不需要删改。

## 安装

### 1. 客户端命令(插件)

```powershell
dsh plugin --profile web add link:<本仓库路径>
```

该命令转发 pnpm 安装,随后自动把 `dsh-ask-template` 追加进
`~/.dsh/profiles/web/package.json` 的 `dsh.profile.bundles`,即装即挂载;装完刷新页面。

也可以直接 npm 源安装(发布后):

```powershell
dsh plugin --profile web add dsh-ask-template
```

### 2. 配套 skill `ask-user`

把下面这段存成 `skills/ask-user/SKILL.md`(本机实际位置 `~/.agents/skills/ask-user/SKILL.md`):

```markdown
---
name: ask-user
description: 结构化填表提问(模型驱动的 ask_user_question 弹表;纯客户端写模板用 /ask 命令)
user-invocable: true
---
进入填表模式:第一步就调用 ask_user_question,把五项作为一次请求里的五个问题提出。
必填:背景情况 / 核心问题 / 约束与要求;
选填:输出格式(结论先行 再分步落地 / 对比表格 / 可执行代码 / 要点清单)、已有尝试。
收到答案后整理成五段(未回答写"未提供"),再作答。
```

字段与命令那条路完全一致,所以你从任一路径得到的表,格式都相同。

## 卸载(一键回滚)

```powershell
dsh plugin --profile web remove dsh-ask-template
```

skill 侧直接删掉 `skills/ask-user/` 目录即可;它原本叫 `ask`,改名是为了把裸 `/ask`
这个手势让给命令,避免 `/` 菜单出现两行同名。

## 实现要点(复现/维护用)

- 命令贡献契约:`ctx.commandUi.register({ name, label, description, available, ui })`,
  `ui: { kind: 'action', run(session) }`,`session` 只带 `sessionId`。
- `sessionId` → 会话作用域:`ctx.sessions.scope(sessionId)`(返回 `undefined` 即无 composer)。
- 写草稿:`ctx.conversation.input.for(actx).setDraft(text)`,再 `.focus()` 把焦点交还输入框。
- 回车命中 `action` 时,提交机器把该次裁决记为 `handled` 并**不产生任何 effect**,
  所以这里写入的草稿不会被"提交后清空"逻辑吃掉。
- 浏览器半是手写 CJS 闭包 bundle:`window.__ModuleLoader__.load({ id, factory })`,
  **无 React、无构建步骤、无运行时依赖**,改完刷新页面即生效。
- 主机半(`lib/index.js`)是空挂载:profile 层要靠一条 Loader 行寻址,客户端 bundle 由
  该行所属的包被发现。

## 已知限制

- `setDraft` 把光标停在草稿末尾(`【已有尝试】` 的 `未提供` 之后),要填第一段需自己点上去。
  契约里没有"带光标位置写草稿"的入口;要解决得改走自绘表单 slot(`conversation.input.overlay`)。
- 输入框已有文字时会被整段替换,替换前会给一条提示;旧草稿不保留。
- 命令与 skill 目前共用同一套五段字段,改字段要两边一起改。

## License

MIT

Install

dsh plugin --profile web add github:maboloshi/dsh-ask-template

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source